[Scummvm-git-logs] scummvm master -> 1907b5f74140793453b33f9265ac065db218ec30

sev- sev at scummvm.org
Wed Mar 24 16:23:05 UTC 2021


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

Summary:
fd11c41a8d NANCY: Add skeleton engine
fd1f0767f8 NANCY: Add resource decompressor
13024420cb NANCY: Load more data from info blocks
1f2932b4f2 NANCY: Add signature for game 2
892ac7f835 NANCY: Refactor resource manager
410861661a NANCY: Refactor resource manager
d245afd843 NANCY: Add partial support for CifTree 2.1
ba12d04986 NANCY: Add heuristic for long filenames in CifTree
53c1548fdf NANCY: Add signature for Nancy Drew 5
dbb64b77e1 NANCY: Make CifTree case insensitive
e989b0e1c8 NANCY: Add debug commands for dumping a resource and loading a new CifTree
164778ba12 NANCY: Refactor resource manager
debe67fa1e NANCY: Add detection for Nancy 6
9720ba812a NANCY: Add support for Nancy 6 CifTree format
66c3efe933 NANCY: Add signatures for Nancy Drew 3 and 4
48c60a01af NANCY: Fix regression in CifTree 2.0 handling
aa1f0bd1ac NANCY: Cleanup
dd91f7e4f5 NANCY: Add support for multiple CifTrees loaded simultaneously
1df38bd500 NANCY: Add support for .cif patches
3efe6a6f76 NANCY: Add partial .avf video support
6381b36643 NANCY: Add support for .his audio files
ef8daa9f3f NANCY: Add exporting to .cif
d5ea0c840a NANCY: Rename debug commands
bd7e83cf8e NANCY: Skip unsupported video frame types for now
2cc91e62ac NANCY: Add support for HIS versions 1.0 and 2.0
5fa6119405 NANCY: Return SeekableAudioStream instead of RewindableAudioStream
0503e651e5 NANCY: Add event polling and timing to play_video debug command
f670e5ad4f NANCY: Load frame time from AVF files
1c93da8699 NANCY: Add support for video frame types 1 and 2
c0fe9e83a0 NANCY: Add IFF parsing
d73103df45 NANCY: Add support for PCAL chunk
369ab0136a NANCY: Add debug command for hexdumping chunks
b393b234be NANCY: Add debugger command for listing chunks inside an IFF
e89c01713b NANCY: Add getting chunks with the same name by index
5decebb72c NANCY: Add InstallShield detection entries for Nancy 3, 4 and 5
3079717143 NANCY: Add InstallShield cab files to SearchMan
50e016a826 NANCY: Show LOGO
e5646013b6 NANCY: Add md5s for another nancy5 version
3655241864 NANCY: Add loading of BOOT/FR* and BOOT/LG*
af8078ceb8 NANCY: Add loading and playing of BOOT/MSND
0284d2172f NANCY: WIP
d97ca12d6c NANCY: Initial commit
4a76623e67 NANCY: Fix compiler errors
0852938520 NANCY: Fix compiler errors
7e1e276c3c NANCY: Add in-game time
11f89b5b97 NANCY: Add initial input support
f60ccc38b9 NANCY: runScene() improvemens
0a4ed23f51 NANCY: Add action event processing
c1a6f38271 NANCY: Add Cursors structure
b79d9f1a32 NANCY: Rendering code refactor
c18e0b171d NANCY: Add initial mouse support
95c7d5031f NANCY: Implement EventFlagsMultiHS action record
abb1583252 NANCY: Add hotspot detection
8397e35212 NANCY: Display UI sliders
cd0301af92 NANCY: Implement scene switching
4ab6de6c23 NANCY: Implement skipping of intro
2fb0dabcbb NANCY: Fix vertical scroll
e12f6b6753 NANCY: Add more scene change types
f4d8368bd4 NANCY: Add initial sound support
7d04efad37 NANCY: Fix event flags issues
b9dc249d53 NANCY: Add debug commands
b2af943ad0 NANCY: Fix game crash
9184dd01bd NANCY: Rename enum value
f683d414f9 NANCY: Fix scene change sound behavior
da461f1d6e NANCY: Upgrade video code
75ba5e6528 NANCY: Add reverse video playback
46341dde11 NANCY: Implement secondary video
cd66fa975d NANCY: Correct video timing
b4315872b5 NANCY: Fix sigtraps when closing
2a6555fe36 NANCY: Partially implement bitmap animations
b642ca3b4d NANCY: Implement secondary movie
9152deeac7 NANCY: Implement map
6ce4390b93 NANCY: Add map location labels and button
d5b7053dbe NANCY: Add scrollbar movement and inventory contents
fb2a65a8e7 NANCY: Initial inventory implementation
188ee86d2f NANCY: Implement item interaction
673bfec209 NANCY: Initial dialogue implementation
cd36df63fc NANCY: Add textbox scrolling
3aaf2a9bea NANCY: Major engine rewrite
ebe99bc271 NANCY: Fix adding items to inventory
1de1f568d7 NANCY: Fix secondary movie
c73ea1b0cb NANCY: Viewport movement fixes
e4890a18fc NANCY: Sound manager improvements
05ac9934c7 NANCY: Global main menu support
20d226fc10 NANCY: Fix load_scene and scene_id console commands
a96f6fdebd NANCY: Move NO SOUND keyword check
3611184261 NANCY: Fix leaked surfaces
0ff64ca11a NANCY: Implement ordering puzzle
a7619575ac NANCY: Bitmap animation fixes
46ed7dce87 NANCY: Fix show_image and play_video debug commands
725ff2f95d NANCY: Implement inventory scrollbar
c7e1c95803 NANCY: Implement rotating lock puzzle
b7c2b5a6e8 NANCY: Textbox improvements
67f11d9c91 NANCY: Scene improvements and fixes
d83edfdca3 NANCY: Implement telephone/hint system
1cd406b5af NANCY: Implement slider puzzle
e0e18b0ebf NANCY: Static bitmap animation fixes
cc7d69dd10 NANCY: Fix wrong type reads for event flags
628a4bd2b1 NANCY: Implement password puzzle
c0ff749821 NANCY: Add event flag to PlayDIGISoundAndDie
197b055881 NANCY: Implement lever puzzle
5d00c8d195 NANCY: Fix two primary video instances being active simultaneously
5d215d46b4 NANCY: Secondary video fixes
7d93c7996e NANCY: Clear input after exiting console
d64a309fb3 NANCY: Small code refactor
c8dbb03b89 NANCY: Fix segfault on exit
6a4e1ecb8e NANCY: Implement repeating action records
7b2aa161ae NANCY: Implement PlaySoundMultiHS action record
d3804a3138 NANCY: Implement PlayStaticBitmapAnimation action record
74b045bd52 NANCY: Primary video fixes
145d5fd735 NANCY: Add ActionRecord and Scene debug channels
51ba19dc70 NANCY: Secondary movie fixes
f5ec222c95 NANCY: Fix sound channel clashes
9626437921 NANCY: Implement cheat dialogs
4f9f97d40c NANCY: Fix secondary video timings
1773522233 NANCY: Fix nancy1 night map locations
aa68bdef0f NANCY: Implement AddInventoryNoHS action record
6ada1a6b90 NANCY: Fix laggy movement when repeatedly clicking viewport edges
5f35b8abcb NANCY: Implement help screen and UI buttons
89ea3ec0cf NANCY: Add map button
cb373ff04e NANCY: Add missing UI sfx
4335818587 NANCY: Add c++11 dependency to configure.engine
a17e45537c NANCY: Change textbox text tokens type
a7fab68839 NANCY: Fix crash after popping scene
a96fcd78db NANCY: Fix infinite hints
0fdda6fbfc NANCY: Code formatting fixes
e516df94df NANCY: Move includes inside include guards
55fe1a5db4 NANCY: Implement credits and WinGame action record
4656c45549 NANCY: Implement LoseGame action record
7c8e0aea3d NANCY: Properly clean up when starting new game
5c1843f871 NANCY: Rename state functions in nancy.h
7fcdeb641c NANCY: Time fixes
a752f1a4f2 NANCY: Initial save/load support
20f432f8d3 NANCY: Engine refactor
19135b354c NANCY: Add The Vampire Diaries detection entry
bde7db15ae NANCY: Use serializer when reading boot summary
b3222b7439 NANCY: Resource manager improvements
b34fcec89a NANCY: Add loading images from .bmp files
a0c6166076 NANCY: Remove unused subclass
a6f2b172f3 NANCY: Hook Logo state to the graphics manager
4eace74d48 NANCY: Add 8-bit color game flag for The Vampire Diaries
2d757175b0 NANCY: Fix msvc compile errors
c7e0c750bb NANCY: Add support for older video header format
5440559918 NANCY: Add support for 8-bit color
d16876272a NANCY: Load hints only when a HINT chunk exists
8468071ca2 NANCY: Load video palette from scene summary
8fccf950fe NANCY: Initialize additional surfaces with screen format
3910d87c82 NANCY: Load correct first scene in The Vampire Diaries
6eb5be2c51 NANCY: Action record reading changes
45e5064eff NANCY: Correctly load early AVF type frames
f24cc5ee24 NANCY: Add method for loading a palette from external file
7220797ca4 NANCY: Remove redundancy in transparency handling
3922a3dc30 NANCY: Load The Vampire Diaries secondary video
ba6a973d4f NANCY: Destroy Scene state on exit
da559b5fe0 NANCY: Display The Vampire Diaries video correctly
653239aa26 NANCY: Clip viewport-relative objects
6f10d41559 NANCY: Fix Secrets Can Kill textbox colors
b7cdb2ee91 NANCY: Implement loading saves from launcher
b3d06c327e NANCY: Add savefile version checks
a16c9b90ae NANCY: Clean up graphics loop
a42de3037f NANCY: Wait for video end during dialogue
15153a5533 NANCY: Filter input between states
e324adf05f NANCY: Code cleanup
a286a287f2 NANCY: Replace NanEngine macro
a7373264f2 NANCY: Pass string arguments by reference
b6df945ae1 NANCY: Formatting fixes
6be4617e26 NANCY: Make Textbox::getInnerHeight() const
8cfd4dba23 NANCY: Replace malloc with new
abe1d47137 NANCY: Replace itoa with U32String::itoa
3e3a8cd5c7 NANCY: Properly mark overriden virtual functions
6728c96e68 NANCY: Add underscore to class member names
54bdb420b0 NANCY: String reading fixes
e9adea4c4e NANCY: Formatting fixes
36883885f0 NANCY: Move ActionRecord::finishExecution() outside header file
8d774ba1e3 NANCY: Add POTFILES and mark translatable strings
af28b38f4b NANCY: Properly read early video format frame IDs
646dfeb035 NANCY: Use macros in detection table
bcd011eb79 NANCY: Reduce calls to getBasePtr() when resizing surface
a2efd6e867 NANCY: Formatting fixes
86e656f709 NANCY: Remove static members of SliderPuzzle
0c5b6a0244 NANCY: Formatting fixes
99da486454 NANCY: Add dependencies to configure.engine
bfc6586f78 NANCY: Add reserve() calls before initializing arrays
56c005ef2c NANCY: Mark functions and iterators as const
0d8d31ffd0 NANCY: Formatting fixes
0d4516c897 NANCY: Initialize sound channel types from a table
cb63f50f71 NANCY: Remove underscore from struct member names
10a1d6e3a7 NANCY: Add vorbis dependency
c846247909 NANCY: Fix viewport glitch
eb2fe847d7 NANCY: Improve code readability in actionmanager.cpp
16c083d015 NANCY: Add warnings for unknown dependency types
1907b5f741 NANCY: Remove static members of GraphicsManager


Commit: fd11c41a8d1d43363b249ed92864f3435e3a237a
    https://github.com/scummvm/scummvm/commit/fd11c41a8d1d43363b249ed92864f3435e3a237a
Author: Strangerke (strangerke at scummvm.org)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add skeleton engine

Changed paths:
  A engines/nancy/configure.engine
  A engines/nancy/console.cpp
  A engines/nancy/console.h
  A engines/nancy/detection.cpp
  A engines/nancy/module.mk
  A engines/nancy/nancy.cpp
  A engines/nancy/nancy.h


diff --git a/engines/nancy/configure.engine b/engines/nancy/configure.engine
new file mode 100644
index 0000000000..643723c542
--- /dev/null
+++ b/engines/nancy/configure.engine
@@ -0,0 +1,3 @@
+# This file is included from the main "configure" script
+# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps]
+add_engine nancy "Nancy Drew" no
diff --git a/engines/nancy/console.cpp b/engines/nancy/console.cpp
new file mode 100644
index 0000000000..cc96c5922c
--- /dev/null
+++ b/engines/nancy/console.cpp
@@ -0,0 +1,34 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "nancy/console.h"
+#include "nancy/nancy.h"
+
+namespace Nancy {
+
+NancyConsole::NancyConsole(NancyEngine *vm) : GUI::Debugger(), _vm(vm) {
+}
+
+NancyConsole::~NancyConsole() {
+}
+
+} // End of namespace Nancy
diff --git a/engines/nancy/console.h b/engines/nancy/console.h
new file mode 100644
index 0000000000..900fdc0d60
--- /dev/null
+++ b/engines/nancy/console.h
@@ -0,0 +1,49 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef NANCY_CONSOLE_H
+#define NANCY_CONSOLE_H
+
+#include "gui/debugger.h"
+
+namespace Nancy {
+
+class NancyEngine;
+
+class NancyConsole : public GUI::Debugger {
+public:
+	NancyConsole(NancyEngine *vm);
+	virtual ~NancyConsole(void);
+
+private:
+	NancyEngine *_vm;
+	bool Cmd_listScreens(int argc, const char **argv);
+	bool Cmd_listObjects(int argc, const char **argv);
+	bool Cmd_getObject(int argc, const char **argv);
+	bool Cmd_getAllObjects(int argc, const char **argv);
+	bool Cmd_gotoScreen(int argc, const char **argv);
+	bool Cmd_boundaries(int argc, const char **argv);
+};
+
+} // End of namespace Nancy
+
+#endif
diff --git a/engines/nancy/detection.cpp b/engines/nancy/detection.cpp
new file mode 100644
index 0000000000..619c13d34e
--- /dev/null
+++ b/engines/nancy/detection.cpp
@@ -0,0 +1,234 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/advancedDetector.h"
+#include "common/system.h"
+#include "common/savefile.h"
+#include "common/textconsole.h"
+#include "graphics/thumbnail.h"
+#include "graphics/surface.h"
+
+#include "nancy/nancy.h"
+
+namespace Nancy {
+
+struct NancyGameDescription {
+	ADGameDescription desc;
+	GameType gameType;
+};
+
+uint32 NancyEngine::getFeatures() const {
+	return _gameDescription->desc.flags;
+}
+
+const char *NancyEngine::getGameId() const {
+	return _gameDescription->desc.gameid;
+}
+
+
+static const PlainGameDescriptor nancyGames[] = {
+	// Games
+	{"nancy1", "Nancy Drew 1"},
+	{0, 0}
+};
+
+static const NancyGameDescription gameDescriptions[] = {
+
+	{
+		{
+			"nancy1", 0,
+			{
+				{"ciftree.dat", 0, "92aaf84693a8948497ad57864fa31c2a", 71010},
+				AD_LISTEND
+			},
+			Common::EN_ANY,
+			Common::kPlatformWindows,
+			ADGF_NO_FLAGS,
+			GUIO0()
+		},
+		kGameTypeNancy1
+	},
+
+	{AD_TABLE_END_MARKER, kGameTypeNone}
+};
+
+class NancyMetaEngine : public AdvancedMetaEngine {
+public:
+	NancyMetaEngine() : AdvancedMetaEngine(gameDescriptions, sizeof(NancyGameDescription), nancyGames) {
+	}
+
+	const char *getName() const {
+		return "Nancy Drew";
+	}
+
+	const char *getOriginalCopyright() const {
+		return "Nancy Drew Engine copyright Her Interactive, 1995-2012";
+	}
+
+	bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *gd) const;
+	bool hasFeature(MetaEngineFeature f) const;
+
+	int getMaximumSaveSlot() const;
+	SaveStateList listSaves(const char *target) const;
+	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
+	void removeSaveState(const char *target, int slot) const;
+};
+
+bool NancyMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *gd) const {
+	if (gd) {
+		*engine = new NancyEngine(syst, (const NancyGameDescription *)gd);
+		((NancyEngine *)*engine)->initGame((const NancyGameDescription *)gd);
+	}
+	return gd != 0;
+}
+
+bool NancyMetaEngine::hasFeature(MetaEngineFeature f) const {
+	return
+	    (f == kSupportsListSaves) ||
+	    (f == kSupportsLoadingDuringStartup) ||
+	    (f == kSupportsDeleteSave) ||
+	    (f == kSavesSupportMetaInfo) ||
+	    (f == kSavesSupportThumbnail) ||
+	    (f == kSavesSupportCreationDate);
+}
+
+int NancyMetaEngine::getMaximumSaveSlot() const { return 99; }
+
+SaveStateList NancyMetaEngine::listSaves(const char *target) const {
+	Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
+	Common::StringArray filenames;
+	Common::String pattern = target;
+	pattern += "-??.SAV";
+
+	filenames = saveFileMan->listSavefiles(pattern);
+	sort(filenames.begin(), filenames.end());   // Sort (hopefully ensuring we are sorted numerically..)
+
+	SaveStateList saveList;
+	char slot[3];
+	int slotNum = 0;
+	for (Common::StringArray::const_iterator filename = filenames.begin(); filename != filenames.end(); ++filename) {
+		slot[0] = filename->c_str()[filename->size() - 6];
+		slot[1] = filename->c_str()[filename->size() - 5];
+		slot[2] = '\0';
+		// Obtain the last 2 digits of the filename (without extension), since they correspond to the save slot
+		slotNum = atoi(slot);
+		if (slotNum >= 0 && slotNum <= getMaximumSaveSlot()) {
+			Common::InSaveFile *file = saveFileMan->openForLoading(*filename);
+			if (file) {
+				int saveVersion = file->readByte();
+
+				if (saveVersion != kSavegameVersion) {
+					warning("Savegame of incompatible version");
+					delete file;
+					continue;
+				}
+
+				// read name
+				uint16 nameSize = file->readUint16BE();
+				if (nameSize >= 255) {
+					delete file;
+					continue;
+				}
+				char name[256];
+				file->read(name, nameSize);
+				name[nameSize] = 0;
+
+				saveList.push_back(SaveStateDescriptor(slotNum, name));
+				delete file;
+			}
+		}
+	}
+
+	return saveList;
+}
+
+SaveStateDescriptor NancyMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
+	Common::String fileName = Common::String::format("%s-%02d.SAV", target, slot);
+	Common::InSaveFile *file = g_system->getSavefileManager()->openForLoading(fileName);
+
+	if (file) {
+		int saveVersion = file->readByte();
+
+		if (saveVersion != kSavegameVersion) {
+			warning("Savegame of incompatible version");
+			delete file;
+			return SaveStateDescriptor();
+		}
+
+		uint32 saveNameLength = file->readUint16BE();
+		char saveName[256];
+		file->read(saveName, saveNameLength);
+		saveName[saveNameLength] = 0;
+
+		SaveStateDescriptor desc(slot, saveName);
+
+		Graphics::Surface *const thumbnail = Graphics::loadThumbnail(*file);
+		desc.setThumbnail(thumbnail);
+
+		desc.setDeletableFlag(true);
+		desc.setWriteProtectedFlag(false);
+
+		uint32 saveDate = file->readUint32BE();
+		uint16 saveTime = file->readUint16BE();
+
+		int day = (saveDate >> 24) & 0xFF;
+		int month = (saveDate >> 16) & 0xFF;
+		int year = saveDate & 0xFFFF;
+
+		desc.setSaveDate(year, month, day);
+
+		int hour = (saveTime >> 8) & 0xFF;
+		int minutes = saveTime & 0xFF;
+
+		desc.setSaveTime(hour, minutes);
+
+		// Slot 0 is used for the 'restart game' save in all Nancy games, thus
+		// we prevent it from being deleted.
+		desc.setDeletableFlag(slot != 0);
+		desc.setWriteProtectedFlag(slot == 0);
+
+		delete file;
+		return desc;
+	}
+	return SaveStateDescriptor();
+}
+
+void NancyMetaEngine::removeSaveState(const char *target, int slot) const {
+	Common::String fileName = Common::String::format("%s-%02d.SAV", target, slot);
+	g_system->getSavefileManager()->removeSavefile(fileName);
+}
+} // End of namespace Nancy
+
+#if PLUGIN_ENABLED_DYNAMIC(NANCY)
+REGISTER_PLUGIN_DYNAMIC(NANCY, PLUGIN_TYPE_ENGINE, Nancy::NancyMetaEngine);
+#else
+REGISTER_PLUGIN_STATIC(NANCY, PLUGIN_TYPE_ENGINE, Nancy::NancyMetaEngine);
+#endif
+
+namespace Nancy {
+
+void NancyEngine::initGame(const NancyGameDescription *gd) {
+	_gameType = gd->gameType;
+	_platform = gd->desc.platform;
+}
+
+} // End of namespace Nancy
diff --git a/engines/nancy/module.mk b/engines/nancy/module.mk
new file mode 100644
index 0000000000..b9bdc1ade2
--- /dev/null
+++ b/engines/nancy/module.mk
@@ -0,0 +1,14 @@
+MODULE := engines/nancy
+
+MODULE_OBJS = \
+  console.o \
+  nancy.o \
+  detection.o
+
+# This module can be built as a plugin
+ifeq ($(ENABLE_NANCY), DYNAMIC_PLUGIN)
+PLUGIN := 1
+endif
+
+# Include common rules
+include $(srcdir)/rules.mk
diff --git a/engines/nancy/nancy.cpp b/engines/nancy/nancy.cpp
new file mode 100644
index 0000000000..9c0a8bcc5a
--- /dev/null
+++ b/engines/nancy/nancy.cpp
@@ -0,0 +1,116 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/system.h"
+#include "common/random.h"
+#include "common/error.h"
+#include "common/events.h"
+#include "common/debug-channels.h"
+#include "common/config-manager.h"
+#include "common/textconsole.h"
+
+#include "nancy/nancy.h"
+
+#include "engines/util.h"
+
+namespace Nancy {
+
+NancyEngine *NancyEngine::s_Engine = 0;
+
+NancyEngine::NancyEngine(OSystem *syst, const NancyGameDescription *gd) : Engine(syst), _gameDescription(gd)
+{
+	_system = syst;
+	DebugMan.addDebugChannel(kDebugSchedule, "Schedule", "Script Schedule debug level");
+	DebugMan.addDebugChannel(kDebugEngine, "Engine", "Engine debug level");
+	DebugMan.addDebugChannel(kDebugDisplay, "Display", "Display debug level");
+	DebugMan.addDebugChannel(kDebugMouse, "Mouse", "Mouse debug level");
+	DebugMan.addDebugChannel(kDebugParser, "Parser", "Parser debug level");
+	DebugMan.addDebugChannel(kDebugFile, "File", "File IO debug level");
+	DebugMan.addDebugChannel(kDebugRoute, "Route", "Route debug level");
+	DebugMan.addDebugChannel(kDebugInventory, "Inventory", "Inventory debug level");
+	DebugMan.addDebugChannel(kDebugObject, "Object", "Object debug level");
+	DebugMan.addDebugChannel(kDebugMusic, "Music", "Music debug level");
+
+	_console = new NancyConsole(this);
+	_rnd = 0;
+}
+
+NancyEngine::~NancyEngine() {
+
+	DebugMan.clearAllDebugChannels();
+	delete _console;
+	delete _rnd;
+}
+
+GUI::Debugger *NancyEngine::getDebugger() {
+	return _console;
+}
+
+bool NancyEngine::hasFeature(EngineFeature f) const {
+	return (f == kSupportsRTL) || (f == kSupportsLoadingDuringRuntime) || (f == kSupportsSavingDuringRuntime);
+}
+
+const char *NancyEngine::getCopyrightString() const {
+	return "Copyright 1989-1997 David P Gray, All Rights Reserved.";
+}
+
+GameType NancyEngine::getGameType() const {
+	return _gameType;
+}
+
+Common::Platform NancyEngine::getPlatform() const {
+	return _platform;
+}
+
+Common::Error NancyEngine::run() {
+	s_Engine = this;
+	initGraphics(320, 200, false);
+	_console = new NancyConsole(this);
+
+//	_mouse = new MouseHandler(this);
+
+	// Setup mixer
+	syncSoundSettings();
+
+	return Common::kNoError;
+}
+
+void NancyEngine::initialize() {
+	debugC(1, kDebugEngine, "initialize");
+
+	_rnd = new Common::RandomSource("nancy");
+	_rnd->setSeed(42);                              // Kick random number generator
+}
+
+void NancyEngine::syncSoundSettings() {
+	Engine::syncSoundSettings();
+
+//	_sound->syncVolume();
+}
+
+Common::String NancyEngine::getSavegameFilename(int slot) {
+	return _targetName + Common::String::format("-%02d.SAV", slot);
+}
+
+
+
+} // End of namespace Nancy
diff --git a/engines/nancy/nancy.h b/engines/nancy/nancy.h
new file mode 100644
index 0000000000..71ff7a9891
--- /dev/null
+++ b/engines/nancy/nancy.h
@@ -0,0 +1,111 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef NANCY_H
+#define NANCY_H
+
+#include "engines/engine.h"
+#include "common/file.h"
+#include "nancy/console.h"
+
+namespace Common {
+class RandomSource;
+}
+
+/**
+ * This is the namespace of the Nancy engine.
+ *
+ * Status of this engine: ???
+ *
+ * Games using this engine:
+ * - Nancy Drew 1
+ * - ...
+ */
+namespace Nancy {
+
+static const int kSavegameVersion = 1;
+
+enum GameType {
+	kGameTypeNone  = 0,
+	kGameTypeNancy1,
+	kGameTypeNancy2
+};
+
+enum NancyDebugChannels {
+	kDebugSchedule  = 1 <<  0,
+	kDebugEngine    = 1 <<  1,
+	kDebugDisplay   = 1 <<  2,
+	kDebugMouse     = 1 <<  3,
+	kDebugParser    = 1 <<  4,
+	kDebugFile      = 1 <<  5,
+	kDebugRoute     = 1 <<  6,
+	kDebugInventory = 1 <<  7,
+	kDebugObject    = 1 <<  8,
+	kDebugMusic     = 1 <<  9
+};
+
+struct NancyGameDescription;
+
+class NancyEngine : public Engine {
+public:
+	NancyEngine(OSystem *syst, const NancyGameDescription *gd);
+	~NancyEngine();
+
+	OSystem *_system;
+
+	GUI::Debugger *getDebugger();
+
+	Common::RandomSource *_rnd;
+
+	const NancyGameDescription *_gameDescription;
+	uint32 getFeatures() const;
+	const char *getGameId() const;
+
+	void initGame(const NancyGameDescription *gd);
+
+	GameType getGameType() const;
+	Common::Platform getPlatform() const;
+
+	bool hasFeature(EngineFeature f) const;
+	const char *getCopyrightString() const;
+
+	Common::String getSavegameFilename(int slot);
+	void syncSoundSettings();
+
+protected:
+
+	// Engine APIs
+	Common::Error run();
+
+private:
+	static NancyEngine *s_Engine;
+
+	NancyConsole *_console;
+	GameType _gameType;
+	Common::Platform _platform;
+
+	void initialize();
+};
+
+} // End of namespace Nancy
+
+#endif // Nancy_H


Commit: fd1f0767f8d744c0a3224782b1b81d13a77654fb
    https://github.com/scummvm/scummvm/commit/fd1f0767f8d744c0a3224782b1b81d13a77654fb
Author: Walter van Niftrik (walter at scummvm.org)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add resource decompressor

Changed paths:
  A engines/nancy/resource.cpp
  A engines/nancy/resource.h
    engines/nancy/console.cpp
    engines/nancy/console.h
    engines/nancy/detection.cpp
    engines/nancy/module.mk
    engines/nancy/nancy.cpp
    engines/nancy/nancy.h


diff --git a/engines/nancy/console.cpp b/engines/nancy/console.cpp
index cc96c5922c..9cfb6a911a 100644
--- a/engines/nancy/console.cpp
+++ b/engines/nancy/console.cpp
@@ -20,15 +20,88 @@
  *
  */
 
+#include "common/system.h"
+#include "graphics/surface.h"
 #include "nancy/console.h"
 #include "nancy/nancy.h"
+#include "nancy/resource.h"
 
 namespace Nancy {
 
 NancyConsole::NancyConsole(NancyEngine *vm) : GUI::Debugger(), _vm(vm) {
+	registerCmd("res_hexdump", WRAP_METHOD(NancyConsole, Cmd_resHexDump));
+	registerCmd("res_list", WRAP_METHOD(NancyConsole, Cmd_resList));
+	registerCmd("res_info", WRAP_METHOD(NancyConsole, Cmd_resInfo));
+	registerCmd("res_show_image", WRAP_METHOD(NancyConsole, Cmd_resShowImage));
 }
 
 NancyConsole::~NancyConsole() {
 }
 
+bool NancyConsole::Cmd_resHexDump(int argc, const char **argv) {
+	if (argc != 2) {
+		debugPrintf("Dumps the specified resource to standard output\n");
+		debugPrintf("Usage: %s <resource name>\n", argv[0]);
+		return true;
+	}
+
+	uint size;
+	byte *buf = _vm->_res->loadResource(argv[1], size);
+	if (!buf) {
+		debugPrintf("Failed to load resource '%s'", argv[1]);
+		return true;
+	}
+
+	Common::hexdump(buf, size);
+	return true;
+}
+
+bool NancyConsole::Cmd_resList(int argc, const char **argv) {
+	if (argc != 1) {
+		debugPrintf("List all resources\n");
+		debugPrintf("Usage: %s\n", argv[0]);
+		return true;
+	}
+
+	Common::Array<Common::String> list;
+	_vm->_res->listResources(list);
+	for (uint i = 0; i < list.size(); i++) {
+		debugPrintf("%-10s", list[i].c_str());
+		if ((i % 8) == 7 && i + 1 != list.size())
+			debugPrintf("\n");
+	}
+
+	debugPrintf("\n");
+
+	return true;
+}
+
+bool NancyConsole::Cmd_resInfo(int argc, const char **argv) {
+	if (argc != 2) {
+		debugPrintf("Prints information about a resource\n");
+		debugPrintf("Usage: %s <resource name>\n", argv[0]);
+		return true;
+	}
+
+	debugPrintf("%s", _vm->_res->getResourceDesc(argv[1]).c_str());
+	return true;
+}
+
+bool NancyConsole::Cmd_resShowImage(int argc, const char **argv) {
+	if (argc != 2) {
+		debugPrintf("Draws an image on the screen\n");
+		debugPrintf("Usage: %s <resource name>\n", argv[0]);
+		return true;
+	}
+
+	Graphics::Surface surf;
+	if (_vm->_res->loadImage(argv[1], surf)) {
+		_vm->_system->fillScreen(0);
+		_vm->_system->copyRectToScreen(surf.getPixels(), surf.pitch, 0, 0, surf.w, surf.h);
+		surf.free();
+	} else
+		debugPrintf("Failed to load image\n");
+	return true;
+}
+
 } // End of namespace Nancy
diff --git a/engines/nancy/console.h b/engines/nancy/console.h
index 900fdc0d60..0ceea708e5 100644
--- a/engines/nancy/console.h
+++ b/engines/nancy/console.h
@@ -36,6 +36,11 @@ public:
 
 private:
 	NancyEngine *_vm;
+	bool Cmd_resHexDump(int argc, const char **argv);
+	bool Cmd_resList(int argc, const char **argv);
+	bool Cmd_resInfo(int argc, const char **argv);
+	bool Cmd_resShowImage(int argc, const char **argv);
+
 	bool Cmd_listScreens(int argc, const char **argv);
 	bool Cmd_listObjects(int argc, const char **argv);
 	bool Cmd_getObject(int argc, const char **argv);
diff --git a/engines/nancy/detection.cpp b/engines/nancy/detection.cpp
index 619c13d34e..d8a4617940 100644
--- a/engines/nancy/detection.cpp
+++ b/engines/nancy/detection.cpp
@@ -45,6 +45,11 @@ const char *NancyEngine::getGameId() const {
 }
 
 
+const char *const directoryGlobs[] = {
+	"game",
+	0
+};
+
 static const PlainGameDescriptor nancyGames[] = {
 	// Games
 	{"nancy1", "Nancy Drew 1"},
@@ -57,7 +62,7 @@ static const NancyGameDescription gameDescriptions[] = {
 		{
 			"nancy1", 0,
 			{
-				{"ciftree.dat", 0, "92aaf84693a8948497ad57864fa31c2a", 71010},
+				{"ciftree.dat", 0, "9f89e0b53717515ae0eb82d14ffe0e88", 4317962},
 				AD_LISTEND
 			},
 			Common::EN_ANY,
@@ -74,6 +79,8 @@ static const NancyGameDescription gameDescriptions[] = {
 class NancyMetaEngine : public AdvancedMetaEngine {
 public:
 	NancyMetaEngine() : AdvancedMetaEngine(gameDescriptions, sizeof(NancyGameDescription), nancyGames) {
+		_maxScanDepth = 2;
+		_directoryGlobs = directoryGlobs;
 	}
 
 	const char *getName() const {
diff --git a/engines/nancy/module.mk b/engines/nancy/module.mk
index b9bdc1ade2..34d280cee2 100644
--- a/engines/nancy/module.mk
+++ b/engines/nancy/module.mk
@@ -3,7 +3,8 @@ MODULE := engines/nancy
 MODULE_OBJS = \
   console.o \
   nancy.o \
-  detection.o
+  detection.o \
+  resource.o
 
 # This module can be built as a plugin
 ifeq ($(ENABLE_NANCY), DYNAMIC_PLUGIN)
diff --git a/engines/nancy/nancy.cpp b/engines/nancy/nancy.cpp
index 9c0a8bcc5a..545f91418b 100644
--- a/engines/nancy/nancy.cpp
+++ b/engines/nancy/nancy.cpp
@@ -29,6 +29,7 @@
 #include "common/textconsole.h"
 
 #include "nancy/nancy.h"
+#include "nancy/resource.h"
 
 #include "engines/util.h"
 
@@ -39,6 +40,10 @@ NancyEngine *NancyEngine::s_Engine = 0;
 NancyEngine::NancyEngine(OSystem *syst, const NancyGameDescription *gd) : Engine(syst), _gameDescription(gd)
 {
 	_system = syst;
+
+	const Common::FSNode gameDataDir(ConfMan.get("path"));
+	SearchMan.addSubDirectoryMatching(gameDataDir, "game");
+
 	DebugMan.addDebugChannel(kDebugSchedule, "Schedule", "Script Schedule debug level");
 	DebugMan.addDebugChannel(kDebugEngine, "Engine", "Engine debug level");
 	DebugMan.addDebugChannel(kDebugDisplay, "Display", "Display debug level");
@@ -83,14 +88,40 @@ Common::Platform NancyEngine::getPlatform() const {
 
 Common::Error NancyEngine::run() {
 	s_Engine = this;
-	initGraphics(320, 200, false);
+	Graphics::PixelFormat format(2, 5, 5, 5, 0, 10, 5, 0, 0);
+	initGraphics(640, 480, true, &format);
 	_console = new NancyConsole(this);
 
 //	_mouse = new MouseHandler(this);
+	_res = new ResourceManager(this);
+	_res->initialize();
 
 	// Setup mixer
 	syncSoundSettings();
 
+	Common::EventManager *ev = g_system->getEventManager();
+	bool quit = false;
+
+	while (!shouldQuit() && !quit) {
+		Common::Event event;
+		if (ev->pollEvent(event)) {
+			if (event.type == Common::EVENT_KEYDOWN && (event.kbd.flags & Common::KBD_CTRL)) {
+				switch(event.kbd.keycode) {
+				case Common::KEYCODE_q:
+					quit = true;
+					break;
+				case Common::KEYCODE_d:
+					_console->attach();
+				default:
+					break;
+				}
+			}
+		}
+		_console->onFrame();
+		_system->updateScreen();
+		_system->delayMillis(16);
+	}
+
 	return Common::kNoError;
 }
 
diff --git a/engines/nancy/nancy.h b/engines/nancy/nancy.h
index 71ff7a9891..6a13f68b2e 100644
--- a/engines/nancy/nancy.h
+++ b/engines/nancy/nancy.h
@@ -65,6 +65,8 @@ enum NancyDebugChannels {
 
 struct NancyGameDescription;
 
+class ResourceManager;
+
 class NancyEngine : public Engine {
 public:
 	NancyEngine(OSystem *syst, const NancyGameDescription *gd);
@@ -76,6 +78,8 @@ public:
 
 	Common::RandomSource *_rnd;
 
+	ResourceManager *_res;
+
 	const NancyGameDescription *_gameDescription;
 	uint32 getFeatures() const;
 	const char *getGameId() const;
diff --git a/engines/nancy/resource.cpp b/engines/nancy/resource.cpp
new file mode 100644
index 0000000000..0e358602a5
--- /dev/null
+++ b/engines/nancy/resource.cpp
@@ -0,0 +1,260 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/file.h"
+#include "common/textconsole.h"
+#include "common/debug.h"
+#include "common/memstream.h"
+#include "graphics/surface.h"
+#include "nancy/resource.h"
+
+namespace Nancy {
+
+ResourceManager::ResourceManager(NancyEngine *vm) : _vm(vm) {
+}
+
+ResourceManager::~ResourceManager() {
+}
+
+void ResourceManager::initialize() {
+	Common::File cifTree;
+
+	if (!cifTree.open("ciftree.dat"))
+		error("Failed to open ciftree.dat");
+
+	char id[20];
+	cifTree.read(id, 20);
+	id[19] = 0;
+
+	if (cifTree.eos() || Common::String(id) != "CIF TREE WayneSikes")
+		error("Invalid id string found in ciftree.dat");
+
+	// 4 bytes unused
+	cifTree.skip(4);
+
+	// Probably some kind of version number
+	uint16 ver[2];
+	ver[0] = cifTree.readUint16LE();
+	ver[1] = cifTree.readUint16LE();
+
+	if (cifTree.eos() || ver[0] != 2 || ver[1] != 0)
+		error("ciftree.dat version %i.%i not supported", ver[0], ver[1]);
+
+	// Number of info blocks
+	int infoBlockCount = cifTree.readUint16LE();
+
+	for (int i = 0; i < kHashMapSize; i++)
+		_hashMap[i] = cifTree.readUint16LE();
+
+	if (cifTree.eos())
+		error("Failed to read hash map from ciftree.dat");
+
+	for (int i = 0; i < infoBlockCount; i++) {
+		ResInfo info;
+
+		char name[9];
+		cifTree.read(name, 9);
+		name[8] = 0;
+		info.name = name;
+
+		cifTree.skip(2); // Index of this block
+		info.width = cifTree.readUint16LE();
+		info.pitch = cifTree.readUint16LE();
+		info.height = cifTree.readUint16LE();
+		cifTree.skip(1); // Unknown
+
+		byte flag = cifTree.readByte();
+		if (cifTree.eos() || flag != 2)
+			error("Unsupported compression flag found");
+
+		info.dataOffset = cifTree.readUint32LE();
+		info.size = cifTree.readUint32LE();
+		cifTree.skip(4); // Another size?
+		info.compressedSize = cifTree.readUint32LE();
+
+		info.flag = cifTree.readByte(); // Unknown flag
+
+		info.next = cifTree.readUint16LE();
+		if (cifTree.eos())
+			error("Failed to read info blocks from ciftree.dat");
+
+		_resInfo.push_back(info);
+	}
+
+	cifTree.close();
+}
+
+byte *ResourceManager::loadResource(const Common::String name, uint &size) {
+	const ResInfo *res = findResource(name);
+
+	if (!res) {
+		debug("Resource '%s' not found", name.c_str());
+		return 0;
+	}
+
+	byte *buf = decompress(*res, size);
+	return buf;
+}
+
+bool ResourceManager::loadImage(const Common::String name, Graphics::Surface &surf) {
+	const ResInfo *res = findResource(name);
+
+	if (!res) {
+		debug("Resource '%s' not found", name.c_str());
+		return 0;
+	}
+
+	uint size;
+	byte *buf = decompress(*res, size);
+
+	Graphics::PixelFormat format(2, 5, 5, 5, 0, 10, 5, 0, 0);
+	surf.w = res->width;
+	surf.h = res->height;
+	surf.pitch = res->pitch;
+	surf.setPixels(buf);
+	surf.format = format;
+	return true;
+}
+
+const ResourceManager::ResInfo *ResourceManager::findResource(const Common::String name) {
+	uint hash = 0;
+
+	for (uint i = 0; i < name.size(); i++)
+		hash += name[i];
+
+	hash &= kHashMapSize - 1;
+
+	uint16 index = _hashMap[hash];
+	while (index != 0xffff) {
+		const ResInfo &info = _resInfo[index];
+		if (name == info.name)
+			return &_resInfo[index];
+		index = info.next;
+	}
+
+	return 0;
+}
+
+byte *ResourceManager::decompress(const ResInfo &res, uint &size) {
+	// TODO: clean this up...
+	Common::File cifTree;
+	uint read = 0, written = 0;
+
+	if (!cifTree.open("ciftree.dat"))
+		error("Failed to open ciftree.dat");
+
+	cifTree.seek(res.dataOffset);
+
+	const int bufSize = 4096;
+	const int bufStart = 4078;
+	byte *buf = new byte[bufSize];
+	byte *output = new byte[res.size];
+	uint bufpos = bufStart;
+
+	memset(buf, ' ', bufStart);
+	uint16 bits = 0;
+	byte val = 0;
+
+	while(1) {
+		bits >>= 1;
+
+		// The highest 8 bits are used to keep track of how many bits are left to process
+		if (!(bits & 0x100)) {
+			// Out of bits
+			if (cifTree.eos() || read == res.compressedSize)
+				break;
+			bits = 0xff00 | ((cifTree.readByte() - val++) & 0xff);
+			++read;
+		}
+
+		if (bits & 1) {
+			// Literal byte
+			if (cifTree.eos() || read == res.compressedSize)
+				break;
+			byte b = cifTree.readByte() - val++;
+			++read;
+			output[written++] = b;
+			if (written == res.size)
+				break;
+			buf[bufpos++] = b;
+			bufpos &= bufSize - 1;
+		} else {
+			// Copy from buffer
+			if (cifTree.eos() || read == res.compressedSize)
+				break;
+			byte b1 = cifTree.readByte() - val++;
+			++read;
+
+			if (cifTree.eos() || read == res.compressedSize)
+				break;
+			byte b2 = cifTree.readByte() - val++;
+			++read;
+
+			uint16 offset = b1 | ((b2 & 0xf0) << 4);
+			uint16 len = (b2 & 0xf) + 3;
+	
+			for (uint i = 0; i < len; i++) {
+				byte t = buf[(offset + i) & (bufSize - 1)];
+				output[written++] = t;
+				if (written == res.size)
+					break;
+				buf[bufpos++] = t;
+				bufpos &=  bufSize - 1;
+			}
+		}
+	}
+
+	delete[] buf;
+
+	if (read != res.compressedSize || written != res.size) {
+		debug("Failed to decompress resource");
+		return 0;
+	}
+
+	size = written;
+	return output;
+}
+
+void ResourceManager::listResources(Common::Array<Common::String> &list) {
+	for (uint i = 0; i < _resInfo.size(); i++)
+		list.push_back(_resInfo[i].name);
+}
+
+Common::String ResourceManager::getResourceDesc(const Common::String name) {
+	const ResInfo *info = findResource(name);
+
+	if (!info)
+		return Common::String::format("Resource '%s' not found\n", name.c_str());
+
+	Common::String desc;
+	desc = Common::String::format("Name: %s\n", info->name.c_str());
+	desc += Common::String::format("Data offset: %i\n", info->dataOffset);
+	desc += Common::String::format("Width: %i\n", info->width);
+	desc += Common::String::format("Pitch: %i\n", info->pitch);
+	desc += Common::String::format("Height: %i\n", info->height);
+	desc += Common::String::format("Flag: %i\n", info->flag);
+	desc += Common::String::format("Size: %i\n", info->size);
+	desc += Common::String::format("Compressed size: %i\n", info->compressedSize);
+	return desc;
+}
+
+} // End of namespace Nancy
diff --git a/engines/nancy/resource.h b/engines/nancy/resource.h
new file mode 100644
index 0000000000..a76680f495
--- /dev/null
+++ b/engines/nancy/resource.h
@@ -0,0 +1,75 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef NANCY_RESOURCE_H
+#define NANCY_RESOURCE_H
+
+namespace Common {
+class MemoryReadStream;
+class String;
+}
+
+namespace Graphics {
+class Surface;
+}
+
+namespace Nancy {
+
+class NancyEngine;
+
+class ResourceManager {
+public:
+	ResourceManager(NancyEngine *vm);
+	~ResourceManager();
+
+	void initialize();
+	byte *loadResource(const Common::String name, uint &size);
+	bool loadImage(const Common::String name, Graphics::Surface &surf);
+
+	// Debugger functions
+	void listResources(Common::Array<Common::String> &list);
+	Common::String getResourceDesc(const Common::String name);
+private:
+	struct ResInfo {
+		Common::String name;
+		uint16 width, pitch, height;
+		byte flag;
+		uint32 compressedSize, size;
+		uint32 dataOffset;
+		uint16 next;
+	};
+
+	enum {
+		kHashMapSize = 1024
+	};
+
+	NancyEngine *_vm;
+	uint16 _hashMap[kHashMapSize];
+	Common::Array<ResInfo> _resInfo;
+
+	const ResInfo *findResource(const Common::String name);
+	byte *decompress(const ResInfo &res, uint &size);
+};
+
+} // End of namespace Nancy
+
+#endif


Commit: 13024420cbf1206300fdcf364c9b616d1867c9bb
    https://github.com/scummvm/scummvm/commit/13024420cbf1206300fdcf364c9b616d1867c9bb
Author: Walter van Niftrik (walter at scummvm.org)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Load more data from info blocks

Changed paths:
    engines/nancy/console.cpp
    engines/nancy/resource.cpp
    engines/nancy/resource.h


diff --git a/engines/nancy/console.cpp b/engines/nancy/console.cpp
index 9cfb6a911a..424ec2fb1c 100644
--- a/engines/nancy/console.cpp
+++ b/engines/nancy/console.cpp
@@ -57,14 +57,15 @@ bool NancyConsole::Cmd_resHexDump(int argc, const char **argv) {
 }
 
 bool NancyConsole::Cmd_resList(int argc, const char **argv) {
-	if (argc != 1) {
-		debugPrintf("List all resources\n");
-		debugPrintf("Usage: %s\n", argv[0]);
+	if (argc != 2) {
+		debugPrintf("List resources of a certain type\n");
+		debugPrintf("Types - 0: all, 2: image, 3: script\n");
+		debugPrintf("Usage: %s <resource type>\n", argv[0]);
 		return true;
 	}
 
 	Common::Array<Common::String> list;
-	_vm->_res->listResources(list);
+	_vm->_res->listResources(list, atoi(argv[1]));
 	for (uint i = 0; i < list.size(); i++) {
 		debugPrintf("%-10s", list[i].c_str());
 		if ((i % 8) == 7 && i + 1 != list.size())
@@ -97,7 +98,7 @@ bool NancyConsole::Cmd_resShowImage(int argc, const char **argv) {
 	Graphics::Surface surf;
 	if (_vm->_res->loadImage(argv[1], surf)) {
 		_vm->_system->fillScreen(0);
-		_vm->_system->copyRectToScreen(surf.getPixels(), surf.pitch, 0, 0, surf.w, surf.h);
+		_vm->_system->copyRectToScreen(surf.getPixels(), surf.pitch, 0, 0, surf.w > 640 ? 640 : surf.w, surf.h > 480 ? 480 : surf.h);
 		surf.free();
 	} else
 		debugPrintf("Failed to load image\n");
diff --git a/engines/nancy/resource.cpp b/engines/nancy/resource.cpp
index 0e358602a5..daa0e97469 100644
--- a/engines/nancy/resource.cpp
+++ b/engines/nancy/resource.cpp
@@ -80,18 +80,15 @@ void ResourceManager::initialize() {
 		info.width = cifTree.readUint16LE();
 		info.pitch = cifTree.readUint16LE();
 		info.height = cifTree.readUint16LE();
-		cifTree.skip(1); // Unknown
-
-		byte flag = cifTree.readByte();
-		if (cifTree.eos() || flag != 2)
-			error("Unsupported compression flag found");
+		info.depth = cifTree.readByte();
 
+		info.comp = cifTree.readByte();
 		info.dataOffset = cifTree.readUint32LE();
 		info.size = cifTree.readUint32LE();
-		cifTree.skip(4); // Another size?
+		info.sizeUnk = cifTree.readUint32LE(); // Unknown
 		info.compressedSize = cifTree.readUint32LE();
 
-		info.flag = cifTree.readByte(); // Unknown flag
+		info.type = cifTree.readByte();
 
 		info.next = cifTree.readUint16LE();
 		if (cifTree.eos())
@@ -123,6 +120,16 @@ bool ResourceManager::loadImage(const Common::String name, Graphics::Surface &su
 		return 0;
 	}
 
+	if (res->type != kResTypeImage) {
+		debug("Resource '%s' is not an image", name.c_str());
+		return 0;
+	}
+
+	if (res->depth != 16) {
+		debug("Image '%s' has unsupported depth %i", name.c_str(), res->depth);
+		return 0;
+	}
+
 	uint size;
 	byte *buf = decompress(*res, size);
 
@@ -159,6 +166,11 @@ byte *ResourceManager::decompress(const ResInfo &res, uint &size) {
 	Common::File cifTree;
 	uint read = 0, written = 0;
 
+	if (res.comp != kResCompression) {
+		debug("Resource '%s' is not compressed", res.name.c_str());
+		return 0;
+	}
+
 	if (!cifTree.open("ciftree.dat"))
 		error("Failed to open ciftree.dat");
 
@@ -234,9 +246,11 @@ byte *ResourceManager::decompress(const ResInfo &res, uint &size) {
 	return output;
 }
 
-void ResourceManager::listResources(Common::Array<Common::String> &list) {
-	for (uint i = 0; i < _resInfo.size(); i++)
-		list.push_back(_resInfo[i].name);
+void ResourceManager::listResources(Common::Array<Common::String> &list, uint type) {
+	for (uint i = 0; i < _resInfo.size(); i++) {
+		if (type == kResTypeAny || _resInfo[i].type == type)
+			list.push_back(_resInfo[i].name);
+	}
 }
 
 Common::String ResourceManager::getResourceDesc(const Common::String name) {
@@ -247,13 +261,16 @@ Common::String ResourceManager::getResourceDesc(const Common::String name) {
 
 	Common::String desc;
 	desc = Common::String::format("Name: %s\n", info->name.c_str());
+	desc += Common::String::format("Type: %i\n", info->type);
+	desc += Common::String::format("Compression: %i\n", info->comp);
 	desc += Common::String::format("Data offset: %i\n", info->dataOffset);
+	desc += Common::String::format("Size: %i\n", info->size);
+	desc += Common::String::format("Size (unknown): %i\n", info->sizeUnk);
+	desc += Common::String::format("Compressed size: %i\n", info->compressedSize);
 	desc += Common::String::format("Width: %i\n", info->width);
 	desc += Common::String::format("Pitch: %i\n", info->pitch);
 	desc += Common::String::format("Height: %i\n", info->height);
-	desc += Common::String::format("Flag: %i\n", info->flag);
-	desc += Common::String::format("Size: %i\n", info->size);
-	desc += Common::String::format("Compressed size: %i\n", info->compressedSize);
+	desc += Common::String::format("Bit depth: %i\n", info->depth);
 	return desc;
 }
 
diff --git a/engines/nancy/resource.h b/engines/nancy/resource.h
index a76680f495..7e61bc54de 100644
--- a/engines/nancy/resource.h
+++ b/engines/nancy/resource.h
@@ -38,6 +38,17 @@ class NancyEngine;
 
 class ResourceManager {
 public:
+	enum ResType {
+		kResTypeAny,
+		kResTypeImage = 2,
+		kResTypeScript
+	};
+
+	enum ResCompression {
+		kResCompressionNone = 1,
+		kResCompression
+	};
+
 	ResourceManager(NancyEngine *vm);
 	~ResourceManager();
 
@@ -46,14 +57,16 @@ public:
 	bool loadImage(const Common::String name, Graphics::Surface &surf);
 
 	// Debugger functions
-	void listResources(Common::Array<Common::String> &list);
+	void listResources(Common::Array<Common::String> &list, uint type);
 	Common::String getResourceDesc(const Common::String name);
 private:
 	struct ResInfo {
 		Common::String name;
+		byte type; // ResType
+		byte comp; // ResCompression
 		uint16 width, pitch, height;
-		byte flag;
-		uint32 compressedSize, size;
+		byte depth; // Bit depth
+		uint32 compressedSize, size, sizeUnk;
 		uint32 dataOffset;
 		uint16 next;
 	};


Commit: 1f2932b4f293cb64d31119f09e7bb83669db132b
    https://github.com/scummvm/scummvm/commit/1f2932b4f293cb64d31119f09e7bb83669db132b
Author: Walter van Niftrik (walter at scummvm.org)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add signature for game 2

Changed paths:
    engines/nancy/detection.cpp


diff --git a/engines/nancy/detection.cpp b/engines/nancy/detection.cpp
index d8a4617940..3b096e4470 100644
--- a/engines/nancy/detection.cpp
+++ b/engines/nancy/detection.cpp
@@ -52,7 +52,8 @@ const char *const directoryGlobs[] = {
 
 static const PlainGameDescriptor nancyGames[] = {
 	// Games
-	{"nancy1", "Nancy Drew 1"},
+	{"nancy1", "Nancy Drew 1: Secrets Can Kill"},
+	{"nancy2", "Nancy Drew 2: Stay Tuned For Danger"},
 	{0, 0}
 };
 
@@ -72,6 +73,20 @@ static const NancyGameDescription gameDescriptions[] = {
 		},
 		kGameTypeNancy1
 	},
+	{
+		{
+			"nancy2", 0,
+			{
+				{"ciftree.dat", 0, "fa4293d728a1b31407961cd82e86a015", 7784516},
+				AD_LISTEND
+			},
+			Common::EN_ANY,
+			Common::kPlatformWindows,
+			ADGF_NO_FLAGS,
+			GUIO0()
+		},
+		kGameTypeNancy1
+	},
 
 	{AD_TABLE_END_MARKER, kGameTypeNone}
 };


Commit: 892ac7f83539dbf516eb3230c87b26e15a9fcde6
    https://github.com/scummvm/scummvm/commit/892ac7f83539dbf516eb3230c87b26e15a9fcde6
Author: Walter van Niftrik (walter at scummvm.org)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Refactor resource manager

Changed paths:
    engines/nancy/resource.cpp
    engines/nancy/resource.h


diff --git a/engines/nancy/resource.cpp b/engines/nancy/resource.cpp
index daa0e97469..9bf9a98189 100644
--- a/engines/nancy/resource.cpp
+++ b/engines/nancy/resource.cpp
@@ -29,157 +29,199 @@
 
 namespace Nancy {
 
-ResourceManager::ResourceManager(NancyEngine *vm) : _vm(vm) {
-}
-
-ResourceManager::~ResourceManager() {
+class CifTree {
+public:
+	virtual ~CifTree() { };	
+	bool findResource(const Common::String name, ResourceManager::CifInfo &info);
+	void listResources(Common::Array<Common::String> &list, uint type);
+
+protected:
+	enum {
+		kHashMapSize = 1024
+	};
+
+	struct CifInfoChain {
+		struct ResourceManager::CifInfo info;
+		uint16 next;
+	};
+
+	uint16 _hashMap[kHashMapSize];
+	Common::Array<CifInfoChain> _cifInfo;
+};
+
+void CifTree::listResources(Common::Array<Common::String> &list, uint type) {
+	for (uint i = 0; i < _cifInfo.size(); i++) {
+		if (type == ResourceManager::kResTypeAny || _cifInfo[i].info.type == type)
+			list.push_back(_cifInfo[i].info.name);
+	}
 }
 
-void ResourceManager::initialize() {
-	Common::File cifTree;
-
-	if (!cifTree.open("ciftree.dat"))
-		error("Failed to open ciftree.dat");
+bool CifTree::findResource(const Common::String name, ResourceManager::CifInfo &info) {
+	uint hash = 0;
 
-	char id[20];
-	cifTree.read(id, 20);
-	id[19] = 0;
+	for (uint i = 0; i < name.size(); i++)
+		hash += name[i];
 
-	if (cifTree.eos() || Common::String(id) != "CIF TREE WayneSikes")
-		error("Invalid id string found in ciftree.dat");
+	hash &= kHashMapSize - 1;
 
-	// 4 bytes unused
-	cifTree.skip(4);
+	uint16 index = _hashMap[hash];
+	while (index != 0xffff) {
+		if (name == _cifInfo[index].info.name) {
+			info = _cifInfo[index].info;
+			return true;
+		}
+		index = _cifInfo[index].next;
+	}
 
-	// Probably some kind of version number
-	uint16 ver[2];
-	ver[0] = cifTree.readUint16LE();
-	ver[1] = cifTree.readUint16LE();
+	return false;
+}
 
-	if (cifTree.eos() || ver[0] != 2 || ver[1] != 0)
-		error("ciftree.dat version %i.%i not supported", ver[0], ver[1]);
+class CifTree20 : public CifTree {
+public:
+	CifTree20(Common::File &f);
+	virtual ~CifTree20() { };
+};
 
+CifTree20::CifTree20(Common::File &f) {
 	// Number of info blocks
-	int infoBlockCount = cifTree.readUint16LE();
+	int infoBlockCount = f.readUint16LE();
 
 	for (int i = 0; i < kHashMapSize; i++)
-		_hashMap[i] = cifTree.readUint16LE();
+		_hashMap[i] = f.readUint16LE();
 
-	if (cifTree.eos())
+	if (f.eos())
 		error("Failed to read hash map from ciftree.dat");
 
 	for (int i = 0; i < infoBlockCount; i++) {
-		ResInfo info;
+		CifInfoChain chain;
+		ResourceManager::CifInfo &info = chain.info;
 
 		char name[9];
-		cifTree.read(name, 9);
+		f.read(name, 9);
 		name[8] = 0;
 		info.name = name;
 
-		cifTree.skip(2); // Index of this block
-		info.width = cifTree.readUint16LE();
-		info.pitch = cifTree.readUint16LE();
-		info.height = cifTree.readUint16LE();
-		info.depth = cifTree.readByte();
+		f.skip(2); // Index of this block
+		info.width = f.readUint16LE();
+		info.pitch = f.readUint16LE();
+		info.height = f.readUint16LE();
+		info.depth = f.readByte();
 
-		info.comp = cifTree.readByte();
-		info.dataOffset = cifTree.readUint32LE();
-		info.size = cifTree.readUint32LE();
-		info.sizeUnk = cifTree.readUint32LE(); // Unknown
-		info.compressedSize = cifTree.readUint32LE();
+		info.comp = f.readByte();
+		info.dataOffset = f.readUint32LE();
+		info.size = f.readUint32LE();
+		info.sizeUnk = f.readUint32LE(); // Unknown
+		info.compressedSize = f.readUint32LE();
 
-		info.type = cifTree.readByte();
+		info.type = f.readByte();
 
-		info.next = cifTree.readUint16LE();
-		if (cifTree.eos())
+		chain.next = f.readUint16LE();
+		if (f.eos())
 			error("Failed to read info blocks from ciftree.dat");
 
-		_resInfo.push_back(info);
+		_cifInfo.push_back(chain);
+	}
+}
+
+ResourceManager::ResourceManager(NancyEngine *vm) : _vm(vm) {
+}
+
+ResourceManager::~ResourceManager() {
+}
+
+void ResourceManager::initialize() {
+	Common::File cifTree;
+
+	if (!cifTree.open("ciftree.dat"))
+		error("Failed to open ciftree.dat");
+
+	char id[20];
+	cifTree.read(id, 20);
+	id[19] = 0;
+
+	if (cifTree.eos() || Common::String(id) != "CIF TREE WayneSikes")
+		error("Invalid id string found in ciftree.dat");
+
+	// 4 bytes unused
+	cifTree.skip(4);
+
+	// Probably some kind of version number
+	uint32 ver;
+	ver = cifTree.readUint16LE() << 16;
+	ver |= cifTree.readUint16LE();
+
+	switch(ver) {
+	case 0x00020000:
+		_cifTree = new CifTree20(cifTree);
+		break;
+	default:
+		error("CifTree version %d.%d not supported", ver >> 16, ver & 0xffff);
 	}
 
 	cifTree.close();
 }
 
 byte *ResourceManager::loadResource(const Common::String name, uint &size) {
-	const ResInfo *res = findResource(name);
+	CifInfo info;
 
-	if (!res) {
+	if (!_cifTree->findResource(name, info)) {
 		debug("Resource '%s' not found", name.c_str());
 		return 0;
 	}
 
-	byte *buf = decompress(*res, size);
+	byte *buf = decompress(info, size);
 	return buf;
 }
 
 bool ResourceManager::loadImage(const Common::String name, Graphics::Surface &surf) {
-	const ResInfo *res = findResource(name);
+	CifInfo info;
 
-	if (!res) {
+	if (!_cifTree->findResource(name, info)) {
 		debug("Resource '%s' not found", name.c_str());
 		return 0;
 	}
 
-	if (res->type != kResTypeImage) {
+	if (info.type != kResTypeImage) {
 		debug("Resource '%s' is not an image", name.c_str());
 		return 0;
 	}
 
-	if (res->depth != 16) {
-		debug("Image '%s' has unsupported depth %i", name.c_str(), res->depth);
+	if (info.depth != 16) {
+		debug("Image '%s' has unsupported depth %i", name.c_str(), info.depth);
 		return 0;
 	}
 
 	uint size;
-	byte *buf = decompress(*res, size);
+	byte *buf = decompress(info, size);
 
 	Graphics::PixelFormat format(2, 5, 5, 5, 0, 10, 5, 0, 0);
-	surf.w = res->width;
-	surf.h = res->height;
-	surf.pitch = res->pitch;
+	surf.w = info.width;
+	surf.h = info.height;
+	surf.pitch = info.pitch;
 	surf.setPixels(buf);
 	surf.format = format;
 	return true;
 }
 
-const ResourceManager::ResInfo *ResourceManager::findResource(const Common::String name) {
-	uint hash = 0;
-
-	for (uint i = 0; i < name.size(); i++)
-		hash += name[i];
-
-	hash &= kHashMapSize - 1;
-
-	uint16 index = _hashMap[hash];
-	while (index != 0xffff) {
-		const ResInfo &info = _resInfo[index];
-		if (name == info.name)
-			return &_resInfo[index];
-		index = info.next;
-	}
-
-	return 0;
-}
-
-byte *ResourceManager::decompress(const ResInfo &res, uint &size) {
+byte *ResourceManager::decompress(const CifInfo &info, uint &size) {
 	// TODO: clean this up...
 	Common::File cifTree;
 	uint read = 0, written = 0;
 
-	if (res.comp != kResCompression) {
-		debug("Resource '%s' is not compressed", res.name.c_str());
+	if (info.comp != kResCompression) {
+		debug("Resource '%s' is not compressed", info.name.c_str());
 		return 0;
 	}
 
 	if (!cifTree.open("ciftree.dat"))
 		error("Failed to open ciftree.dat");
 
-	cifTree.seek(res.dataOffset);
+	cifTree.seek(info.dataOffset);
 
 	const int bufSize = 4096;
 	const int bufStart = 4078;
 	byte *buf = new byte[bufSize];
-	byte *output = new byte[res.size];
+	byte *output = new byte[info.size];
 	uint bufpos = bufStart;
 
 	memset(buf, ' ', bufStart);
@@ -192,7 +234,7 @@ byte *ResourceManager::decompress(const ResInfo &res, uint &size) {
 		// The highest 8 bits are used to keep track of how many bits are left to process
 		if (!(bits & 0x100)) {
 			// Out of bits
-			if (cifTree.eos() || read == res.compressedSize)
+			if (cifTree.eos() || read == info.compressedSize)
 				break;
 			bits = 0xff00 | ((cifTree.readByte() - val++) & 0xff);
 			++read;
@@ -200,23 +242,23 @@ byte *ResourceManager::decompress(const ResInfo &res, uint &size) {
 
 		if (bits & 1) {
 			// Literal byte
-			if (cifTree.eos() || read == res.compressedSize)
+			if (cifTree.eos() || read == info.compressedSize)
 				break;
 			byte b = cifTree.readByte() - val++;
 			++read;
 			output[written++] = b;
-			if (written == res.size)
+			if (written == info.size)
 				break;
 			buf[bufpos++] = b;
 			bufpos &= bufSize - 1;
 		} else {
 			// Copy from buffer
-			if (cifTree.eos() || read == res.compressedSize)
+			if (cifTree.eos() || read == info.compressedSize)
 				break;
 			byte b1 = cifTree.readByte() - val++;
 			++read;
 
-			if (cifTree.eos() || read == res.compressedSize)
+			if (cifTree.eos() || read == info.compressedSize)
 				break;
 			byte b2 = cifTree.readByte() - val++;
 			++read;
@@ -227,7 +269,7 @@ byte *ResourceManager::decompress(const ResInfo &res, uint &size) {
 			for (uint i = 0; i < len; i++) {
 				byte t = buf[(offset + i) & (bufSize - 1)];
 				output[written++] = t;
-				if (written == res.size)
+				if (written == info.size)
 					break;
 				buf[bufpos++] = t;
 				bufpos &=  bufSize - 1;
@@ -237,7 +279,7 @@ byte *ResourceManager::decompress(const ResInfo &res, uint &size) {
 
 	delete[] buf;
 
-	if (read != res.compressedSize || written != res.size) {
+	if (read != info.compressedSize || written != info.size) {
 		debug("Failed to decompress resource");
 		return 0;
 	}
@@ -247,30 +289,27 @@ byte *ResourceManager::decompress(const ResInfo &res, uint &size) {
 }
 
 void ResourceManager::listResources(Common::Array<Common::String> &list, uint type) {
-	for (uint i = 0; i < _resInfo.size(); i++) {
-		if (type == kResTypeAny || _resInfo[i].type == type)
-			list.push_back(_resInfo[i].name);
-	}
+	_cifTree->listResources(list, type);
 }
 
 Common::String ResourceManager::getResourceDesc(const Common::String name) {
-	const ResInfo *info = findResource(name);
+	CifInfo info;
 
-	if (!info)
+	if (!_cifTree->findResource(name, info))
 		return Common::String::format("Resource '%s' not found\n", name.c_str());
 
 	Common::String desc;
-	desc = Common::String::format("Name: %s\n", info->name.c_str());
-	desc += Common::String::format("Type: %i\n", info->type);
-	desc += Common::String::format("Compression: %i\n", info->comp);
-	desc += Common::String::format("Data offset: %i\n", info->dataOffset);
-	desc += Common::String::format("Size: %i\n", info->size);
-	desc += Common::String::format("Size (unknown): %i\n", info->sizeUnk);
-	desc += Common::String::format("Compressed size: %i\n", info->compressedSize);
-	desc += Common::String::format("Width: %i\n", info->width);
-	desc += Common::String::format("Pitch: %i\n", info->pitch);
-	desc += Common::String::format("Height: %i\n", info->height);
-	desc += Common::String::format("Bit depth: %i\n", info->depth);
+	desc = Common::String::format("Name: %s\n", info.name.c_str());
+	desc += Common::String::format("Type: %i\n", info.type);
+	desc += Common::String::format("Compression: %i\n", info.comp);
+	desc += Common::String::format("Data offset: %i\n", info.dataOffset);
+	desc += Common::String::format("Size: %i\n", info.size);
+	desc += Common::String::format("Size (unknown): %i\n", info.sizeUnk);
+	desc += Common::String::format("Compressed size: %i\n", info.compressedSize);
+	desc += Common::String::format("Width: %i\n", info.width);
+	desc += Common::String::format("Pitch: %i\n", info.pitch);
+	desc += Common::String::format("Height: %i\n", info.height);
+	desc += Common::String::format("Bit depth: %i\n", info.depth);
 	return desc;
 }
 
diff --git a/engines/nancy/resource.h b/engines/nancy/resource.h
index 7e61bc54de..48e4df6146 100644
--- a/engines/nancy/resource.h
+++ b/engines/nancy/resource.h
@@ -35,6 +35,7 @@ class Surface;
 namespace Nancy {
 
 class NancyEngine;
+class CifTree;
 
 class ResourceManager {
 public:
@@ -49,6 +50,16 @@ public:
 		kResCompression
 	};
 
+	struct CifInfo {
+		Common::String name;
+		byte type; // ResType
+		byte comp; // ResCompression
+		uint16 width, pitch, height;
+		byte depth; // Bit depth
+		uint32 compressedSize, size, sizeUnk;
+		uint32 dataOffset;
+	};
+
 	ResourceManager(NancyEngine *vm);
 	~ResourceManager();
 
@@ -60,27 +71,10 @@ public:
 	void listResources(Common::Array<Common::String> &list, uint type);
 	Common::String getResourceDesc(const Common::String name);
 private:
-	struct ResInfo {
-		Common::String name;
-		byte type; // ResType
-		byte comp; // ResCompression
-		uint16 width, pitch, height;
-		byte depth; // Bit depth
-		uint32 compressedSize, size, sizeUnk;
-		uint32 dataOffset;
-		uint16 next;
-	};
-
-	enum {
-		kHashMapSize = 1024
-	};
-
 	NancyEngine *_vm;
-	uint16 _hashMap[kHashMapSize];
-	Common::Array<ResInfo> _resInfo;
+	CifTree *_cifTree;
 
-	const ResInfo *findResource(const Common::String name);
-	byte *decompress(const ResInfo &res, uint &size);
+	byte *decompress(const CifInfo &res, uint &size);
 };
 
 } // End of namespace Nancy


Commit: 410861661af60e581ee49c8c999f33f06ac2b105
    https://github.com/scummvm/scummvm/commit/410861661af60e581ee49c8c999f33f06ac2b105
Author: Walter van Niftrik (walter at scummvm.org)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Refactor resource manager

Changed paths:
    engines/nancy/resource.cpp
    engines/nancy/resource.h


diff --git a/engines/nancy/resource.cpp b/engines/nancy/resource.cpp
index 9bf9a98189..ee090f289f 100644
--- a/engines/nancy/resource.cpp
+++ b/engines/nancy/resource.cpp
@@ -31,7 +31,8 @@ namespace Nancy {
 
 class CifTree {
 public:
-	virtual ~CifTree() { };	
+	virtual ~CifTree() { };
+	bool initialize(Common::File &f);
 	bool findResource(const Common::String name, ResourceManager::CifInfo &info);
 	void listResources(Common::Array<Common::String> &list, uint type);
 
@@ -45,10 +46,34 @@ protected:
 		uint16 next;
 	};
 
+	virtual uint readHeader(Common::File &f) = 0;
+	virtual void readCifInfo(Common::File &f, CifInfoChain &chain) = 0;
+
 	uint16 _hashMap[kHashMapSize];
 	Common::Array<CifInfoChain> _cifInfo;
+	Common::String _filename;
 };
 
+bool CifTree::initialize(Common::File &f) {
+	_filename = f.getName();
+
+	int infoBlockCount = readHeader(f);
+
+	for (int i = 0; i < kHashMapSize; i++)
+		_hashMap[i] = f.readUint16LE();
+
+	if (f.eos())
+		error("Error reading CifTree '%s'", _filename.c_str());
+
+	for (int i = 0; i < infoBlockCount; i++) {
+		CifInfoChain chain;
+		readCifInfo(f, chain);
+		_cifInfo.push_back(chain);
+	}
+
+	return true;
+}
+
 void CifTree::listResources(Common::Array<Common::String> &list, uint type) {
 	for (uint i = 0; i < _cifInfo.size(); i++) {
 		if (type == ResourceManager::kResTypeAny || _cifInfo[i].info.type == type)
@@ -77,50 +102,46 @@ bool CifTree::findResource(const Common::String name, ResourceManager::CifInfo &
 }
 
 class CifTree20 : public CifTree {
-public:
-	CifTree20(Common::File &f);
-	virtual ~CifTree20() { };
+protected:
+	virtual uint readHeader(Common::File &f);
+	virtual void readCifInfo(Common::File &f, CifInfoChain &chain);
 };
 
-CifTree20::CifTree20(Common::File &f) {
-	// Number of info blocks
-	int infoBlockCount = f.readUint16LE();
-
-	for (int i = 0; i < kHashMapSize; i++)
-		_hashMap[i] = f.readUint16LE();
+uint CifTree20::readHeader(Common::File &f) {
+	uint infoBlockCount = f.readUint16LE();
 
 	if (f.eos())
-		error("Failed to read hash map from ciftree.dat");
+		error("Failed to read cif info block count from CifTree");
 
-	for (int i = 0; i < infoBlockCount; i++) {
-		CifInfoChain chain;
-		ResourceManager::CifInfo &info = chain.info;
+	return infoBlockCount;
+}
 
-		char name[9];
-		f.read(name, 9);
-		name[8] = 0;
-		info.name = name;
+void CifTree20::readCifInfo(Common::File &f, CifInfoChain &chain)
+{
+	ResourceManager::CifInfo &info = chain.info;
 
-		f.skip(2); // Index of this block
-		info.width = f.readUint16LE();
-		info.pitch = f.readUint16LE();
-		info.height = f.readUint16LE();
-		info.depth = f.readByte();
+	char name[9];
+	f.read(name, 9);
+	name[8] = 0;
+	info.name = name;
 
-		info.comp = f.readByte();
-		info.dataOffset = f.readUint32LE();
-		info.size = f.readUint32LE();
-		info.sizeUnk = f.readUint32LE(); // Unknown
-		info.compressedSize = f.readUint32LE();
+	f.skip(2); // Index of this block
+	info.width = f.readUint16LE();
+	info.pitch = f.readUint16LE();
+	info.height = f.readUint16LE();
+	info.depth = f.readByte();
 
-		info.type = f.readByte();
+	info.comp = f.readByte();
+	info.dataOffset = f.readUint32LE();
+	info.size = f.readUint32LE();
+	info.sizeUnk = f.readUint32LE(); // Unknown
+	info.compressedSize = f.readUint32LE();
 
-		chain.next = f.readUint16LE();
-		if (f.eos())
-			error("Failed to read info blocks from ciftree.dat");
+	info.type = f.readByte();
 
-		_cifInfo.push_back(chain);
-	}
+	chain.next = f.readUint16LE();
+	if (f.eos())
+		error("Failed to read info block from CifTree");
 }
 
 ResourceManager::ResourceManager(NancyEngine *vm) : _vm(vm) {
@@ -130,42 +151,45 @@ ResourceManager::~ResourceManager() {
 }
 
 void ResourceManager::initialize() {
-	Common::File cifTree;
+	Common::File f;
 
-	if (!cifTree.open("ciftree.dat"))
+	if (!f.open("ciftree.dat"))
 		error("Failed to open ciftree.dat");
 
 	char id[20];
-	cifTree.read(id, 20);
+	f.read(id, 20);
 	id[19] = 0;
 
-	if (cifTree.eos() || Common::String(id) != "CIF TREE WayneSikes")
+	if (f.eos() || Common::String(id) != "CIF TREE WayneSikes")
 		error("Invalid id string found in ciftree.dat");
 
 	// 4 bytes unused
-	cifTree.skip(4);
+	f.skip(4);
 
 	// Probably some kind of version number
 	uint32 ver;
-	ver = cifTree.readUint16LE() << 16;
-	ver |= cifTree.readUint16LE();
+	ver = f.readUint16LE() << 16;
+	ver |= f.readUint16LE();
 
 	switch(ver) {
 	case 0x00020000:
-		_cifTree = new CifTree20(cifTree);
+		_cifTree = new CifTree20;
 		break;
 	default:
 		error("CifTree version %d.%d not supported", ver >> 16, ver & 0xffff);
 	}
 
-	cifTree.close();
+	if (!_cifTree->initialize(f))
+		error("Failed to read CifTree 'ciftree.dat'");
+
+	f.close();
 }
 
 byte *ResourceManager::loadResource(const Common::String name, uint &size) {
 	CifInfo info;
 
 	if (!_cifTree->findResource(name, info)) {
-		debug("Resource '%s' not found", name.c_str());
+		warning("Resource '%s' not found", name.c_str());
 		return 0;
 	}
 
@@ -177,17 +201,17 @@ bool ResourceManager::loadImage(const Common::String name, Graphics::Surface &su
 	CifInfo info;
 
 	if (!_cifTree->findResource(name, info)) {
-		debug("Resource '%s' not found", name.c_str());
+		warning("Resource '%s' not found", name.c_str());
 		return 0;
 	}
 
 	if (info.type != kResTypeImage) {
-		debug("Resource '%s' is not an image", name.c_str());
+		warning("Resource '%s' is not an image", name.c_str());
 		return 0;
 	}
 
 	if (info.depth != 16) {
-		debug("Image '%s' has unsupported depth %i", name.c_str(), info.depth);
+		warning("Image '%s' has unsupported depth %i", name.c_str(), info.depth);
 		return 0;
 	}
 
@@ -209,7 +233,7 @@ byte *ResourceManager::decompress(const CifInfo &info, uint &size) {
 	uint read = 0, written = 0;
 
 	if (info.comp != kResCompression) {
-		debug("Resource '%s' is not compressed", info.name.c_str());
+		warning("Resource '%s' is not compressed", info.name.c_str());
 		return 0;
 	}
 
@@ -280,7 +304,7 @@ byte *ResourceManager::decompress(const CifInfo &info, uint &size) {
 	delete[] buf;
 
 	if (read != info.compressedSize || written != info.size) {
-		debug("Failed to decompress resource");
+		warning("Failed to decompress resource");
 		return 0;
 	}
 
diff --git a/engines/nancy/resource.h b/engines/nancy/resource.h
index 48e4df6146..f8531fee1c 100644
--- a/engines/nancy/resource.h
+++ b/engines/nancy/resource.h
@@ -24,7 +24,6 @@
 #define NANCY_RESOURCE_H
 
 namespace Common {
-class MemoryReadStream;
 class String;
 }
 


Commit: d245afd84325ee786d3645ffc242db4d6ae720ad
    https://github.com/scummvm/scummvm/commit/d245afd84325ee786d3645ffc242db4d6ae720ad
Author: Walter van Niftrik (walter at scummvm.org)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add partial support for CifTree 2.1

Changed paths:
    engines/nancy/resource.cpp


diff --git a/engines/nancy/resource.cpp b/engines/nancy/resource.cpp
index ee090f289f..b5bca416fb 100644
--- a/engines/nancy/resource.cpp
+++ b/engines/nancy/resource.cpp
@@ -67,6 +67,7 @@ bool CifTree::initialize(Common::File &f) {
 
 	for (int i = 0; i < infoBlockCount; i++) {
 		CifInfoChain chain;
+		memset(&chain, 0, sizeof(CifInfoChain));
 		readCifInfo(f, chain);
 		_cifInfo.push_back(chain);
 	}
@@ -116,8 +117,7 @@ uint CifTree20::readHeader(Common::File &f) {
 	return infoBlockCount;
 }
 
-void CifTree20::readCifInfo(Common::File &f, CifInfoChain &chain)
-{
+void CifTree20::readCifInfo(Common::File &f, CifInfoChain &chain) {
 	ResourceManager::CifInfo &info = chain.info;
 
 	char name[9];
@@ -144,6 +144,52 @@ void CifTree20::readCifInfo(Common::File &f, CifInfoChain &chain)
 		error("Failed to read info block from CifTree");
 }
 
+class CifTree21 : public CifTree {
+protected:
+	virtual uint readHeader(Common::File &f);
+	virtual void readCifInfo(Common::File &f, CifInfoChain &chain);
+};
+
+uint CifTree21::readHeader(Common::File &f) {
+	uint infoBlockCount = f.readUint16LE();
+
+	if (f.eos())
+		error("Failed to read cif info block count from CifTree");
+
+	f.readByte(); // Unknown
+	f.readByte(); // Unknown
+
+	return infoBlockCount;
+}
+
+void CifTree21::readCifInfo(Common::File &f, CifInfoChain &chain) {
+	ResourceManager::CifInfo &info = chain.info;
+
+	char name[9];
+	f.read(name, 9);
+	name[8] = 0;
+	info.name = name;
+
+	f.skip(2); // Index of this block
+
+	f.skip(32);
+
+	info.width = f.readUint16LE();
+	info.pitch = f.readUint16LE();
+	info.height = f.readUint16LE();
+	info.depth = f.readByte();
+
+	info.comp = f.readByte();
+	info.dataOffset = f.readUint32LE();
+	info.size = f.readUint32LE();
+	info.sizeUnk = f.readUint32LE(); // Unknown
+	info.compressedSize = f.readUint32LE();
+
+	info.type = f.readByte();
+
+	chain.next = f.readUint16LE();
+}
+
 ResourceManager::ResourceManager(NancyEngine *vm) : _vm(vm) {
 }
 
@@ -175,6 +221,9 @@ void ResourceManager::initialize() {
 	case 0x00020000:
 		_cifTree = new CifTree20;
 		break;
+	case 0x00020001:
+		_cifTree = new CifTree21;
+		break;
 	default:
 		error("CifTree version %d.%d not supported", ver >> 16, ver & 0xffff);
 	}


Commit: ba12d0498688ca2be5b14e5ca68ba2ae9139e0b2
    https://github.com/scummvm/scummvm/commit/ba12d0498688ca2be5b14e5ca68ba2ae9139e0b2
Author: Walter van Niftrik (walter at scummvm.org)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add heuristic for long filenames in CifTree

Changed paths:
    engines/nancy/console.cpp
    engines/nancy/resource.cpp


diff --git a/engines/nancy/console.cpp b/engines/nancy/console.cpp
index 424ec2fb1c..1fcdb32f4f 100644
--- a/engines/nancy/console.cpp
+++ b/engines/nancy/console.cpp
@@ -67,8 +67,8 @@ bool NancyConsole::Cmd_resList(int argc, const char **argv) {
 	Common::Array<Common::String> list;
 	_vm->_res->listResources(list, atoi(argv[1]));
 	for (uint i = 0; i < list.size(); i++) {
-		debugPrintf("%-10s", list[i].c_str());
-		if ((i % 8) == 7 && i + 1 != list.size())
+		debugPrintf("%-38s", list[i].c_str());
+		if ((i % 2) == 1 && i + 1 != list.size())
 			debugPrintf("\n");
 	}
 
diff --git a/engines/nancy/resource.cpp b/engines/nancy/resource.cpp
index b5bca416fb..ca94a8b713 100644
--- a/engines/nancy/resource.cpp
+++ b/engines/nancy/resource.cpp
@@ -145,9 +145,16 @@ void CifTree20::readCifInfo(Common::File &f, CifInfoChain &chain) {
 }
 
 class CifTree21 : public CifTree {
+public:
+	CifTree21() : _hasLongNames(false) { };
+
 protected:
 	virtual uint readHeader(Common::File &f);
 	virtual void readCifInfo(Common::File &f, CifInfoChain &chain);
+
+private:
+	void testLongNames(Common::File &f);
+	bool _hasLongNames;
 };
 
 uint CifTree21::readHeader(Common::File &f) {
@@ -159,15 +166,21 @@ uint CifTree21::readHeader(Common::File &f) {
 	f.readByte(); // Unknown
 	f.readByte(); // Unknown
 
+	testLongNames(f);
+
 	return infoBlockCount;
 }
 
 void CifTree21::readCifInfo(Common::File &f, CifInfoChain &chain) {
 	ResourceManager::CifInfo &info = chain.info;
+	int nameSize = 8;
 
-	char name[9];
-	f.read(name, 9);
-	name[8] = 0;
+	if (_hasLongNames)
+		nameSize = 32;
+
+	char name[33];
+	f.read(name, nameSize + 1);
+	name[nameSize] = 0;
 	info.name = name;
 
 	f.skip(2); // Index of this block
@@ -190,6 +203,20 @@ void CifTree21::readCifInfo(Common::File &f, CifInfoChain &chain) {
 	chain.next = f.readUint16LE();
 }
 
+void CifTree21::testLongNames(Common::File &f) {
+	// This is a heuristic for the switch to long filenames during the 2.1 version
+	uint pos = f.pos();
+
+	f.seek(2159);
+	uint16 index1 = f.readUint16LE();
+
+	f.seek(68, SEEK_CUR);
+	uint16 index2 = f.readUint16LE();
+
+	f.seek(pos);
+	_hasLongNames = !(index1 == 1 && index2 == 2);
+}
+
 ResourceManager::ResourceManager(NancyEngine *vm) : _vm(vm) {
 }
 


Commit: 53c1548fdfa229d7f6a5be02786ba3585d7f3d49
    https://github.com/scummvm/scummvm/commit/53c1548fdfa229d7f6a5be02786ba3585d7f3d49
Author: Walter van Niftrik (walter at scummvm.org)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add signature for Nancy Drew 5

Changed paths:
    engines/nancy/detection.cpp
    engines/nancy/nancy.cpp


diff --git a/engines/nancy/detection.cpp b/engines/nancy/detection.cpp
index 3b096e4470..947904659e 100644
--- a/engines/nancy/detection.cpp
+++ b/engines/nancy/detection.cpp
@@ -47,6 +47,7 @@ const char *NancyEngine::getGameId() const {
 
 const char *const directoryGlobs[] = {
 	"game",
+	"datafiles",
 	0
 };
 
@@ -54,6 +55,7 @@ static const PlainGameDescriptor nancyGames[] = {
 	// Games
 	{"nancy1", "Nancy Drew 1: Secrets Can Kill"},
 	{"nancy2", "Nancy Drew 2: Stay Tuned For Danger"},
+	{"nancy5", "Nancy Drew 5: The Final Scene"},
 	{0, 0}
 };
 
@@ -87,6 +89,20 @@ static const NancyGameDescription gameDescriptions[] = {
 		},
 		kGameTypeNancy1
 	},
+	{
+		{
+			"nancy5", 0,
+			{
+				{"ciftree.dat", 0, "21fa81f322595c3100d8d58d100852d5", 8187692},
+				AD_LISTEND
+			},
+			Common::EN_ANY,
+			Common::kPlatformWindows,
+			ADGF_NO_FLAGS,
+			GUIO0()
+		},
+		kGameTypeNancy1
+	},
 
 	{AD_TABLE_END_MARKER, kGameTypeNone}
 };
diff --git a/engines/nancy/nancy.cpp b/engines/nancy/nancy.cpp
index 545f91418b..f2e089a867 100644
--- a/engines/nancy/nancy.cpp
+++ b/engines/nancy/nancy.cpp
@@ -43,6 +43,7 @@ NancyEngine::NancyEngine(OSystem *syst, const NancyGameDescription *gd) : Engine
 
 	const Common::FSNode gameDataDir(ConfMan.get("path"));
 	SearchMan.addSubDirectoryMatching(gameDataDir, "game");
+	SearchMan.addSubDirectoryMatching(gameDataDir, "datafiles");
 
 	DebugMan.addDebugChannel(kDebugSchedule, "Schedule", "Script Schedule debug level");
 	DebugMan.addDebugChannel(kDebugEngine, "Engine", "Engine debug level");


Commit: dbb64b77e16ec7637e3b6307cacfd7b9add9df4c
    https://github.com/scummvm/scummvm/commit/dbb64b77e16ec7637e3b6307cacfd7b9add9df4c
Author: Walter van Niftrik (walter at scummvm.org)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Make CifTree case insensitive

Changed paths:
    engines/nancy/resource.cpp


diff --git a/engines/nancy/resource.cpp b/engines/nancy/resource.cpp
index ca94a8b713..cda386f7c9 100644
--- a/engines/nancy/resource.cpp
+++ b/engines/nancy/resource.cpp
@@ -83,16 +83,18 @@ void CifTree::listResources(Common::Array<Common::String> &list, uint type) {
 }
 
 bool CifTree::findResource(const Common::String name, ResourceManager::CifInfo &info) {
+	Common::String nameUpper = name;
+	nameUpper.toUppercase();
 	uint hash = 0;
 
-	for (uint i = 0; i < name.size(); i++)
-		hash += name[i];
+	for (uint i = 0; i < nameUpper.size(); i++)
+		hash += nameUpper[i];
 
 	hash &= kHashMapSize - 1;
 
 	uint16 index = _hashMap[hash];
 	while (index != 0xffff) {
-		if (name == _cifInfo[index].info.name) {
+		if (nameUpper == _cifInfo[index].info.name) {
 			info = _cifInfo[index].info;
 			return true;
 		}


Commit: e989b0e1c8746b294241ee4e3f154f2105942b09
    https://github.com/scummvm/scummvm/commit/e989b0e1c8746b294241ee4e3f154f2105942b09
Author: Walter van Niftrik (walter at scummvm.org)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add debug commands for dumping a resource and loading a new CifTree

Changed paths:
    engines/nancy/console.cpp
    engines/nancy/console.h
    engines/nancy/resource.cpp
    engines/nancy/resource.h


diff --git a/engines/nancy/console.cpp b/engines/nancy/console.cpp
index 1fcdb32f4f..e1d2915356 100644
--- a/engines/nancy/console.cpp
+++ b/engines/nancy/console.cpp
@@ -29,7 +29,9 @@
 namespace Nancy {
 
 NancyConsole::NancyConsole(NancyEngine *vm) : GUI::Debugger(), _vm(vm) {
+	registerCmd("res_load_ciftree", WRAP_METHOD(NancyConsole, Cmd_resLoadCifTree));
 	registerCmd("res_hexdump", WRAP_METHOD(NancyConsole, Cmd_resHexDump));
+	registerCmd("res_diskdump", WRAP_METHOD(NancyConsole, Cmd_resDiskDump));
 	registerCmd("res_list", WRAP_METHOD(NancyConsole, Cmd_resList));
 	registerCmd("res_info", WRAP_METHOD(NancyConsole, Cmd_resInfo));
 	registerCmd("res_show_image", WRAP_METHOD(NancyConsole, Cmd_resShowImage));
@@ -48,11 +50,43 @@ bool NancyConsole::Cmd_resHexDump(int argc, const char **argv) {
 	uint size;
 	byte *buf = _vm->_res->loadResource(argv[1], size);
 	if (!buf) {
-		debugPrintf("Failed to load resource '%s'", argv[1]);
+		debugPrintf("Failed to load resource '%s'\n", argv[1]);
 		return true;
 	}
 
 	Common::hexdump(buf, size);
+	delete[] buf;
+	return true;
+}
+
+bool NancyConsole::Cmd_resDiskDump(int argc, const char **argv) {
+	if (argc != 2) {
+		debugPrintf("Dumps the specified resource to disk\n");
+		debugPrintf("Usage: %s <resource name>\n", argv[0]);
+		return true;
+	}
+
+	uint size;
+	byte *buf = _vm->_res->loadResource(argv[1], size);
+	if (!buf) {
+		debugPrintf("Failed to load resource '%s'\n", argv[1]);
+		return true;
+	}
+
+	Common::String filename = argv[1];
+	filename += ".raw";
+	Common::DumpFile dump;
+	if (!dump.open(filename)) {
+		debugPrintf("Failed to open dump file '%s'\n", filename.c_str());
+		delete[] buf;
+		return true;
+	}
+
+	if (dump.write(buf, size) < size)
+		debugPrintf("Failed to write dump file '%s'\n", filename.c_str());
+
+	dump.close();
+	delete[] buf;
 	return true;
 }
 
@@ -105,4 +139,16 @@ bool NancyConsole::Cmd_resShowImage(int argc, const char **argv) {
 	return true;
 }
 
+bool NancyConsole::Cmd_resLoadCifTree(int argc, const char **argv) {
+	if (argc != 2) {
+		debugPrintf("Loads a new CifTree file\n");
+		debugPrintf("Usage: %s <filename>\n", argv[0]);
+		return true;
+	}
+
+	if (!_vm->_res->loadCifTree(argv[1]))
+		debugPrintf("Failed to load CifTree '%s'\n", argv[1]);
+	return true;
+}
+
 } // End of namespace Nancy
diff --git a/engines/nancy/console.h b/engines/nancy/console.h
index 0ceea708e5..13cdb7788d 100644
--- a/engines/nancy/console.h
+++ b/engines/nancy/console.h
@@ -36,7 +36,9 @@ public:
 
 private:
 	NancyEngine *_vm;
+	bool Cmd_resLoadCifTree(int argc, const char **argv);
 	bool Cmd_resHexDump(int argc, const char **argv);
+	bool Cmd_resDiskDump(int argc, const char **argv);
 	bool Cmd_resList(int argc, const char **argv);
 	bool Cmd_resInfo(int argc, const char **argv);
 	bool Cmd_resShowImage(int argc, const char **argv);
diff --git a/engines/nancy/resource.cpp b/engines/nancy/resource.cpp
index cda386f7c9..2801cbb0be 100644
--- a/engines/nancy/resource.cpp
+++ b/engines/nancy/resource.cpp
@@ -35,6 +35,7 @@ public:
 	bool initialize(Common::File &f);
 	bool findResource(const Common::String name, ResourceManager::CifInfo &info);
 	void listResources(Common::Array<Common::String> &list, uint type);
+	const Common::String &getName() { return _filename; }
 
 protected:
 	enum {
@@ -219,24 +220,32 @@ void CifTree21::testLongNames(Common::File &f) {
 	_hasLongNames = !(index1 == 1 && index2 == 2);
 }
 
-ResourceManager::ResourceManager(NancyEngine *vm) : _vm(vm) {
+ResourceManager::ResourceManager(NancyEngine *vm) : _vm(vm), _cifTree(0) {
 }
 
 ResourceManager::~ResourceManager() {
 }
 
-void ResourceManager::initialize() {
+bool ResourceManager::loadCifTree(const Common::String filename) {
+	// NOTE: It seems likely that multiple CifTrees can be open at the same time
+	// For now, we just replace the current CifTree with the new one
+
 	Common::File f;
+	CifTree *cifTree;
 
-	if (!f.open("ciftree.dat"))
-		error("Failed to open ciftree.dat");
+	if (!f.open(filename)) {
+		warning("Failed to open CifTree '%s'", filename.c_str());
+		return false;
+	}
 
 	char id[20];
 	f.read(id, 20);
 	id[19] = 0;
 
-	if (f.eos() || Common::String(id) != "CIF TREE WayneSikes")
-		error("Invalid id string found in ciftree.dat");
+	if (f.eos() || Common::String(id) != "CIF TREE WayneSikes") {
+		warning("Invalid id string found in CifTree '%s'", filename.c_str());
+		return false;
+	}
 
 	// 4 bytes unused
 	f.skip(4);
@@ -248,19 +257,35 @@ void ResourceManager::initialize() {
 
 	switch(ver) {
 	case 0x00020000:
-		_cifTree = new CifTree20;
+		cifTree = new CifTree20;
 		break;
 	case 0x00020001:
-		_cifTree = new CifTree21;
+		cifTree = new CifTree21;
 		break;
 	default:
-		error("CifTree version %d.%d not supported", ver >> 16, ver & 0xffff);
+		warning("Unsupported version %d.%d found in CifTree '%s'", ver >> 16, ver & 0xffff, filename.c_str());
+		return false;
 	}
 
-	if (!_cifTree->initialize(f))
-		error("Failed to read CifTree 'ciftree.dat'");
+	if (!cifTree->initialize(f)) {
+		warning("Failed to read CifTree '%s'", filename.c_str());
+		delete cifTree;
+		return false;
+	}
 
 	f.close();
+
+	// Delete previous tree
+	if (_cifTree)
+		delete _cifTree;
+
+	_cifTree = cifTree;
+	return true;
+}
+
+void ResourceManager::initialize() {
+	if (!loadCifTree("ciftree.dat"))
+		error("Failed to read 'ciftree.dat'");
 }
 
 byte *ResourceManager::loadResource(const Common::String name, uint &size) {
@@ -296,6 +321,11 @@ bool ResourceManager::loadImage(const Common::String name, Graphics::Surface &su
 	uint size;
 	byte *buf = decompress(info, size);
 
+	if (!buf) {
+		warning("Failed to decompress image '%s'", name.c_str());
+		return false;
+	}
+
 	Graphics::PixelFormat format(2, 5, 5, 5, 0, 10, 5, 0, 0);
 	surf.w = info.width;
 	surf.h = info.height;
@@ -315,8 +345,10 @@ byte *ResourceManager::decompress(const CifInfo &info, uint &size) {
 		return 0;
 	}
 
-	if (!cifTree.open("ciftree.dat"))
-		error("Failed to open ciftree.dat");
+	if (!cifTree.open(_cifTree->getName())) {
+		warning("Failed to open '%s'", _cifTree->getName().c_str());
+		return 0;
+	}
 
 	cifTree.seek(info.dataOffset);
 
@@ -383,6 +415,7 @@ byte *ResourceManager::decompress(const CifInfo &info, uint &size) {
 
 	if (read != info.compressedSize || written != info.size) {
 		warning("Failed to decompress resource");
+		delete[] output;
 		return 0;
 	}
 
diff --git a/engines/nancy/resource.h b/engines/nancy/resource.h
index f8531fee1c..25cd1b78b0 100644
--- a/engines/nancy/resource.h
+++ b/engines/nancy/resource.h
@@ -63,6 +63,7 @@ public:
 	~ResourceManager();
 
 	void initialize();
+	bool loadCifTree(const Common::String filename);
 	byte *loadResource(const Common::String name, uint &size);
 	bool loadImage(const Common::String name, Graphics::Surface &surf);
 


Commit: 164778ba12794465d11f809c7ce195d2af649a13
    https://github.com/scummvm/scummvm/commit/164778ba12794465d11f809c7ce195d2af649a13
Author: Walter van Niftrik (walter at scummvm.org)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Refactor resource manager

Changed paths:
    engines/nancy/resource.cpp
    engines/nancy/resource.h


diff --git a/engines/nancy/resource.cpp b/engines/nancy/resource.cpp
index 2801cbb0be..42dd5126f2 100644
--- a/engines/nancy/resource.cpp
+++ b/engines/nancy/resource.cpp
@@ -29,13 +29,37 @@
 
 namespace Nancy {
 
+static void readCifInfo20(Common::File &f, ResourceManager::CifInfo &info, bool hasOffset = false) {
+	info.width = f.readUint16LE();
+	info.pitch = f.readUint16LE();
+	info.height = f.readUint16LE();
+	info.depth = f.readByte();
+
+	info.comp = f.readByte();
+	if (hasOffset)
+		info.dataOffset = f.readUint32LE();
+	info.size = f.readUint32LE();
+	info.sizeUnk = f.readUint32LE(); // Unknown
+	info.compressedSize = f.readUint32LE();
+
+	info.type = f.readByte();
+}
+
+static void readCifInfo21(Common::File &f, ResourceManager::CifInfo &info, bool hasOffset = false) {
+	f.skip(32); // TODO
+
+	readCifInfo20(f, info, hasOffset);
+}
+
 class CifTree {
 public:
 	virtual ~CifTree() { };
 	bool initialize(Common::File &f);
-	bool findResource(const Common::String name, ResourceManager::CifInfo &info);
-	void listResources(Common::Array<Common::String> &list, uint type);
-	const Common::String &getName() { return _filename; }
+	bool findResource(const Common::String &name, ResourceManager::CifInfo &info) const;
+	void listResources(Common::Array<Common::String> &list, uint type) const;
+	const Common::String &getName() const { return _filename; }
+
+	static const CifTree *load(const Common::String filename);
 
 protected:
 	enum {
@@ -76,14 +100,14 @@ bool CifTree::initialize(Common::File &f) {
 	return true;
 }
 
-void CifTree::listResources(Common::Array<Common::String> &list, uint type) {
+void CifTree::listResources(Common::Array<Common::String> &list, uint type) const {
 	for (uint i = 0; i < _cifInfo.size(); i++) {
 		if (type == ResourceManager::kResTypeAny || _cifInfo[i].info.type == type)
 			list.push_back(_cifInfo[i].info.name);
 	}
 }
 
-bool CifTree::findResource(const Common::String name, ResourceManager::CifInfo &info) {
+bool CifTree::findResource(const Common::String &name, ResourceManager::CifInfo &info) const {
 	Common::String nameUpper = name;
 	nameUpper.toUppercase();
 	uint hash = 0;
@@ -128,19 +152,7 @@ void CifTree20::readCifInfo(Common::File &f, CifInfoChain &chain) {
 	name[8] = 0;
 	info.name = name;
 
-	f.skip(2); // Index of this block
-	info.width = f.readUint16LE();
-	info.pitch = f.readUint16LE();
-	info.height = f.readUint16LE();
-	info.depth = f.readByte();
-
-	info.comp = f.readByte();
-	info.dataOffset = f.readUint32LE();
-	info.size = f.readUint32LE();
-	info.sizeUnk = f.readUint32LE(); // Unknown
-	info.compressedSize = f.readUint32LE();
-
-	info.type = f.readByte();
+	readCifInfo20(f, info, true);
 
 	chain.next = f.readUint16LE();
 	if (f.eos())
@@ -188,20 +200,7 @@ void CifTree21::readCifInfo(Common::File &f, CifInfoChain &chain) {
 
 	f.skip(2); // Index of this block
 
-	f.skip(32);
-
-	info.width = f.readUint16LE();
-	info.pitch = f.readUint16LE();
-	info.height = f.readUint16LE();
-	info.depth = f.readByte();
-
-	info.comp = f.readByte();
-	info.dataOffset = f.readUint32LE();
-	info.size = f.readUint32LE();
-	info.sizeUnk = f.readUint32LE(); // Unknown
-	info.compressedSize = f.readUint32LE();
-
-	info.type = f.readByte();
+	readCifInfo21(f, info, true);
 
 	chain.next = f.readUint16LE();
 }
@@ -220,22 +219,13 @@ void CifTree21::testLongNames(Common::File &f) {
 	_hasLongNames = !(index1 == 1 && index2 == 2);
 }
 
-ResourceManager::ResourceManager(NancyEngine *vm) : _vm(vm), _cifTree(0) {
-}
-
-ResourceManager::~ResourceManager() {
-}
-
-bool ResourceManager::loadCifTree(const Common::String filename) {
-	// NOTE: It seems likely that multiple CifTrees can be open at the same time
-	// For now, we just replace the current CifTree with the new one
-
+const CifTree *CifTree::load(const Common::String filename) {
 	Common::File f;
-	CifTree *cifTree;
+	CifTree *cifTree = 0;
 
 	if (!f.open(filename)) {
 		warning("Failed to open CifTree '%s'", filename.c_str());
-		return false;
+		return 0;
 	}
 
 	char id[20];
@@ -244,7 +234,8 @@ bool ResourceManager::loadCifTree(const Common::String filename) {
 
 	if (f.eos() || Common::String(id) != "CIF TREE WayneSikes") {
 		warning("Invalid id string found in CifTree '%s'", filename.c_str());
-		return false;
+		f.close();
+		return 0;
 	}
 
 	// 4 bytes unused
@@ -264,16 +255,32 @@ bool ResourceManager::loadCifTree(const Common::String filename) {
 		break;
 	default:
 		warning("Unsupported version %d.%d found in CifTree '%s'", ver >> 16, ver & 0xffff, filename.c_str());
-		return false;
 	}
 
-	if (!cifTree->initialize(f)) {
+	if (cifTree && !cifTree->initialize(f)) {
 		warning("Failed to read CifTree '%s'", filename.c_str());
 		delete cifTree;
-		return false;
+		cifTree = 0;
 	}
 
 	f.close();
+	return cifTree;
+}
+
+ResourceManager::ResourceManager(NancyEngine *vm) : _vm(vm), _cifTree(0) {
+}
+
+ResourceManager::~ResourceManager() {
+}
+
+bool ResourceManager::loadCifTree(const Common::String filename) {
+	// NOTE: It seems likely that multiple CifTrees can be open at the same time
+	// For now, we just replace the current CifTree with the new one
+
+	const CifTree *cifTree = CifTree::load(filename);
+
+	if (!cifTree)
+		return false;
 
 	// Delete previous tree
 	if (_cifTree)
@@ -305,17 +312,17 @@ bool ResourceManager::loadImage(const Common::String name, Graphics::Surface &su
 
 	if (!_cifTree->findResource(name, info)) {
 		warning("Resource '%s' not found", name.c_str());
-		return 0;
+		return false;
 	}
 
 	if (info.type != kResTypeImage) {
 		warning("Resource '%s' is not an image", name.c_str());
-		return 0;
+		return false;
 	}
 
 	if (info.depth != 16) {
 		warning("Image '%s' has unsupported depth %i", name.c_str(), info.depth);
-		return 0;
+		return false;
 	}
 
 	uint size;
diff --git a/engines/nancy/resource.h b/engines/nancy/resource.h
index 25cd1b78b0..542577672f 100644
--- a/engines/nancy/resource.h
+++ b/engines/nancy/resource.h
@@ -72,7 +72,7 @@ public:
 	Common::String getResourceDesc(const Common::String name);
 private:
 	NancyEngine *_vm;
-	CifTree *_cifTree;
+	const CifTree *_cifTree;
 
 	byte *decompress(const CifInfo &res, uint &size);
 };


Commit: debe67fa1ea0e5722728e266798d3150eb5b4a86
    https://github.com/scummvm/scummvm/commit/debe67fa1ea0e5722728e266798d3150eb5b4a86
Author: Strangerke (strangerke at scummvm.org)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add detection for Nancy 6

Changed paths:
    engines/nancy/detection.cpp


diff --git a/engines/nancy/detection.cpp b/engines/nancy/detection.cpp
index 947904659e..993e4a65c3 100644
--- a/engines/nancy/detection.cpp
+++ b/engines/nancy/detection.cpp
@@ -56,12 +56,13 @@ static const PlainGameDescriptor nancyGames[] = {
 	{"nancy1", "Nancy Drew 1: Secrets Can Kill"},
 	{"nancy2", "Nancy Drew 2: Stay Tuned For Danger"},
 	{"nancy5", "Nancy Drew 5: The Final Scene"},
+	{"nancy6", "Nancy Drew 6: Secret of the Scarlet Hand"},
 	{0, 0}
 };
 
 static const NancyGameDescription gameDescriptions[] = {
 
-	{
+	{ // MD5 by waltervn
 		{
 			"nancy1", 0,
 			{
@@ -75,7 +76,7 @@ static const NancyGameDescription gameDescriptions[] = {
 		},
 		kGameTypeNancy1
 	},
-	{
+	{ // MD5 by waltervn
 		{
 			"nancy2", 0,
 			{
@@ -89,7 +90,7 @@ static const NancyGameDescription gameDescriptions[] = {
 		},
 		kGameTypeNancy1
 	},
-	{
+	{ // MD5 by waltervn
 		{
 			"nancy5", 0,
 			{
@@ -103,7 +104,20 @@ static const NancyGameDescription gameDescriptions[] = {
 		},
 		kGameTypeNancy1
 	},
-
+	{ // MD5 by Strangerke
+		{
+			"nancy6", 0,
+			{
+				{"ciftree.dat", 0, "a97b848651fdcf38f5cad7092d98e4a1", 28888006},
+				AD_LISTEND
+			},
+			Common::EN_ANY,
+			Common::kPlatformWindows,
+			ADGF_NO_FLAGS,
+			GUIO0()
+		},
+		kGameTypeNancy1
+	},
 	{AD_TABLE_END_MARKER, kGameTypeNone}
 };
 


Commit: 9720ba812acee4d0c1ca449e166cfb9d8ab9f4e6
    https://github.com/scummvm/scummvm/commit/9720ba812acee4d0c1ca449e166cfb9d8ab9f4e6
Author: Walter van Niftrik (walter at scummvm.org)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add support for Nancy 6 CifTree format

Changed paths:
    engines/nancy/detection.cpp
    engines/nancy/resource.cpp


diff --git a/engines/nancy/detection.cpp b/engines/nancy/detection.cpp
index 993e4a65c3..c98c692753 100644
--- a/engines/nancy/detection.cpp
+++ b/engines/nancy/detection.cpp
@@ -54,7 +54,7 @@ const char *const directoryGlobs[] = {
 static const PlainGameDescriptor nancyGames[] = {
 	// Games
 	{"nancy1", "Nancy Drew 1: Secrets Can Kill"},
-	{"nancy2", "Nancy Drew 2: Stay Tuned For Danger"},
+	{"nancy2", "Nancy Drew 2: Stay Tuned for Danger"},
 	{"nancy5", "Nancy Drew 5: The Final Scene"},
 	{"nancy6", "Nancy Drew 6: Secret of the Scarlet Hand"},
 	{0, 0}
diff --git a/engines/nancy/resource.cpp b/engines/nancy/resource.cpp
index 42dd5126f2..9acdc02268 100644
--- a/engines/nancy/resource.cpp
+++ b/engines/nancy/resource.cpp
@@ -45,12 +45,6 @@ static void readCifInfo20(Common::File &f, ResourceManager::CifInfo &info, bool
 	info.type = f.readByte();
 }
 
-static void readCifInfo21(Common::File &f, ResourceManager::CifInfo &info, bool hasOffset = false) {
-	f.skip(32); // TODO
-
-	readCifInfo20(f, info, hasOffset);
-}
-
 class CifTree {
 public:
 	virtual ~CifTree() { };
@@ -161,15 +155,16 @@ void CifTree20::readCifInfo(Common::File &f, CifInfoChain &chain) {
 
 class CifTree21 : public CifTree {
 public:
-	CifTree21() : _hasLongNames(false) { };
+	CifTree21() : _hasLongNames(false), _hasOffsetFirst(false) { };
 
 protected:
 	virtual uint readHeader(Common::File &f);
 	virtual void readCifInfo(Common::File &f, CifInfoChain &chain);
 
 private:
-	void testLongNames(Common::File &f);
+	void determineSubtype(Common::File &f);
 	bool _hasLongNames;
+	bool _hasOffsetFirst;
 };
 
 uint CifTree21::readHeader(Common::File &f) {
@@ -181,7 +176,7 @@ uint CifTree21::readHeader(Common::File &f) {
 	f.readByte(); // Unknown
 	f.readByte(); // Unknown
 
-	testLongNames(f);
+	determineSubtype(f);
 
 	return infoBlockCount;
 }
@@ -200,13 +195,22 @@ void CifTree21::readCifInfo(Common::File &f, CifInfoChain &chain) {
 
 	f.skip(2); // Index of this block
 
-	readCifInfo21(f, info, true);
+	if (_hasOffsetFirst) {
+		info.dataOffset = f.readUint32LE();
+		chain.next = f.readUint16LE();
+	}
+
+	f.skip(32); // TODO
 
-	chain.next = f.readUint16LE();
+	readCifInfo20(f, info, !_hasOffsetFirst);
+
+	if (!_hasOffsetFirst)
+		chain.next = f.readUint16LE();
 }
 
-void CifTree21::testLongNames(Common::File &f) {
-	// This is a heuristic for the switch to long filenames during the 2.1 version
+void CifTree21::determineSubtype(Common::File &f) {
+	// Perform heuristic for long filenames
+	// Assume short file names and read indices 1 and 2
 	uint pos = f.pos();
 
 	f.seek(2159);
@@ -215,8 +219,22 @@ void CifTree21::testLongNames(Common::File &f) {
 	f.seek(68, SEEK_CUR);
 	uint16 index2 = f.readUint16LE();
 
+	// If they don't match, this file must have long filenames
+	if (index1 != 1 || index2 != 2)
+		_hasLongNames = true;
+
+	if (_hasLongNames) {
+		// Perform heuristic for offset at the beginning of the block
+		// Read offset and next of the first info block
+		// If either of these is zero, offset can't be first
+		f.seek(2115);
+		uint32 offset = f.readUint32LE();
+		uint16 next = f.readUint32LE();
+		if (offset && next)
+			_hasOffsetFirst = true;
+	}
+
 	f.seek(pos);
-	_hasLongNames = !(index1 == 1 && index2 == 2);
 }
 
 const CifTree *CifTree::load(const Common::String filename) {


Commit: 66c3efe9330d8ce051d6e239523383465f4b4efb
    https://github.com/scummvm/scummvm/commit/66c3efe9330d8ce051d6e239523383465f4b4efb
Author: Walter van Niftrik (walter at scummvm.org)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add signatures for Nancy Drew 3 and 4

Changed paths:
    engines/nancy/detection.cpp


diff --git a/engines/nancy/detection.cpp b/engines/nancy/detection.cpp
index c98c692753..2cc161f080 100644
--- a/engines/nancy/detection.cpp
+++ b/engines/nancy/detection.cpp
@@ -55,6 +55,8 @@ static const PlainGameDescriptor nancyGames[] = {
 	// Games
 	{"nancy1", "Nancy Drew 1: Secrets Can Kill"},
 	{"nancy2", "Nancy Drew 2: Stay Tuned for Danger"},
+	{"nancy3", "Nancy Drew 3: Message in a Haunted Mansion"},
+	{"nancy4", "Nancy Drew 4: Treasure in the Royal Tower"},
 	{"nancy5", "Nancy Drew 5: The Final Scene"},
 	{"nancy6", "Nancy Drew 6: Secret of the Scarlet Hand"},
 	{0, 0}
@@ -90,6 +92,34 @@ static const NancyGameDescription gameDescriptions[] = {
 		},
 		kGameTypeNancy1
 	},
+	{ // MD5 by waltervn
+		{
+			"nancy3", 0,
+			{
+				{"ciftree.dat", 0, "ee5f8832226567c3610556497c451b09", 16256355},
+				AD_LISTEND
+			},
+			Common::EN_ANY,
+			Common::kPlatformWindows,
+			ADGF_NO_FLAGS,
+			GUIO0()
+		},
+		kGameTypeNancy1
+	},
+	{ // MD5 by waltervn
+		{
+			"nancy4", 0,
+			{
+				{"ciftree.dat", 0, "e9d45f7db453b0d8f37d202fc979537c", 8742289},
+				AD_LISTEND
+			},
+			Common::EN_ANY,
+			Common::kPlatformWindows,
+			ADGF_NO_FLAGS,
+			GUIO0()
+		},
+		kGameTypeNancy1
+	},
 	{ // MD5 by waltervn
 		{
 			"nancy5", 0,


Commit: 48c60a01af5ad687105a5d08df12381a86d4531e
    https://github.com/scummvm/scummvm/commit/48c60a01af5ad687105a5d08df12381a86d4531e
Author: Walter van Niftrik (walter at scummvm.org)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Fix regression in CifTree 2.0 handling

Changed paths:
    engines/nancy/resource.cpp


diff --git a/engines/nancy/resource.cpp b/engines/nancy/resource.cpp
index 9acdc02268..5cff0e4a97 100644
--- a/engines/nancy/resource.cpp
+++ b/engines/nancy/resource.cpp
@@ -146,6 +146,8 @@ void CifTree20::readCifInfo(Common::File &f, CifInfoChain &chain) {
 	name[8] = 0;
 	info.name = name;
 
+	f.skip(2); // Index of this block
+
 	readCifInfo20(f, info, true);
 
 	chain.next = f.readUint16LE();


Commit: aa1f0bd1ac5a7644fcff32c68a93a4386f293909
    https://github.com/scummvm/scummvm/commit/aa1f0bd1ac5a7644fcff32c68a93a4386f293909
Author: Walter van Niftrik (walter at scummvm.org)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Cleanup

Changed paths:
    engines/nancy/resource.cpp
    engines/nancy/resource.h


diff --git a/engines/nancy/resource.cpp b/engines/nancy/resource.cpp
index 5cff0e4a97..1403e70a7a 100644
--- a/engines/nancy/resource.cpp
+++ b/engines/nancy/resource.cpp
@@ -39,7 +39,7 @@ static void readCifInfo20(Common::File &f, ResourceManager::CifInfo &info, bool
 	if (hasOffset)
 		info.dataOffset = f.readUint32LE();
 	info.size = f.readUint32LE();
-	info.sizeUnk = f.readUint32LE(); // Unknown
+	f.skip(4); // A 2nd size for obsolete Cif type 1
 	info.compressedSize = f.readUint32LE();
 
 	info.type = f.readByte();
@@ -466,7 +466,6 @@ Common::String ResourceManager::getResourceDesc(const Common::String name) {
 	desc += Common::String::format("Compression: %i\n", info.comp);
 	desc += Common::String::format("Data offset: %i\n", info.dataOffset);
 	desc += Common::String::format("Size: %i\n", info.size);
-	desc += Common::String::format("Size (unknown): %i\n", info.sizeUnk);
 	desc += Common::String::format("Compressed size: %i\n", info.compressedSize);
 	desc += Common::String::format("Width: %i\n", info.width);
 	desc += Common::String::format("Pitch: %i\n", info.pitch);
diff --git a/engines/nancy/resource.h b/engines/nancy/resource.h
index 542577672f..4c4d778db6 100644
--- a/engines/nancy/resource.h
+++ b/engines/nancy/resource.h
@@ -40,8 +40,9 @@ class ResourceManager {
 public:
 	enum ResType {
 		kResTypeAny,
+		// Type 1 seems to be obsolete
 		kResTypeImage = 2,
-		kResTypeScript
+		kResTypeData
 	};
 
 	enum ResCompression {
@@ -55,7 +56,7 @@ public:
 		byte comp; // ResCompression
 		uint16 width, pitch, height;
 		byte depth; // Bit depth
-		uint32 compressedSize, size, sizeUnk;
+		uint32 compressedSize, size;
 		uint32 dataOffset;
 	};
 


Commit: dd91f7e4f5082187f148c50570a499f31578344f
    https://github.com/scummvm/scummvm/commit/dd91f7e4f5082187f148c50570a499f31578344f
Author: Walter van Niftrik (walter at scummvm.org)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add support for multiple CifTrees loaded simultaneously

Changed paths:
    engines/nancy/console.cpp
    engines/nancy/console.h
    engines/nancy/resource.cpp
    engines/nancy/resource.h


diff --git a/engines/nancy/console.cpp b/engines/nancy/console.cpp
index e1d2915356..bb94d59351 100644
--- a/engines/nancy/console.cpp
+++ b/engines/nancy/console.cpp
@@ -29,7 +29,7 @@
 namespace Nancy {
 
 NancyConsole::NancyConsole(NancyEngine *vm) : GUI::Debugger(), _vm(vm) {
-	registerCmd("res_load_ciftree", WRAP_METHOD(NancyConsole, Cmd_resLoadCifTree));
+	registerCmd("res_load_cal", WRAP_METHOD(NancyConsole, Cmd_resLoadCal));
 	registerCmd("res_hexdump", WRAP_METHOD(NancyConsole, Cmd_resHexDump));
 	registerCmd("res_diskdump", WRAP_METHOD(NancyConsole, Cmd_resDiskDump));
 	registerCmd("res_list", WRAP_METHOD(NancyConsole, Cmd_resList));
@@ -41,14 +41,14 @@ NancyConsole::~NancyConsole() {
 }
 
 bool NancyConsole::Cmd_resHexDump(int argc, const char **argv) {
-	if (argc != 2) {
+	if (argc < 2 || argc > 3) {
 		debugPrintf("Dumps the specified resource to standard output\n");
-		debugPrintf("Usage: %s <resource name>\n", argv[0]);
+		debugPrintf("Usage: %s name [cal]\n", argv[0]);
 		return true;
 	}
 
 	uint size;
-	byte *buf = _vm->_res->loadResource(argv[1], size);
+	byte *buf = _vm->_res->loadCif((argc == 2 ? "ciftree" : argv[2]), argv[1], size);
 	if (!buf) {
 		debugPrintf("Failed to load resource '%s'\n", argv[1]);
 		return true;
@@ -60,14 +60,14 @@ bool NancyConsole::Cmd_resHexDump(int argc, const char **argv) {
 }
 
 bool NancyConsole::Cmd_resDiskDump(int argc, const char **argv) {
-	if (argc != 2) {
+	if (argc < 2 || argc > 3) {
 		debugPrintf("Dumps the specified resource to disk\n");
-		debugPrintf("Usage: %s <resource name>\n", argv[0]);
+		debugPrintf("Usage: %s name [cal]\n", argv[0]);
 		return true;
 	}
 
 	uint size;
-	byte *buf = _vm->_res->loadResource(argv[1], size);
+	byte *buf = _vm->_res->loadCif((argc == 2 ? "ciftree" : argv[2]), argv[1], size);
 	if (!buf) {
 		debugPrintf("Failed to load resource '%s'\n", argv[1]);
 		return true;
@@ -91,15 +91,15 @@ bool NancyConsole::Cmd_resDiskDump(int argc, const char **argv) {
 }
 
 bool NancyConsole::Cmd_resList(int argc, const char **argv) {
-	if (argc != 2) {
+	if (argc < 2 || argc > 3) {
 		debugPrintf("List resources of a certain type\n");
 		debugPrintf("Types - 0: all, 2: image, 3: script\n");
-		debugPrintf("Usage: %s <resource type>\n", argv[0]);
+		debugPrintf("Usage: %s type [cal]\n", argv[0]);
 		return true;
 	}
 
 	Common::Array<Common::String> list;
-	_vm->_res->listResources(list, atoi(argv[1]));
+	_vm->_res->list((argc == 2 ? "ciftree" : argv[2]), list, atoi(argv[1]));
 	for (uint i = 0; i < list.size(); i++) {
 		debugPrintf("%-38s", list[i].c_str());
 		if ((i % 2) == 1 && i + 1 != list.size())
@@ -112,25 +112,25 @@ bool NancyConsole::Cmd_resList(int argc, const char **argv) {
 }
 
 bool NancyConsole::Cmd_resInfo(int argc, const char **argv) {
-	if (argc != 2) {
+	if (argc < 2 || argc > 3) {
 		debugPrintf("Prints information about a resource\n");
-		debugPrintf("Usage: %s <resource name>\n", argv[0]);
+		debugPrintf("Usage: %s name [cal]\n", argv[0]);
 		return true;
 	}
 
-	debugPrintf("%s", _vm->_res->getResourceDesc(argv[1]).c_str());
+	debugPrintf("%s", _vm->_res->getCifDescription((argc == 2 ? "ciftree" : argv[2]), argv[1]).c_str());
 	return true;
 }
 
 bool NancyConsole::Cmd_resShowImage(int argc, const char **argv) {
-	if (argc != 2) {
+	if (argc < 2 || argc > 3) {
 		debugPrintf("Draws an image on the screen\n");
-		debugPrintf("Usage: %s <resource name>\n", argv[0]);
+		debugPrintf("Usage: %s name [cal]\n", argv[0]);
 		return true;
 	}
 
 	Graphics::Surface surf;
-	if (_vm->_res->loadImage(argv[1], surf)) {
+	if (_vm->_res->loadImage((argc == 2 ? "ciftree" : argv[2]), argv[1], surf)) {
 		_vm->_system->fillScreen(0);
 		_vm->_system->copyRectToScreen(surf.getPixels(), surf.pitch, 0, 0, surf.w > 640 ? 640 : surf.w, surf.h > 480 ? 480 : surf.h);
 		surf.free();
@@ -139,15 +139,15 @@ bool NancyConsole::Cmd_resShowImage(int argc, const char **argv) {
 	return true;
 }
 
-bool NancyConsole::Cmd_resLoadCifTree(int argc, const char **argv) {
+bool NancyConsole::Cmd_resLoadCal(int argc, const char **argv) {
 	if (argc != 2) {
-		debugPrintf("Loads a new CifTree file\n");
-		debugPrintf("Usage: %s <filename>\n", argv[0]);
+		debugPrintf("Loads a .cal file\n");
+		debugPrintf("Usage: %s <name>\n", argv[0]);
 		return true;
 	}
 
-	if (!_vm->_res->loadCifTree(argv[1]))
-		debugPrintf("Failed to load CifTree '%s'\n", argv[1]);
+	if (!_vm->_res->loadCifTree(argv[1], "cal"))
+		debugPrintf("Failed to load '%s.cal'\n", argv[1]);
 	return true;
 }
 
diff --git a/engines/nancy/console.h b/engines/nancy/console.h
index 13cdb7788d..1e87738c7f 100644
--- a/engines/nancy/console.h
+++ b/engines/nancy/console.h
@@ -36,7 +36,7 @@ public:
 
 private:
 	NancyEngine *_vm;
-	bool Cmd_resLoadCifTree(int argc, const char **argv);
+	bool Cmd_resLoadCal(int argc, const char **argv);
 	bool Cmd_resHexDump(int argc, const char **argv);
 	bool Cmd_resDiskDump(int argc, const char **argv);
 	bool Cmd_resList(int argc, const char **argv);
diff --git a/engines/nancy/resource.cpp b/engines/nancy/resource.cpp
index 1403e70a7a..6606c86a3c 100644
--- a/engines/nancy/resource.cpp
+++ b/engines/nancy/resource.cpp
@@ -23,6 +23,7 @@
 #include "common/file.h"
 #include "common/textconsole.h"
 #include "common/debug.h"
+#include "common/stream.h"
 #include "common/memstream.h"
 #include "graphics/surface.h"
 #include "nancy/resource.h"
@@ -45,15 +46,114 @@ static void readCifInfo20(Common::File &f, ResourceManager::CifInfo &info, bool
 	info.type = f.readByte();
 }
 
+class Decompressor {
+public:
+	// Decompresses data from input until the end of the stream
+	// The output stream must have the right size for the decompressed data
+	bool decompress(Common::ReadStream &input, Common::MemoryWriteStream &output);
+
+private:
+	enum {
+		kBufSize = 4096,
+		kBufStart = 4078
+	};
+
+	void init(Common::ReadStream &input, Common::WriteStream &output);
+	bool readByte(byte &b);
+	bool writeByte(byte b);
+
+	byte _buf[kBufSize];
+	uint _bufpos;
+	bool _err;
+	byte _val;
+	Common::ReadStream *_input;
+	Common::WriteStream *_output;
+};
+
+void Decompressor::init(Common::ReadStream &input, Common::WriteStream &output) {
+	memset(_buf, ' ', kBufSize);
+	_bufpos = kBufStart;
+	_err = false;
+	_val = 0;
+	_input = &input;
+	_output = &output;
+}
+
+bool Decompressor::readByte(byte &b) {
+	b = _input->readByte();
+
+	if (_input->eos())
+		return false;
+
+	if (_input->err())
+		error("Read error encountered during decompression");
+
+	b -= _val++;
+	return true;
+}
+
+bool Decompressor::writeByte(byte b) {
+	_output->writeByte(b);
+	_buf[_bufpos++] = b;
+	_bufpos &= kBufSize - 1;
+	return true;
+}
+
+bool Decompressor::decompress(Common::ReadStream &input, Common::MemoryWriteStream &output) {
+	init(input, output);
+	uint16 bits = 0;
+
+	while(1) {
+		byte b;
+
+		bits >>= 1;
+
+		// The highest 8 bits are used to keep track of how many bits are left to process
+		if (!(bits & 0x100)) {
+			// Out of bits
+			if (!readByte(b))
+				break;
+			bits = 0xff00 | b;
+		}
+
+		if (bits & 1) {
+			// Literal byte
+			if (!readByte(b))
+				break;
+			writeByte(b);
+		} else {
+			// Copy from buffer
+			byte b2;
+			if (!readByte(b) || !readByte(b2))
+				break;
+
+			uint16 offset = b | ((b2 & 0xf0) << 4);
+			uint16 len = (b2 & 0xf) + 3;
+
+			for (uint i = 0; i < len; i++)
+				writeByte(_buf[(offset + i) & (kBufSize - 1)]);
+		}
+	}
+
+	if (output.err() || output.pos() != output.size()) {
+		warning("Failed to decompress resource");
+		return false;
+	}
+
+	return true;
+}
+
 class CifTree {
 public:
+	CifTree(const Common::String &name, const Common::String &ext);
 	virtual ~CifTree() { };
-	bool initialize(Common::File &f);
-	bool findResource(const Common::String &name, ResourceManager::CifInfo &info) const;
-	void listResources(Common::Array<Common::String> &list, uint type) const;
-	const Common::String &getName() const { return _filename; }
+	bool initialize();
+	void list(Common::Array<Common::String> &nameList, uint type) const;
+	byte *getCifData(const Common::String &name, uint *size = 0) const;
+	bool getCifInfo(const Common::String &name, ResourceManager::CifInfo &info) const;
+	const Common::String &getName() const { return _name; }
 
-	static const CifTree *load(const Common::String filename);
+	static const CifTree *load(const Common::String &name, const Common::String &ext);
 
 protected:
 	enum {
@@ -70,11 +170,19 @@ protected:
 
 	uint16 _hashMap[kHashMapSize];
 	Common::Array<CifInfoChain> _cifInfo;
+	Common::String _name;
 	Common::String _filename;
 };
 
-bool CifTree::initialize(Common::File &f) {
-	_filename = f.getName();
+CifTree::CifTree(const Common::String &name, const Common::String &ext) : _name(name) {
+	_filename = name + '.' + ext;
+}
+
+bool CifTree::initialize() {
+	Common::File f;
+
+	if (!f.open(_filename) || !f.seek(28))
+		error("Failed to open CifTree '%s'", _name.c_str());
 
 	int infoBlockCount = readHeader(f);
 
@@ -82,7 +190,7 @@ bool CifTree::initialize(Common::File &f) {
 		_hashMap[i] = f.readUint16LE();
 
 	if (f.eos())
-		error("Error reading CifTree '%s'", _filename.c_str());
+		error("Error reading CifTree '%s'", _name.c_str());
 
 	for (int i = 0; i < infoBlockCount; i++) {
 		CifInfoChain chain;
@@ -91,17 +199,18 @@ bool CifTree::initialize(Common::File &f) {
 		_cifInfo.push_back(chain);
 	}
 
+	f.close();
 	return true;
 }
 
-void CifTree::listResources(Common::Array<Common::String> &list, uint type) const {
+void CifTree::list(Common::Array<Common::String> &nameList, uint type) const {
 	for (uint i = 0; i < _cifInfo.size(); i++) {
 		if (type == ResourceManager::kResTypeAny || _cifInfo[i].info.type == type)
-			list.push_back(_cifInfo[i].info.name);
+			nameList.push_back(_cifInfo[i].info.name);
 	}
 }
 
-bool CifTree::findResource(const Common::String &name, ResourceManager::CifInfo &info) const {
+bool CifTree::getCifInfo(const Common::String &name, ResourceManager::CifInfo &info) const {
 	Common::String nameUpper = name;
 	nameUpper.toUppercase();
 	uint hash = 0;
@@ -120,10 +229,41 @@ bool CifTree::findResource(const Common::String &name, ResourceManager::CifInfo
 		index = _cifInfo[index].next;
 	}
 
+	warning("Couldn't find '%s' in CifTree '%s'", name.c_str(), _name.c_str());
 	return false;
 }
 
+byte *CifTree::getCifData(const Common::String &name, uint *size) const {
+	ResourceManager::CifInfo info;
+	if (!getCifInfo(name, info))
+		return 0;
+
+	Common::File f;
+
+	if (!f.open(_filename)) {
+		warning("Failed to open CifTree '%s'", _name.c_str());
+		return 0;
+	}
+
+	uint dataSize = (info.comp == 2 ? info.compressedSize : info.size);
+	byte *buf = new byte[dataSize];
+
+	if (!f.seek(info.dataOffset) || f.read(buf, dataSize) < dataSize) {
+		warning("Failed to read data for '%s' from CifTree '%s'", name.c_str(), _name.c_str());
+		delete[] buf;
+		f.close();
+		return 0;
+	}
+
+	f.close();
+	if (size)
+		*size = dataSize;
+	return buf;
+}
+
 class CifTree20 : public CifTree {
+public:
+	CifTree20(const Common::String &name, const Common::String &ext) : CifTree(name, ext) { }
 protected:
 	virtual uint readHeader(Common::File &f);
 	virtual void readCifInfo(Common::File &f, CifInfoChain &chain);
@@ -157,7 +297,7 @@ void CifTree20::readCifInfo(Common::File &f, CifInfoChain &chain) {
 
 class CifTree21 : public CifTree {
 public:
-	CifTree21() : _hasLongNames(false), _hasOffsetFirst(false) { };
+	CifTree21(const Common::String &name, const Common::String &ext) : CifTree(name, ext), _hasLongNames(false), _hasOffsetFirst(false) { };
 
 protected:
 	virtual uint readHeader(Common::File &f);
@@ -239,12 +379,12 @@ void CifTree21::determineSubtype(Common::File &f) {
 	f.seek(pos);
 }
 
-const CifTree *CifTree::load(const Common::String filename) {
+const CifTree *CifTree::load(const Common::String &name, const Common::String &ext) {
 	Common::File f;
 	CifTree *cifTree = 0;
 
-	if (!f.open(filename)) {
-		warning("Failed to open CifTree '%s'", filename.c_str());
+	if (!f.open(name + '.' + ext)) {
+		warning("Failed to open CifTree '%s'", name.c_str());
 		return 0;
 	}
 
@@ -253,7 +393,7 @@ const CifTree *CifTree::load(const Common::String filename) {
 	id[19] = 0;
 
 	if (f.eos() || Common::String(id) != "CIF TREE WayneSikes") {
-		warning("Invalid id string found in CifTree '%s'", filename.c_str());
+		warning("Invalid id string found in CifTree '%s'", name.c_str());
 		f.close();
 		return 0;
 	}
@@ -266,75 +406,127 @@ const CifTree *CifTree::load(const Common::String filename) {
 	ver = f.readUint16LE() << 16;
 	ver |= f.readUint16LE();
 
+	f.close();
+
 	switch(ver) {
 	case 0x00020000:
-		cifTree = new CifTree20;
+		cifTree = new CifTree20(name, ext);
 		break;
 	case 0x00020001:
-		cifTree = new CifTree21;
+		cifTree = new CifTree21(name, ext);
 		break;
 	default:
-		warning("Unsupported version %d.%d found in CifTree '%s'", ver >> 16, ver & 0xffff, filename.c_str());
+		warning("Unsupported version %d.%d found in CifTree '%s'", ver >> 16, ver & 0xffff, name.c_str());
 	}
 
-	if (cifTree && !cifTree->initialize(f)) {
-		warning("Failed to read CifTree '%s'", filename.c_str());
+	if (cifTree && !cifTree->initialize()) {
+		warning("Failed to read CifTree '%s'", name.c_str());
 		delete cifTree;
 		cifTree = 0;
 	}
 
-	f.close();
 	return cifTree;
 }
 
-ResourceManager::ResourceManager(NancyEngine *vm) : _vm(vm), _cifTree(0) {
+ResourceManager::ResourceManager(NancyEngine *vm) : _vm(vm) {
 }
 
 ResourceManager::~ResourceManager() {
+	for (uint i = 0; i < _cifTrees.size(); i++)
+		delete _cifTrees[i];
 }
 
-bool ResourceManager::loadCifTree(const Common::String filename) {
+bool ResourceManager::loadCifTree(const Common::String &name, const Common::String &ext) {
 	// NOTE: It seems likely that multiple CifTrees can be open at the same time
 	// For now, we just replace the current CifTree with the new one
 
-	const CifTree *cifTree = CifTree::load(filename);
+	const CifTree *cifTree = CifTree::load(name, ext);
 
 	if (!cifTree)
 		return false;
 
-	// Delete previous tree
-	if (_cifTree)
-		delete _cifTree;
-
-	_cifTree = cifTree;
+	_cifTrees.push_back(cifTree);
 	return true;
 }
 
+const CifTree *ResourceManager::findCifTree(const Common::String &name) const {
+	for (uint i = 0; i < _cifTrees.size(); i++)
+		if (_cifTrees[i]->getName().equalsIgnoreCase(name))
+			return _cifTrees[i];
+
+	warning("CifTree '%s' not loaded", name.c_str());
+	return 0;
+}
+
 void ResourceManager::initialize() {
-	if (!loadCifTree("ciftree.dat"))
+	if (!loadCifTree("ciftree", "dat"))
 		error("Failed to read 'ciftree.dat'");
 }
 
-byte *ResourceManager::loadResource(const Common::String name, uint &size) {
-	CifInfo info;
+bool ResourceManager::getCifInfo(const Common::String &treeName, const Common::String &name, CifInfo &info) {
+	const CifTree *cifTree = findCifTree(treeName);
 
-	if (!_cifTree->findResource(name, info)) {
-		warning("Resource '%s' not found", name.c_str());
+	if (!cifTree)
 		return 0;
+
+	return cifTree->getCifInfo(name, info);
+}
+
+byte *ResourceManager::getCifData(const Common::String &treeName, const Common::String &name, uint *size) {
+	const CifTree *cifTree = findCifTree(treeName);
+	if (!cifTree)
+		return 0;
+
+	CifInfo info;
+	if (!cifTree->getCifInfo(name, info))
+		return 0;
+
+	byte *buf = cifTree->getCifData(name, size);
+
+	if (buf && info.comp == kResCompression) {
+		Decompressor dec;
+		Common::MemoryReadStream input(buf, info.compressedSize);
+		byte *raw = new byte[info.size];
+		Common::MemoryWriteStream output(raw, info.size);
+		if (!dec.decompress(input, output)) {
+			warning("Failed to decompress '%s' from '%s'", name.c_str(), treeName.c_str());
+			delete[] buf;
+			delete[] raw;
+			return 0;
+		}
+		delete[] buf;
+		if (size)
+			*size = output.size();
+		return raw;
 	}
 
-	byte *buf = decompress(info, size);
 	return buf;
 }
 
-bool ResourceManager::loadImage(const Common::String name, Graphics::Surface &surf) {
+byte *ResourceManager::loadCif(const Common::String &treeName, const Common::String &name, uint &size) {
+	return getCifData(treeName, name, &size);
+}
+
+byte *ResourceManager::loadData(const Common::String &treeName, const Common::String &name, uint &size) {
 	CifInfo info;
 
-	if (!_cifTree->findResource(name, info)) {
-		warning("Resource '%s' not found", name.c_str());
-		return false;
+	if (!getCifInfo(treeName, name, info))
+		return 0;
+
+	if (info.type != kResTypeData) {
+		warning("Resource '%s' is not data", name.c_str());
+		return 0;
 	}
 
+	return getCifData(treeName, name);
+}
+
+bool ResourceManager::loadImage(const Common::String &treeName, const Common::String &name, Graphics::Surface &surf) {
+	CifInfo info;
+
+	if (!getCifInfo(treeName, name, info))
+		return false;
+
 	if (info.type != kResTypeImage) {
 		warning("Resource '%s' is not an image", name.c_str());
 		return false;
@@ -345,13 +537,10 @@ bool ResourceManager::loadImage(const Common::String name, Graphics::Surface &su
 		return false;
 	}
 
-	uint size;
-	byte *buf = decompress(info, size);
+	byte *buf = getCifData(treeName, name);
 
-	if (!buf) {
-		warning("Failed to decompress image '%s'", name.c_str());
+	if (!buf)
 		return false;
-	}
 
 	Graphics::PixelFormat format(2, 5, 5, 5, 0, 10, 5, 0, 0);
 	surf.w = info.width;
@@ -362,103 +551,24 @@ bool ResourceManager::loadImage(const Common::String name, Graphics::Surface &su
 	return true;
 }
 
-byte *ResourceManager::decompress(const CifInfo &info, uint &size) {
-	// TODO: clean this up...
-	Common::File cifTree;
-	uint read = 0, written = 0;
-
-	if (info.comp != kResCompression) {
-		warning("Resource '%s' is not compressed", info.name.c_str());
-		return 0;
-	}
-
-	if (!cifTree.open(_cifTree->getName())) {
-		warning("Failed to open '%s'", _cifTree->getName().c_str());
-		return 0;
-	}
-
-	cifTree.seek(info.dataOffset);
-
-	const int bufSize = 4096;
-	const int bufStart = 4078;
-	byte *buf = new byte[bufSize];
-	byte *output = new byte[info.size];
-	uint bufpos = bufStart;
-
-	memset(buf, ' ', bufStart);
-	uint16 bits = 0;
-	byte val = 0;
-
-	while(1) {
-		bits >>= 1;
+void ResourceManager::list(const Common::String &treeName, Common::Array<Common::String> &nameList, uint type) {
+	const CifTree *cifTree = findCifTree(treeName);
 
-		// The highest 8 bits are used to keep track of how many bits are left to process
-		if (!(bits & 0x100)) {
-			// Out of bits
-			if (cifTree.eos() || read == info.compressedSize)
-				break;
-			bits = 0xff00 | ((cifTree.readByte() - val++) & 0xff);
-			++read;
-		}
-
-		if (bits & 1) {
-			// Literal byte
-			if (cifTree.eos() || read == info.compressedSize)
-				break;
-			byte b = cifTree.readByte() - val++;
-			++read;
-			output[written++] = b;
-			if (written == info.size)
-				break;
-			buf[bufpos++] = b;
-			bufpos &= bufSize - 1;
-		} else {
-			// Copy from buffer
-			if (cifTree.eos() || read == info.compressedSize)
-				break;
-			byte b1 = cifTree.readByte() - val++;
-			++read;
-
-			if (cifTree.eos() || read == info.compressedSize)
-				break;
-			byte b2 = cifTree.readByte() - val++;
-			++read;
-
-			uint16 offset = b1 | ((b2 & 0xf0) << 4);
-			uint16 len = (b2 & 0xf) + 3;
-	
-			for (uint i = 0; i < len; i++) {
-				byte t = buf[(offset + i) & (bufSize - 1)];
-				output[written++] = t;
-				if (written == info.size)
-					break;
-				buf[bufpos++] = t;
-				bufpos &=  bufSize - 1;
-			}
-		}
-	}
-
-	delete[] buf;
-
-	if (read != info.compressedSize || written != info.size) {
-		warning("Failed to decompress resource");
-		delete[] output;
-		return 0;
-	}
-
-	size = written;
-	return output;
-}
+	if (!cifTree)
+		return;
 
-void ResourceManager::listResources(Common::Array<Common::String> &list, uint type) {
-	_cifTree->listResources(list, type);
+	cifTree->list(nameList, type);
 }
 
-Common::String ResourceManager::getResourceDesc(const Common::String name) {
+Common::String ResourceManager::getCifDescription(const Common::String &treeName, const Common::String &name) {
 	CifInfo info;
+	const CifTree *cifTree = findCifTree(treeName);
+
+	if (!cifTree)
+		return Common::String::format("Failed to open CifTree '%s'\n", treeName.c_str());
 
-	if (!_cifTree->findResource(name, info))
-		return Common::String::format("Resource '%s' not found\n", name.c_str());
+	if (!cifTree->getCifInfo(name, info))
+		return Common::String::format("Couldn't find '%s' in CifTree '%s'\n", name.c_str(), treeName.c_str());
 
 	Common::String desc;
 	desc = Common::String::format("Name: %s\n", info.name.c_str());
diff --git a/engines/nancy/resource.h b/engines/nancy/resource.h
index 4c4d778db6..c164566114 100644
--- a/engines/nancy/resource.h
+++ b/engines/nancy/resource.h
@@ -64,18 +64,22 @@ public:
 	~ResourceManager();
 
 	void initialize();
-	bool loadCifTree(const Common::String filename);
-	byte *loadResource(const Common::String name, uint &size);
-	bool loadImage(const Common::String name, Graphics::Surface &surf);
+	bool loadCifTree(const Common::String &name, const Common::String &ext);
+	bool loadImage(const Common::String &treeName, const Common::String &name, Graphics::Surface &surf);
+	byte *loadData(const Common::String &treeName, const Common::String &name, uint &size);
 
 	// Debugger functions
-	void listResources(Common::Array<Common::String> &list, uint type);
-	Common::String getResourceDesc(const Common::String name);
+	void list(const Common::String &treeName, Common::Array<Common::String> &nameList, uint type);
+	byte *loadCif(const Common::String &treeName, const Common::String &name, uint &size);
+	Common::String getCifDescription(const Common::String &treeName, const Common::String &name);
 private:
 	NancyEngine *_vm;
-	const CifTree *_cifTree;
 
-	byte *decompress(const CifInfo &res, uint &size);
+	byte *getCifData(const Common::String &treeName, const Common::String &name, uint *size = 0);
+	bool getCifInfo(const Common::String &treeName, const Common::String &name, CifInfo &info);
+	const CifTree *findCifTree(const Common::String &name) const;
+
+	Common::Array<const CifTree *> _cifTrees;
 };
 
 } // End of namespace Nancy


Commit: 1df38bd5005b0995ab8852aa4cd376c2d012ae9b
    https://github.com/scummvm/scummvm/commit/1df38bd5005b0995ab8852aa4cd376c2d012ae9b
Author: Walter van Niftrik (walter at scummvm.org)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add support for .cif patches

Changed paths:
    engines/nancy/resource.cpp
    engines/nancy/resource.h


diff --git a/engines/nancy/resource.cpp b/engines/nancy/resource.cpp
index 6606c86a3c..7ff36983b5 100644
--- a/engines/nancy/resource.cpp
+++ b/engines/nancy/resource.cpp
@@ -30,15 +30,15 @@
 
 namespace Nancy {
 
-static void readCifInfo20(Common::File &f, ResourceManager::CifInfo &info, bool hasOffset = false) {
+static void readCifInfo20(Common::File &f, ResourceManager::CifInfo &info, uint32 *dataOffset = 0) {
 	info.width = f.readUint16LE();
 	info.pitch = f.readUint16LE();
 	info.height = f.readUint16LE();
 	info.depth = f.readByte();
 
 	info.comp = f.readByte();
-	if (hasOffset)
-		info.dataOffset = f.readUint32LE();
+	if (dataOffset)
+		*dataOffset = f.readUint32LE();
 	info.size = f.readUint32LE();
 	f.skip(4); // A 2nd size for obsolete Cif type 1
 	info.compressedSize = f.readUint32LE();
@@ -143,14 +143,140 @@ bool Decompressor::decompress(Common::ReadStream &input, Common::MemoryWriteStre
 	return true;
 }
 
+class CifFile {
+public:
+	CifFile(const Common::String &name, Common::File *f) : _name(name), _f(f) { };
+	virtual ~CifFile();
+
+	bool initialize();
+	byte *getCifData(ResourceManager::CifInfo &info, uint *size = 0) const;
+	void getCifInfo(ResourceManager::CifInfo &info) const;
+
+	static const CifFile *load(const Common::String &name);
+
+protected:
+	virtual void readCifInfo(Common::File &f) = 0;
+
+	ResourceManager::CifInfo _cifInfo;
+	Common::String _name;
+	Common::File *_f;
+	uint32 _dataOffset;
+};
+
+CifFile::~CifFile() {
+	delete _f;
+}
+
+bool CifFile::initialize() {
+	readCifInfo(*_f);
+
+	if (_f->eos() || _f->err()) {
+		warning("Error reading from CifFile '%s'", _name.c_str());
+		return false;
+	}
+
+	return true;
+}
+
+byte *CifFile::getCifData(ResourceManager::CifInfo &info, uint *size) const {
+	uint dataSize = (_cifInfo.comp == 2 ? _cifInfo.compressedSize : _cifInfo.size);
+	byte *buf = new byte[dataSize];
+
+	if (_f->read(buf, dataSize) < dataSize) {
+		warning("Failed to read CifFile '%s'", _name.c_str());
+		delete[] buf;
+		return 0;
+	}
+
+	if (size)
+		*size = dataSize;
+	info = _cifInfo;
+	return buf;
+}
+
+void CifFile::getCifInfo(ResourceManager::CifInfo &info) const {
+	info = _cifInfo;
+}
+
+class CifFile20 : public CifFile {
+public:
+	CifFile20(const Common::String &name, Common::File *f) : CifFile(name, f) { }
+protected:
+	virtual void readCifInfo(Common::File &f);
+};
+
+void CifFile20::readCifInfo(Common::File &f) {
+	readCifInfo20(f, _cifInfo);
+}
+
+class CifFile21 : public CifFile {
+public:
+	CifFile21(const Common::String &name, Common::File *f) : CifFile(name, f) { }
+protected:
+	virtual void readCifInfo(Common::File &f);
+};
+
+void CifFile21::readCifInfo(Common::File &f) {
+	f.skip(32);
+	readCifInfo20(f, _cifInfo);
+}
+
+const CifFile *CifFile::load(const Common::String &name) {
+	Common::File *f = new Common::File;
+	CifFile *cifFile = 0;
+
+	if (!f->open(name + ".cif")) {
+		delete f;
+		return 0;
+	}
+
+	char id[20];
+	f->read(id, 20);
+	id[19] = 0;
+
+	if (f->eos() || Common::String(id) != "CIF FILE WayneSikes") {
+		warning("Invalid id string found in CifFile '%s'", name.c_str());
+		delete f;
+		return 0;
+	}
+
+	// 4 bytes unused
+	f->skip(4);
+
+	// Probably some kind of version number
+	uint32 ver;
+	ver = f->readUint16LE() << 16;
+	ver |= f->readUint16LE();
+
+	switch(ver) {
+	case 0x00020000:
+		cifFile = new CifFile20(name, f);
+		break;
+	case 0x00020001:
+		cifFile = new CifFile21(name, f);
+		break;
+	default:
+		warning("Unsupported version %d.%d found in CifFile '%s'", ver >> 16, ver & 0xffff, name.c_str());
+	}
+
+	if (!cifFile || !cifFile->initialize()) {
+		warning("Failed to read CifFile '%s'", name.c_str());
+		delete cifFile;
+		delete f;
+		return 0;
+	}
+
+	return cifFile;
+}
+
 class CifTree {
 public:
 	CifTree(const Common::String &name, const Common::String &ext);
 	virtual ~CifTree() { };
 	bool initialize();
 	void list(Common::Array<Common::String> &nameList, uint type) const;
-	byte *getCifData(const Common::String &name, uint *size = 0) const;
-	bool getCifInfo(const Common::String &name, ResourceManager::CifInfo &info) const;
+	byte *getCifData(const Common::String &name, ResourceManager::CifInfo &info, uint *size = 0) const;
+	bool getCifInfo(const Common::String &name, ResourceManager::CifInfo &info, uint32 *dataOffset = 0) const;
 	const Common::String &getName() const { return _name; }
 
 	static const CifTree *load(const Common::String &name, const Common::String &ext);
@@ -162,6 +288,7 @@ protected:
 
 	struct CifInfoChain {
 		struct ResourceManager::CifInfo info;
+		uint32 dataOffset;
 		uint16 next;
 	};
 
@@ -210,7 +337,7 @@ void CifTree::list(Common::Array<Common::String> &nameList, uint type) const {
 	}
 }
 
-bool CifTree::getCifInfo(const Common::String &name, ResourceManager::CifInfo &info) const {
+bool CifTree::getCifInfo(const Common::String &name, ResourceManager::CifInfo &info, uint32 *dataOffset) const {
 	Common::String nameUpper = name;
 	nameUpper.toUppercase();
 	uint hash = 0;
@@ -224,6 +351,8 @@ bool CifTree::getCifInfo(const Common::String &name, ResourceManager::CifInfo &i
 	while (index != 0xffff) {
 		if (nameUpper == _cifInfo[index].info.name) {
 			info = _cifInfo[index].info;
+			if (dataOffset)
+				*dataOffset = _cifInfo[index].dataOffset;
 			return true;
 		}
 		index = _cifInfo[index].next;
@@ -233,9 +362,10 @@ bool CifTree::getCifInfo(const Common::String &name, ResourceManager::CifInfo &i
 	return false;
 }
 
-byte *CifTree::getCifData(const Common::String &name, uint *size) const {
-	ResourceManager::CifInfo info;
-	if (!getCifInfo(name, info))
+byte *CifTree::getCifData(const Common::String &name, ResourceManager::CifInfo &info, uint *size) const {
+	uint32 dataOffset;
+
+	if (!getCifInfo(name, info, &dataOffset))
 		return 0;
 
 	Common::File f;
@@ -248,7 +378,7 @@ byte *CifTree::getCifData(const Common::String &name, uint *size) const {
 	uint dataSize = (info.comp == 2 ? info.compressedSize : info.size);
 	byte *buf = new byte[dataSize];
 
-	if (!f.seek(info.dataOffset) || f.read(buf, dataSize) < dataSize) {
+	if (!f.seek(dataOffset) || f.read(buf, dataSize) < dataSize) {
 		warning("Failed to read data for '%s' from CifTree '%s'", name.c_str(), _name.c_str());
 		delete[] buf;
 		f.close();
@@ -288,7 +418,7 @@ void CifTree20::readCifInfo(Common::File &f, CifInfoChain &chain) {
 
 	f.skip(2); // Index of this block
 
-	readCifInfo20(f, info, true);
+	readCifInfo20(f, info, &chain.dataOffset);
 
 	chain.next = f.readUint16LE();
 	if (f.eos())
@@ -338,13 +468,13 @@ void CifTree21::readCifInfo(Common::File &f, CifInfoChain &chain) {
 	f.skip(2); // Index of this block
 
 	if (_hasOffsetFirst) {
-		info.dataOffset = f.readUint32LE();
+		chain.dataOffset = f.readUint32LE();
 		chain.next = f.readUint16LE();
 	}
 
 	f.skip(32); // TODO
 
-	readCifInfo20(f, info, !_hasOffsetFirst);
+	readCifInfo20(f, info, (_hasOffsetFirst ? 0 : &chain.dataOffset));
 
 	if (!_hasOffsetFirst)
 		chain.next = f.readUint16LE();
@@ -464,6 +594,13 @@ void ResourceManager::initialize() {
 }
 
 bool ResourceManager::getCifInfo(const Common::String &treeName, const Common::String &name, CifInfo &info) {
+	const CifFile *cifFile = CifFile::load(name);
+
+	if (cifFile) {
+		cifFile->getCifInfo(info);
+		delete cifFile;
+	}
+
 	const CifTree *cifTree = findCifTree(treeName);
 
 	if (!cifTree)
@@ -472,16 +609,20 @@ bool ResourceManager::getCifInfo(const Common::String &treeName, const Common::S
 	return cifTree->getCifInfo(name, info);
 }
 
-byte *ResourceManager::getCifData(const Common::String &treeName, const Common::String &name, uint *size) {
-	const CifTree *cifTree = findCifTree(treeName);
-	if (!cifTree)
-		return 0;
+byte *ResourceManager::getCifData(const Common::String &treeName, const Common::String &name, CifInfo &info, uint *size) {
+	const CifFile *cifFile = CifFile::load(name);
+	byte *buf;
 
-	CifInfo info;
-	if (!cifTree->getCifInfo(name, info))
-		return 0;
+	if (cifFile) {
+		buf = cifFile->getCifData(info, size);
+		delete cifFile;
+	} else {
+		const CifTree *cifTree = findCifTree(treeName);
+		if (!cifTree)
+			return 0;
 
-	byte *buf = cifTree->getCifData(name, size);
+		buf = cifTree->getCifData(name, info, size);
+	}
 
 	if (buf && info.comp == kResCompression) {
 		Decompressor dec;
@@ -489,7 +630,7 @@ byte *ResourceManager::getCifData(const Common::String &treeName, const Common::
 		byte *raw = new byte[info.size];
 		Common::MemoryWriteStream output(raw, info.size);
 		if (!dec.decompress(input, output)) {
-			warning("Failed to decompress '%s' from '%s'", name.c_str(), treeName.c_str());
+			warning("Failed to decompress '%s'", name.c_str());
 			delete[] buf;
 			delete[] raw;
 			return 0;
@@ -504,44 +645,47 @@ byte *ResourceManager::getCifData(const Common::String &treeName, const Common::
 }
 
 byte *ResourceManager::loadCif(const Common::String &treeName, const Common::String &name, uint &size) {
-	return getCifData(treeName, name, &size);
+	CifInfo info;
+	return getCifData(treeName, name, info, &size);
 }
 
 byte *ResourceManager::loadData(const Common::String &treeName, const Common::String &name, uint &size) {
 	CifInfo info;
 
-	if (!getCifInfo(treeName, name, info))
+	byte *buf = getCifData(treeName, name, info, &size);
+
+	if (!buf)
 		return 0;
 
 	if (info.type != kResTypeData) {
 		warning("Resource '%s' is not data", name.c_str());
+		delete[] buf;
 		return 0;
 	}
 
-	return getCifData(treeName, name);
+	return buf;
 }
 
 bool ResourceManager::loadImage(const Common::String &treeName, const Common::String &name, Graphics::Surface &surf) {
 	CifInfo info;
 
-	if (!getCifInfo(treeName, name, info))
+	byte *buf = getCifData(treeName, name, info);
+
+	if (!buf)
 		return false;
 
 	if (info.type != kResTypeImage) {
 		warning("Resource '%s' is not an image", name.c_str());
+		delete[] buf;
 		return false;
 	}
 
 	if (info.depth != 16) {
 		warning("Image '%s' has unsupported depth %i", name.c_str(), info.depth);
+		delete[] buf;
 		return false;
 	}
 
-	byte *buf = getCifData(treeName, name);
-
-	if (!buf)
-		return false;
-
 	Graphics::PixelFormat format(2, 5, 5, 5, 0, 10, 5, 0, 0);
 	surf.w = info.width;
 	surf.h = info.height;
@@ -574,7 +718,6 @@ Common::String ResourceManager::getCifDescription(const Common::String &treeName
 	desc = Common::String::format("Name: %s\n", info.name.c_str());
 	desc += Common::String::format("Type: %i\n", info.type);
 	desc += Common::String::format("Compression: %i\n", info.comp);
-	desc += Common::String::format("Data offset: %i\n", info.dataOffset);
 	desc += Common::String::format("Size: %i\n", info.size);
 	desc += Common::String::format("Compressed size: %i\n", info.compressedSize);
 	desc += Common::String::format("Width: %i\n", info.width);
diff --git a/engines/nancy/resource.h b/engines/nancy/resource.h
index c164566114..3623c77bcb 100644
--- a/engines/nancy/resource.h
+++ b/engines/nancy/resource.h
@@ -57,7 +57,6 @@ public:
 		uint16 width, pitch, height;
 		byte depth; // Bit depth
 		uint32 compressedSize, size;
-		uint32 dataOffset;
 	};
 
 	ResourceManager(NancyEngine *vm);
@@ -75,7 +74,7 @@ public:
 private:
 	NancyEngine *_vm;
 
-	byte *getCifData(const Common::String &treeName, const Common::String &name, uint *size = 0);
+	byte *getCifData(const Common::String &treeName, const Common::String &name, CifInfo &info, uint *size = 0);
 	bool getCifInfo(const Common::String &treeName, const Common::String &name, CifInfo &info);
 	const CifTree *findCifTree(const Common::String &name) const;
 


Commit: 3efe6a6f7665e8e1ae95e3d6c2b3742603a82fe3
    https://github.com/scummvm/scummvm/commit/3efe6a6f7665e8e1ae95e3d6c2b3742603a82fe3
Author: Walter van Niftrik (walter at scummvm.org)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add partial .avf video support

Changed paths:
  A engines/nancy/decompress.cpp
  A engines/nancy/decompress.h
  A engines/nancy/video.cpp
  A engines/nancy/video.h
    engines/nancy/console.cpp
    engines/nancy/console.h
    engines/nancy/module.mk
    engines/nancy/nancy.cpp
    engines/nancy/resource.cpp
    engines/nancy/resource.h


diff --git a/engines/nancy/console.cpp b/engines/nancy/console.cpp
index bb94d59351..03db0fe21d 100644
--- a/engines/nancy/console.cpp
+++ b/engines/nancy/console.cpp
@@ -25,6 +25,7 @@
 #include "nancy/console.h"
 #include "nancy/nancy.h"
 #include "nancy/resource.h"
+#include "nancy/video.h"
 
 namespace Nancy {
 
@@ -35,11 +36,32 @@ NancyConsole::NancyConsole(NancyEngine *vm) : GUI::Debugger(), _vm(vm) {
 	registerCmd("res_list", WRAP_METHOD(NancyConsole, Cmd_resList));
 	registerCmd("res_info", WRAP_METHOD(NancyConsole, Cmd_resInfo));
 	registerCmd("res_show_image", WRAP_METHOD(NancyConsole, Cmd_resShowImage));
+	registerCmd("play_video", WRAP_METHOD(NancyConsole, Cmd_playVideo));
 }
 
 NancyConsole::~NancyConsole() {
 }
 
+void NancyConsole::postEnter() {
+	if (!_videoFile.empty()) {
+		Video::VideoDecoder *dec = new AVFDecoder;
+	
+		if (dec->loadFile(_videoFile)) {
+			dec->start();
+			while (!dec->endOfVideo()) {
+				const Graphics::Surface *frame = dec->decodeNextFrame();
+				_vm->_system->fillScreen(0);
+				_vm->_system->copyRectToScreen(frame->getPixels(), frame->pitch, 0, 0, frame->w, frame->h);
+				_vm->_system->updateScreen();
+				_vm->_system->delayMillis(60);
+			}
+		} else {
+			debugPrintf("Failed to load '%s'\n", _videoFile.c_str());
+		}
+		_videoFile.clear();
+	}
+}
+
 bool NancyConsole::Cmd_resHexDump(int argc, const char **argv) {
 	if (argc < 2 || argc > 3) {
 		debugPrintf("Dumps the specified resource to standard output\n");
@@ -134,9 +156,11 @@ bool NancyConsole::Cmd_resShowImage(int argc, const char **argv) {
 		_vm->_system->fillScreen(0);
 		_vm->_system->copyRectToScreen(surf.getPixels(), surf.pitch, 0, 0, surf.w > 640 ? 640 : surf.w, surf.h > 480 ? 480 : surf.h);
 		surf.free();
-	} else
+		return cmdExit(0, 0);
+	} else {
 		debugPrintf("Failed to load image\n");
-	return true;
+		return true;
+	}
 }
 
 bool NancyConsole::Cmd_resLoadCal(int argc, const char **argv) {
@@ -151,4 +175,16 @@ bool NancyConsole::Cmd_resLoadCal(int argc, const char **argv) {
 	return true;
 }
 
+bool NancyConsole::Cmd_playVideo(int argc, const char **argv) {
+	if (argc != 2) {
+		debugPrintf("Plays a video\n");
+		debugPrintf("Usage: %s <name>\n", argv[0]);
+		return true;
+	}
+
+	_videoFile = argv[1];
+	_videoFile += ".avf";
+	return cmdExit(0, 0);
+}
+
 } // End of namespace Nancy
diff --git a/engines/nancy/console.h b/engines/nancy/console.h
index 1e87738c7f..838d021802 100644
--- a/engines/nancy/console.h
+++ b/engines/nancy/console.h
@@ -34,6 +34,8 @@ public:
 	NancyConsole(NancyEngine *vm);
 	virtual ~NancyConsole(void);
 
+	void postEnter();
+
 private:
 	NancyEngine *_vm;
 	bool Cmd_resLoadCal(int argc, const char **argv);
@@ -42,6 +44,7 @@ private:
 	bool Cmd_resList(int argc, const char **argv);
 	bool Cmd_resInfo(int argc, const char **argv);
 	bool Cmd_resShowImage(int argc, const char **argv);
+	bool Cmd_playVideo(int argc, const char **argv);
 
 	bool Cmd_listScreens(int argc, const char **argv);
 	bool Cmd_listObjects(int argc, const char **argv);
@@ -49,6 +52,8 @@ private:
 	bool Cmd_getAllObjects(int argc, const char **argv);
 	bool Cmd_gotoScreen(int argc, const char **argv);
 	bool Cmd_boundaries(int argc, const char **argv);
+
+	Common::String _videoFile;
 };
 
 } // End of namespace Nancy
diff --git a/engines/nancy/decompress.cpp b/engines/nancy/decompress.cpp
new file mode 100644
index 0000000000..7135794374
--- /dev/null
+++ b/engines/nancy/decompress.cpp
@@ -0,0 +1,102 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/memstream.h"
+#include "common/textconsole.h"
+#include "nancy/decompress.h"
+
+namespace Nancy {
+
+void Decompressor::init(Common::ReadStream &input, Common::WriteStream &output) {
+	memset(_buf, ' ', kBufSize);
+	_bufpos = kBufStart;
+	_err = false;
+	_val = 0;
+	_input = &input;
+	_output = &output;
+}
+
+bool Decompressor::readByte(byte &b) {
+	b = _input->readByte();
+
+	if (_input->eos())
+		return false;
+
+	if (_input->err())
+		error("Read error encountered during decompression");
+
+	b -= _val++;
+	return true;
+}
+
+bool Decompressor::writeByte(byte b) {
+	_output->writeByte(b);
+	_buf[_bufpos++] = b;
+	_bufpos &= kBufSize - 1;
+	return true;
+}
+
+bool Decompressor::decompress(Common::ReadStream &input, Common::MemoryWriteStream &output) {
+	init(input, output);
+	uint16 bits = 0;
+
+	while(1) {
+		byte b;
+
+		bits >>= 1;
+
+		// The highest 8 bits are used to keep track of how many bits are left to process
+		if (!(bits & 0x100)) {
+			// Out of bits
+			if (!readByte(b))
+				break;
+			bits = 0xff00 | b;
+		}
+
+		if (bits & 1) {
+			// Literal byte
+			if (!readByte(b))
+				break;
+			writeByte(b);
+		} else {
+			// Copy from buffer
+			byte b2;
+			if (!readByte(b) || !readByte(b2))
+				break;
+
+			uint16 offset = b | ((b2 & 0xf0) << 4);
+			uint16 len = (b2 & 0xf) + 3;
+
+			for (uint i = 0; i < len; i++)
+				writeByte(_buf[(offset + i) & (kBufSize - 1)]);
+		}
+	}
+
+	if (output.err() || output.pos() != output.size()) {
+		warning("Failed to decompress resource");
+		return false;
+	}
+
+	return true;
+}
+
+} // End of namespace Nancy
diff --git a/engines/nancy/decompress.h b/engines/nancy/decompress.h
new file mode 100644
index 0000000000..94ffe5e314
--- /dev/null
+++ b/engines/nancy/decompress.h
@@ -0,0 +1,59 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef NANCY_DECOMPRESS_H
+#define NANCY_DECOMPRESS_H
+
+namespace Common {
+	class ReadStream;
+	class WriteStream;
+}
+
+namespace Nancy {
+
+class Decompressor {
+public:
+	// Decompresses data from input until the end of the stream
+	// The output stream must have the right size for the decompressed data
+	bool decompress(Common::ReadStream &input, Common::MemoryWriteStream &output);
+
+private:
+	enum {
+		kBufSize = 4096,
+		kBufStart = 4078
+	};
+
+	void init(Common::ReadStream &input, Common::WriteStream &output);
+	bool readByte(byte &b);
+	bool writeByte(byte b);
+
+	byte _buf[kBufSize];
+	uint _bufpos;
+	bool _err;
+	byte _val;
+	Common::ReadStream *_input;
+	Common::WriteStream *_output;
+};
+
+} // End of namespace Nancy
+
+#endif
diff --git a/engines/nancy/module.mk b/engines/nancy/module.mk
index 34d280cee2..d1712e10f6 100644
--- a/engines/nancy/module.mk
+++ b/engines/nancy/module.mk
@@ -4,7 +4,9 @@ MODULE_OBJS = \
   console.o \
   nancy.o \
   detection.o \
-  resource.o
+  resource.o \
+  video.o \
+  decompress.o
 
 # This module can be built as a plugin
 ifeq ($(ENABLE_NANCY), DYNAMIC_PLUGIN)
diff --git a/engines/nancy/nancy.cpp b/engines/nancy/nancy.cpp
index f2e089a867..85d4d4ae7c 100644
--- a/engines/nancy/nancy.cpp
+++ b/engines/nancy/nancy.cpp
@@ -44,6 +44,10 @@ NancyEngine::NancyEngine(OSystem *syst, const NancyGameDescription *gd) : Engine
 	const Common::FSNode gameDataDir(ConfMan.get("path"));
 	SearchMan.addSubDirectoryMatching(gameDataDir, "game");
 	SearchMan.addSubDirectoryMatching(gameDataDir, "datafiles");
+	SearchMan.addSubDirectoryMatching(gameDataDir, "hdsound");
+	SearchMan.addSubDirectoryMatching(gameDataDir, "cdsound");
+	SearchMan.addSubDirectoryMatching(gameDataDir, "hdvideo");
+	SearchMan.addSubDirectoryMatching(gameDataDir, "cdvideo");
 
 	DebugMan.addDebugChannel(kDebugSchedule, "Schedule", "Script Schedule debug level");
 	DebugMan.addDebugChannel(kDebugEngine, "Engine", "Engine debug level");
diff --git a/engines/nancy/resource.cpp b/engines/nancy/resource.cpp
index 7ff36983b5..069ade9fba 100644
--- a/engines/nancy/resource.cpp
+++ b/engines/nancy/resource.cpp
@@ -27,6 +27,7 @@
 #include "common/memstream.h"
 #include "graphics/surface.h"
 #include "nancy/resource.h"
+#include "nancy/decompress.h"
 
 namespace Nancy {
 
@@ -46,103 +47,6 @@ static void readCifInfo20(Common::File &f, ResourceManager::CifInfo &info, uint3
 	info.type = f.readByte();
 }
 
-class Decompressor {
-public:
-	// Decompresses data from input until the end of the stream
-	// The output stream must have the right size for the decompressed data
-	bool decompress(Common::ReadStream &input, Common::MemoryWriteStream &output);
-
-private:
-	enum {
-		kBufSize = 4096,
-		kBufStart = 4078
-	};
-
-	void init(Common::ReadStream &input, Common::WriteStream &output);
-	bool readByte(byte &b);
-	bool writeByte(byte b);
-
-	byte _buf[kBufSize];
-	uint _bufpos;
-	bool _err;
-	byte _val;
-	Common::ReadStream *_input;
-	Common::WriteStream *_output;
-};
-
-void Decompressor::init(Common::ReadStream &input, Common::WriteStream &output) {
-	memset(_buf, ' ', kBufSize);
-	_bufpos = kBufStart;
-	_err = false;
-	_val = 0;
-	_input = &input;
-	_output = &output;
-}
-
-bool Decompressor::readByte(byte &b) {
-	b = _input->readByte();
-
-	if (_input->eos())
-		return false;
-
-	if (_input->err())
-		error("Read error encountered during decompression");
-
-	b -= _val++;
-	return true;
-}
-
-bool Decompressor::writeByte(byte b) {
-	_output->writeByte(b);
-	_buf[_bufpos++] = b;
-	_bufpos &= kBufSize - 1;
-	return true;
-}
-
-bool Decompressor::decompress(Common::ReadStream &input, Common::MemoryWriteStream &output) {
-	init(input, output);
-	uint16 bits = 0;
-
-	while(1) {
-		byte b;
-
-		bits >>= 1;
-
-		// The highest 8 bits are used to keep track of how many bits are left to process
-		if (!(bits & 0x100)) {
-			// Out of bits
-			if (!readByte(b))
-				break;
-			bits = 0xff00 | b;
-		}
-
-		if (bits & 1) {
-			// Literal byte
-			if (!readByte(b))
-				break;
-			writeByte(b);
-		} else {
-			// Copy from buffer
-			byte b2;
-			if (!readByte(b) || !readByte(b2))
-				break;
-
-			uint16 offset = b | ((b2 & 0xf0) << 4);
-			uint16 len = (b2 & 0xf) + 3;
-
-			for (uint i = 0; i < len; i++)
-				writeByte(_buf[(offset + i) & (kBufSize - 1)]);
-		}
-	}
-
-	if (output.err() || output.pos() != output.size()) {
-		warning("Failed to decompress resource");
-		return false;
-	}
-
-	return true;
-}
-
 class CifFile {
 public:
 	CifFile(const Common::String &name, Common::File *f) : _name(name), _f(f) { };
@@ -559,11 +463,13 @@ const CifTree *CifTree::load(const Common::String &name, const Common::String &e
 }
 
 ResourceManager::ResourceManager(NancyEngine *vm) : _vm(vm) {
+	_dec = new Decompressor;
 }
 
 ResourceManager::~ResourceManager() {
 	for (uint i = 0; i < _cifTrees.size(); i++)
 		delete _cifTrees[i];
+	delete _dec;
 }
 
 bool ResourceManager::loadCifTree(const Common::String &name, const Common::String &ext) {
@@ -625,11 +531,10 @@ byte *ResourceManager::getCifData(const Common::String &treeName, const Common::
 	}
 
 	if (buf && info.comp == kResCompression) {
-		Decompressor dec;
 		Common::MemoryReadStream input(buf, info.compressedSize);
 		byte *raw = new byte[info.size];
 		Common::MemoryWriteStream output(raw, info.size);
-		if (!dec.decompress(input, output)) {
+		if (!_dec->decompress(input, output)) {
 			warning("Failed to decompress '%s'", name.c_str());
 			delete[] buf;
 			delete[] raw;
diff --git a/engines/nancy/resource.h b/engines/nancy/resource.h
index 3623c77bcb..a57d956d8c 100644
--- a/engines/nancy/resource.h
+++ b/engines/nancy/resource.h
@@ -35,6 +35,7 @@ namespace Nancy {
 
 class NancyEngine;
 class CifTree;
+class Decompressor;
 
 class ResourceManager {
 public:
@@ -73,6 +74,7 @@ public:
 	Common::String getCifDescription(const Common::String &treeName, const Common::String &name);
 private:
 	NancyEngine *_vm;
+	Decompressor *_dec;
 
 	byte *getCifData(const Common::String &treeName, const Common::String &name, CifInfo &info, uint *size = 0);
 	bool getCifInfo(const Common::String &treeName, const Common::String &name, CifInfo &info);
diff --git a/engines/nancy/video.cpp b/engines/nancy/video.cpp
new file mode 100644
index 0000000000..e5c1122c95
--- /dev/null
+++ b/engines/nancy/video.cpp
@@ -0,0 +1,131 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/endian.h"
+#include "common/stream.h"
+#include "common/system.h"
+#include "common/textconsole.h"
+#include "common/debug.h"
+#include "common/memstream.h"
+
+#include "graphics/surface.h"
+
+#include "nancy/video.h"
+#include "nancy/decompress.h"
+
+namespace Nancy {
+
+AVFDecoder::~AVFDecoder() {
+	close();
+}
+
+bool AVFDecoder::loadStream(Common::SeekableReadStream *stream) {
+	close();
+
+	char id[15];
+	stream->read(id, 15);
+	id[14] = 0;
+
+	if (stream->eos() || Common::String(id) != "AVF WayneSikes") {
+		warning("Invalid id string found in AVF");
+		return false;
+	}
+
+	stream->skip(1); // Unknown
+
+	uint32 ver;
+	ver = stream->readUint16LE() << 16;
+	ver |= stream->readUint16LE();
+
+	if (ver != 0x00020000) {
+		warning("Unsupported version %d.%d found in AVF", ver >> 16, ver & 0xffff);
+		return false;
+	}
+
+	addTrack(new AVFVideoTrack(stream));
+
+	return true;
+}
+
+AVFDecoder::AVFVideoTrack::AVFVideoTrack(Common::SeekableReadStream *stream) {
+	assert(stream);
+	_fileStream = stream;
+	_curFrame = -1;
+	_dec = new Decompressor;
+
+	stream->skip(1); // Unknown
+
+	_frameCount = stream->readUint16LE();
+	_width = stream->readUint16LE();
+	_height = stream->readUint16LE();
+	_depth = stream->readByte();
+	stream->skip(4); // Unknown
+
+	byte comp = stream->readByte();
+
+	if (comp != 1 && comp != 2)
+		error("Unknown compression type %d found in AVF", comp);
+
+	_compressed = comp == 2;
+
+	_surface = new Graphics::Surface();
+	_pixelFormat = Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0);
+	_surface->create(_width, _height, _pixelFormat);
+
+	for (uint i = 0; i < _frameCount; i++) {
+		ChunkInfo info;
+		info.index = stream->readUint16LE();
+		info.offset = stream->readUint32LE();
+		info.compressedSize = stream->readUint32LE();
+		info.size = stream->readUint32LE();
+		stream->skip(5); // Unknown
+		_chunkInfo.push_back(info);
+	}
+}
+
+AVFDecoder::AVFVideoTrack::~AVFVideoTrack() {
+	delete _fileStream;
+	_surface->free();
+	delete _surface;
+	delete _dec;
+}
+
+const Graphics::Surface *AVFDecoder::AVFVideoTrack::decodeNextFrame() {
+	_curFrame++;
+	_fileStream->seek(_chunkInfo[_curFrame].offset);
+	uint size = _chunkInfo[_curFrame].size;
+
+	if (_compressed) {
+		Common::ReadStream *input = _fileStream->readStream(_chunkInfo[_curFrame].compressedSize);
+		Common::MemoryWriteStream output((byte *)_surface->getPixels(), size);
+		if (!_dec->decompress(*input, output))
+			warning("Failed to decompress frame %d in AVF", _curFrame);
+		delete input;
+	} else {
+		if (_fileStream->read(_surface->getPixels(), size) < size)
+			warning("Read error trying to copy uncompressed frame");
+	}
+
+	return _surface;
+}
+
+} // End of namespace Nancy
diff --git a/engines/nancy/video.h b/engines/nancy/video.h
new file mode 100644
index 0000000000..07e0a01f17
--- /dev/null
+++ b/engines/nancy/video.h
@@ -0,0 +1,88 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef NANCY_VIDEO_H
+#define NANCY_VIDEO_H
+
+#include "graphics/pixelformat.h"
+#include "video/video_decoder.h"
+#include "common/rational.h"
+
+namespace Common {
+class SeekableReadStream;
+}
+
+namespace Graphics {
+struct Surface;
+}
+
+namespace Nancy {
+
+class Decompressor;
+
+class AVFDecoder : public Video::VideoDecoder {
+public:
+	virtual ~AVFDecoder();
+
+	bool loadStream(Common::SeekableReadStream *stream);
+
+private:
+	class AVFVideoTrack : public FixedRateVideoTrack {
+	public:
+		AVFVideoTrack(Common::SeekableReadStream *stream);
+		~AVFVideoTrack();
+
+		uint16 getWidth() const { return _width; }
+		uint16 getHeight() const { return _height; }
+		Graphics::PixelFormat getPixelFormat() const { return _pixelFormat; }
+		int getCurFrame() const { return _curFrame; }
+		int getFrameCount() const { return _frameCount; }
+		const Graphics::Surface *decodeNextFrame();
+
+	protected:
+		Common::Rational getFrameRate() const { return 20; }
+
+	private:
+		struct ChunkInfo {
+			uint16 index;
+			uint32 offset;
+			uint32 compressedSize;
+			uint32 size;
+		};
+
+		bool decodeFrame(byte *rleData, int rleSize, byte *litData, int litSize, byte *dest, int left, int width, int height, int colorKey);
+
+		Common::SeekableReadStream *_fileStream;
+		Graphics::PixelFormat _pixelFormat;
+		uint _width, _height, _depth;
+		int _curFrame;
+		uint _frameCount;
+		bool _compressed;
+		Graphics::Surface *_surface;
+		Common::Array<ChunkInfo> _chunkInfo;
+		Decompressor *_dec;
+	};
+};
+
+} // End of namespace Nancy
+
+#endif


Commit: 6381b3664393ffd8fb0cbb272c6f0a447476a733
    https://github.com/scummvm/scummvm/commit/6381b3664393ffd8fb0cbb272c6f0a447476a733
Author: Walter van Niftrik (walter at scummvm.org)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add support for .his audio files

Changed paths:
  A engines/nancy/audio.cpp
  A engines/nancy/audio.h
    engines/nancy/console.cpp
    engines/nancy/console.h
    engines/nancy/module.mk


diff --git a/engines/nancy/audio.cpp b/engines/nancy/audio.cpp
new file mode 100644
index 0000000000..743d3b6530
--- /dev/null
+++ b/engines/nancy/audio.cpp
@@ -0,0 +1,96 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/debug.h"
+#include "common/textconsole.h"
+#include "common/stream.h"
+#include "common/substream.h"
+
+#include "audio/audiostream.h"
+#include "audio/decoders/raw.h"
+
+namespace Nancy {
+
+Audio::RewindableAudioStream *makeHISStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) {
+	// HIS files are just WAVE files with the first 22 bytes of the file
+	// overwritten with a string
+
+	char buf[22];
+
+	stream->read(buf, 22);
+	buf[21] = 0;
+
+	if (Common::String(buf) != "Her Interactive Sound") {
+		warning("Invalid header found in HIS file");
+		return 0;
+	}
+
+	// Most of this is copied from the standard WAVE decoder
+	uint16 numChannels = stream->readUint16LE();
+	uint32 samplesPerSec = stream->readUint32LE();
+	stream->skip(6);
+	uint16 bitsPerSample = stream->readUint16LE();
+
+	byte flags = 0;
+	if (bitsPerSample == 8)		// 8 bit data is unsigned
+		flags |= Audio::FLAG_UNSIGNED;
+	else if (bitsPerSample == 16)	// 16 bit data is signed little endian
+		flags |= (Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN);
+	else {
+		warning("Unsupported bitsPerSample %d found in HIS file", bitsPerSample);
+		return 0;
+	}
+
+	if (numChannels == 2)
+		flags |= Audio::FLAG_STEREO;
+	else if (numChannels != 1) {
+		warning("Unsupported number of channels %d found in HIS file", numChannels);
+		return 0;
+	}
+
+	stream->read(buf, 4);
+	buf[4] = 0;
+
+	if (Common::String(buf) != "data") {
+		warning("Data chunk not found in HIS file");
+		return 0;
+	}
+
+	uint32 size = stream->readUint32LE();
+
+	if (stream->eos() || stream->err()) {
+		warning("Error reading HIS file");
+		return 0;
+	}
+
+	// Raw PCM, make sure the last packet is complete
+	uint sampleSize = (flags & Audio::FLAG_16BITS ? 2 : 1) * (flags & Audio::FLAG_STEREO ? 2 : 1);
+	if (size % sampleSize != 0) {
+		warning("Trying to play an HIS file with an incomplete PCM packet");
+		size &= ~(sampleSize - 1);
+	}
+
+	Common::SeekableSubReadStream *subStream = new Common::SeekableSubReadStream(stream, stream->pos(), size, disposeAfterUse);
+	return Audio::makeRawStream(subStream, samplesPerSec, flags);
+}
+
+} // End of namespace Nancy
diff --git a/engines/nancy/audio.h b/engines/nancy/audio.h
new file mode 100644
index 0000000000..8de4ae7ea6
--- /dev/null
+++ b/engines/nancy/audio.h
@@ -0,0 +1,38 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef NANCY_AUDIO_H
+#define NANCY_AUDIO_H
+
+namespace Common {
+class SeekableReadStream;
+}
+
+namespace Nancy {
+
+class RewindableAudioStream;
+
+Audio::RewindableAudioStream *makeHISStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse);
+
+} // End of namespace Nancy
+
+#endif
diff --git a/engines/nancy/console.cpp b/engines/nancy/console.cpp
index 03db0fe21d..ad4bb36778 100644
--- a/engines/nancy/console.cpp
+++ b/engines/nancy/console.cpp
@@ -22,10 +22,12 @@
 
 #include "common/system.h"
 #include "graphics/surface.h"
+#include "audio/audiostream.h"
 #include "nancy/console.h"
 #include "nancy/nancy.h"
 #include "nancy/resource.h"
 #include "nancy/video.h"
+#include "nancy/audio.h"
 
 namespace Nancy {
 
@@ -37,6 +39,7 @@ NancyConsole::NancyConsole(NancyEngine *vm) : GUI::Debugger(), _vm(vm) {
 	registerCmd("res_info", WRAP_METHOD(NancyConsole, Cmd_resInfo));
 	registerCmd("res_show_image", WRAP_METHOD(NancyConsole, Cmd_resShowImage));
 	registerCmd("play_video", WRAP_METHOD(NancyConsole, Cmd_playVideo));
+	registerCmd("play_audio", WRAP_METHOD(NancyConsole, Cmd_playAudio));
 }
 
 NancyConsole::~NancyConsole() {
@@ -187,4 +190,29 @@ bool NancyConsole::Cmd_playVideo(int argc, const char **argv) {
 	return cmdExit(0, 0);
 }
 
+bool NancyConsole::Cmd_playAudio(int argc, const char **argv) {
+	if (argc != 2) {
+		debugPrintf("Plays an audio file\n");
+		debugPrintf("Usage: %s <name>\n", argv[0]);
+		return true;
+	}
+
+	Common::File *f = new Common::File;
+	if (!f->open(Common::String(argv[1]) + ".his")) {
+		debugPrintf("Failed to open '%s.his'\n", argv[1]);
+		return true;
+	}
+
+	Audio::AudioStream *stream = makeHISStream(f, DisposeAfterUse::YES);
+
+	if (!stream) {
+		debugPrintf("Failed to load '%s.his'\n", argv[1]);
+		delete f;
+		return true;
+	}
+	Audio::SoundHandle handle;
+	_vm->_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &handle, stream);
+	return true;
+}
+
 } // End of namespace Nancy
diff --git a/engines/nancy/console.h b/engines/nancy/console.h
index 838d021802..293ba79b81 100644
--- a/engines/nancy/console.h
+++ b/engines/nancy/console.h
@@ -45,6 +45,7 @@ private:
 	bool Cmd_resInfo(int argc, const char **argv);
 	bool Cmd_resShowImage(int argc, const char **argv);
 	bool Cmd_playVideo(int argc, const char **argv);
+	bool Cmd_playAudio(int argc, const char **argv);
 
 	bool Cmd_listScreens(int argc, const char **argv);
 	bool Cmd_listObjects(int argc, const char **argv);
diff --git a/engines/nancy/module.mk b/engines/nancy/module.mk
index d1712e10f6..6b2d75cbec 100644
--- a/engines/nancy/module.mk
+++ b/engines/nancy/module.mk
@@ -6,7 +6,8 @@ MODULE_OBJS = \
   detection.o \
   resource.o \
   video.o \
-  decompress.o
+  decompress.o \
+  audio.o
 
 # This module can be built as a plugin
 ifeq ($(ENABLE_NANCY), DYNAMIC_PLUGIN)


Commit: ef8daa9f3f519235d8a4eb57602317d4c854f7c2
    https://github.com/scummvm/scummvm/commit/ef8daa9f3f519235d8a4eb57602317d4c854f7c2
Author: Walter van Niftrik (walter at scummvm.org)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add exporting to .cif

Changed paths:
    engines/nancy/console.cpp
    engines/nancy/console.h
    engines/nancy/resource.cpp
    engines/nancy/resource.h


diff --git a/engines/nancy/console.cpp b/engines/nancy/console.cpp
index ad4bb36778..e50788c822 100644
--- a/engines/nancy/console.cpp
+++ b/engines/nancy/console.cpp
@@ -34,7 +34,7 @@ namespace Nancy {
 NancyConsole::NancyConsole(NancyEngine *vm) : GUI::Debugger(), _vm(vm) {
 	registerCmd("res_load_cal", WRAP_METHOD(NancyConsole, Cmd_resLoadCal));
 	registerCmd("res_hexdump", WRAP_METHOD(NancyConsole, Cmd_resHexDump));
-	registerCmd("res_diskdump", WRAP_METHOD(NancyConsole, Cmd_resDiskDump));
+	registerCmd("export_cif", WRAP_METHOD(NancyConsole, Cmd_exportCif));
 	registerCmd("res_list", WRAP_METHOD(NancyConsole, Cmd_resList));
 	registerCmd("res_info", WRAP_METHOD(NancyConsole, Cmd_resInfo));
 	registerCmd("res_show_image", WRAP_METHOD(NancyConsole, Cmd_resShowImage));
@@ -84,34 +84,16 @@ bool NancyConsole::Cmd_resHexDump(int argc, const char **argv) {
 	return true;
 }
 
-bool NancyConsole::Cmd_resDiskDump(int argc, const char **argv) {
+bool NancyConsole::Cmd_exportCif(int argc, const char **argv) {
 	if (argc < 2 || argc > 3) {
-		debugPrintf("Dumps the specified resource to disk\n");
+		debugPrintf("Exports the specified resource to .cif file\n");
 		debugPrintf("Usage: %s name [cal]\n", argv[0]);
 		return true;
 	}
 
-	uint size;
-	byte *buf = _vm->_res->loadCif((argc == 2 ? "ciftree" : argv[2]), argv[1], size);
-	if (!buf) {
-		debugPrintf("Failed to load resource '%s'\n", argv[1]);
-		return true;
-	}
+	if (!_vm->_res->exportCif((argc == 2 ? "ciftree" : argv[2]), argv[1]))
+		debugPrintf("Failed to export '%s'\n", argv[1]);
 
-	Common::String filename = argv[1];
-	filename += ".raw";
-	Common::DumpFile dump;
-	if (!dump.open(filename)) {
-		debugPrintf("Failed to open dump file '%s'\n", filename.c_str());
-		delete[] buf;
-		return true;
-	}
-
-	if (dump.write(buf, size) < size)
-		debugPrintf("Failed to write dump file '%s'\n", filename.c_str());
-
-	dump.close();
-	delete[] buf;
 	return true;
 }
 
diff --git a/engines/nancy/console.h b/engines/nancy/console.h
index 293ba79b81..398e98ad37 100644
--- a/engines/nancy/console.h
+++ b/engines/nancy/console.h
@@ -40,7 +40,7 @@ private:
 	NancyEngine *_vm;
 	bool Cmd_resLoadCal(int argc, const char **argv);
 	bool Cmd_resHexDump(int argc, const char **argv);
-	bool Cmd_resDiskDump(int argc, const char **argv);
+	bool Cmd_exportCif(int argc, const char **argv);
 	bool Cmd_resList(int argc, const char **argv);
 	bool Cmd_resInfo(int argc, const char **argv);
 	bool Cmd_resShowImage(int argc, const char **argv);
diff --git a/engines/nancy/resource.cpp b/engines/nancy/resource.cpp
index 069ade9fba..31ac002d04 100644
--- a/engines/nancy/resource.cpp
+++ b/engines/nancy/resource.cpp
@@ -74,6 +74,8 @@ CifFile::~CifFile() {
 bool CifFile::initialize() {
 	readCifInfo(*_f);
 
+	_cifInfo.name = _name;
+
 	if (_f->eos() || _f->err()) {
 		warning("Error reading from CifFile '%s'", _name.c_str());
 		return false;
@@ -177,6 +179,8 @@ class CifTree {
 public:
 	CifTree(const Common::String &name, const Common::String &ext);
 	virtual ~CifTree() { };
+	virtual uint32 getVersion() const = 0;
+
 	bool initialize();
 	void list(Common::Array<Common::String> &nameList, uint type) const;
 	byte *getCifData(const Common::String &name, ResourceManager::CifInfo &info, uint *size = 0) const;
@@ -225,7 +229,6 @@ bool CifTree::initialize() {
 
 	for (int i = 0; i < infoBlockCount; i++) {
 		CifInfoChain chain;
-		memset(&chain, 0, sizeof(CifInfoChain));
 		readCifInfo(f, chain);
 		_cifInfo.push_back(chain);
 	}
@@ -301,6 +304,7 @@ public:
 protected:
 	virtual uint readHeader(Common::File &f);
 	virtual void readCifInfo(Common::File &f, CifInfoChain &chain);
+	virtual uint32 getVersion() const { return 0x00020000; }
 };
 
 uint CifTree20::readHeader(Common::File &f) {
@@ -329,13 +333,14 @@ void CifTree20::readCifInfo(Common::File &f, CifInfoChain &chain) {
 		error("Failed to read info block from CifTree");
 }
 
-class CifTree21 : public CifTree {
+class CifTree21 : public CifTree20 {
 public:
-	CifTree21(const Common::String &name, const Common::String &ext) : CifTree(name, ext), _hasLongNames(false), _hasOffsetFirst(false) { };
+	CifTree21(const Common::String &name, const Common::String &ext) : CifTree20(name, ext), _hasLongNames(false), _hasOffsetFirst(false) { };
 
 protected:
 	virtual uint readHeader(Common::File &f);
 	virtual void readCifInfo(Common::File &f, CifInfoChain &chain);
+	virtual uint32 getVersion() const { return 0x00020001; }
 
 private:
 	void determineSubtype(Common::File &f);
@@ -462,6 +467,100 @@ const CifTree *CifTree::load(const Common::String &name, const Common::String &e
 	return cifTree;
 }
 
+class CifExporter {
+public:
+	virtual ~CifExporter() { };
+	bool dump(const byte *data, uint32 size, const ResourceManager::CifInfo &info) const;
+
+	static const CifExporter *create(uint32 version);
+
+protected:
+	virtual void writeCifInfo(Common::DumpFile &f, const ResourceManager::CifInfo &info) const = 0;
+	virtual uint32 getVersion() const = 0;
+	virtual void writeHeader(Common::DumpFile &f) const;
+};
+
+void CifExporter::writeHeader(Common::DumpFile &f) const {
+	f.writeString("CIF FILE WayneSikes");
+	f.writeByte(0);
+	f.writeUint32LE(0);
+	uint32 version = getVersion();
+	f.writeUint16LE(version >> 16);
+	f.writeUint16LE(version);
+}
+
+bool CifExporter::dump(const byte *data, uint32 size, const ResourceManager::CifInfo &info) const {
+	Common::DumpFile f;
+	if (!f.open(info.name + ".cif")) {
+		warning("Failed to open export file '%s.cif'", info.name.c_str());
+		return false;
+	}
+
+	writeHeader(f);
+	writeCifInfo(f, info);
+	f.write(data, size);
+
+	if (f.err()) {
+		warning("Error writing to export file '%s.cif'", info.name.c_str());
+		f.close();
+		return false;
+	}
+
+	f.close();
+	return true;
+}
+
+class CifExporter20 : public CifExporter {
+protected:
+	virtual void writeCifInfo(Common::DumpFile &f, const ResourceManager::CifInfo &info) const;
+	uint32 getVersion() const { return 0x00020000; }
+};
+
+void CifExporter20::writeCifInfo(Common::DumpFile &f, const ResourceManager::CifInfo &info) const {
+	f.writeUint16LE(info.width);
+	f.writeUint16LE(info.pitch);
+	f.writeUint16LE(info.height);
+	f.writeByte(info.depth);
+
+	f.writeByte(1);
+	f.writeUint32LE(info.size);
+	f.writeUint32LE(0);
+	f.writeUint32LE(0);
+
+	f.writeByte(info.type);
+}
+
+class CifExporter21 : public CifExporter20 {
+protected:
+	virtual void writeCifInfo(Common::DumpFile &f, const ResourceManager::CifInfo &info) const;
+	uint32 getVersion() const { return 0x00020001; }
+};
+
+void CifExporter21::writeCifInfo(Common::DumpFile &f, const ResourceManager::CifInfo &info) const {
+	for (uint i = 0; i < 32; i++)
+		f.writeByte(0); // TODO
+
+	CifExporter20::writeCifInfo(f, info);
+}
+
+const CifExporter *CifExporter::create(uint32 version) {
+	const CifExporter *exp;
+
+	switch(version) {
+	case 0x00020000:
+		exp = new CifExporter20;
+		break;
+	case 0x00020001:
+		exp = new CifExporter21;
+		break;
+	default:
+		warning("Version %d.%d not supported by CifExporter", version >> 16, version & 0xffff);
+		return 0;
+	}
+
+	return exp;
+}
+
 ResourceManager::ResourceManager(NancyEngine *vm) : _vm(vm) {
 	_dec = new Decompressor;
 }
@@ -505,12 +604,13 @@ bool ResourceManager::getCifInfo(const Common::String &treeName, const Common::S
 	if (cifFile) {
 		cifFile->getCifInfo(info);
 		delete cifFile;
+		return true;
 	}
 
 	const CifTree *cifTree = findCifTree(treeName);
 
 	if (!cifTree)
-		return 0;
+		return false;
 
 	return cifTree->getCifInfo(name, info);
 }
@@ -554,6 +654,28 @@ byte *ResourceManager::loadCif(const Common::String &treeName, const Common::Str
 	return getCifData(treeName, name, info, &size);
 }
 
+bool ResourceManager::exportCif(const Common::String &treeName, const Common::String &name) {
+	CifInfo info;
+	uint size;
+	byte *buf = getCifData(treeName, name, info, &size);
+
+	if (!buf)
+		return false;
+
+	// Find out what CIF version this game uses
+	uint32 version = 0;
+	if (_cifTrees.size() > 0)
+		version = _cifTrees[0]->getVersion();
+
+	bool retval = false;
+	const CifExporter *exp = CifExporter::create(version);
+	if (exp) {
+		retval = exp->dump(buf, size, info);
+		delete exp;
+	}
+	return retval;
+}
+
 byte *ResourceManager::loadData(const Common::String &treeName, const Common::String &name, uint &size) {
 	CifInfo info;
 
@@ -611,12 +733,7 @@ void ResourceManager::list(const Common::String &treeName, Common::Array<Common:
 
 Common::String ResourceManager::getCifDescription(const Common::String &treeName, const Common::String &name) {
 	CifInfo info;
-	const CifTree *cifTree = findCifTree(treeName);
-
-	if (!cifTree)
-		return Common::String::format("Failed to open CifTree '%s'\n", treeName.c_str());
-
-	if (!cifTree->getCifInfo(name, info))
+	if (!getCifInfo(treeName, name, info))
 		return Common::String::format("Couldn't find '%s' in CifTree '%s'\n", name.c_str(), treeName.c_str());
 
 	Common::String desc;
diff --git a/engines/nancy/resource.h b/engines/nancy/resource.h
index a57d956d8c..9aa140ca15 100644
--- a/engines/nancy/resource.h
+++ b/engines/nancy/resource.h
@@ -71,6 +71,7 @@ public:
 	// Debugger functions
 	void list(const Common::String &treeName, Common::Array<Common::String> &nameList, uint type);
 	byte *loadCif(const Common::String &treeName, const Common::String &name, uint &size);
+	bool exportCif(const Common::String &treeName, const Common::String &name);
 	Common::String getCifDescription(const Common::String &treeName, const Common::String &name);
 private:
 	NancyEngine *_vm;


Commit: d5ea0c840aa1316fac0648d944df89fe6399cf87
    https://github.com/scummvm/scummvm/commit/d5ea0c840aa1316fac0648d944df89fe6399cf87
Author: Walter van Niftrik (walter at scummvm.org)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Rename debug commands

Changed paths:
    engines/nancy/console.cpp
    engines/nancy/console.h
    engines/nancy/resource.cpp
    engines/nancy/resource.h


diff --git a/engines/nancy/console.cpp b/engines/nancy/console.cpp
index e50788c822..ccc88894c0 100644
--- a/engines/nancy/console.cpp
+++ b/engines/nancy/console.cpp
@@ -32,12 +32,12 @@
 namespace Nancy {
 
 NancyConsole::NancyConsole(NancyEngine *vm) : GUI::Debugger(), _vm(vm) {
-	registerCmd("res_load_cal", WRAP_METHOD(NancyConsole, Cmd_resLoadCal));
-	registerCmd("res_hexdump", WRAP_METHOD(NancyConsole, Cmd_resHexDump));
-	registerCmd("export_cif", WRAP_METHOD(NancyConsole, Cmd_exportCif));
-	registerCmd("res_list", WRAP_METHOD(NancyConsole, Cmd_resList));
-	registerCmd("res_info", WRAP_METHOD(NancyConsole, Cmd_resInfo));
-	registerCmd("res_show_image", WRAP_METHOD(NancyConsole, Cmd_resShowImage));
+	registerCmd("load_cal", WRAP_METHOD(NancyConsole, Cmd_loadCal));
+	registerCmd("cif_hexdump", WRAP_METHOD(NancyConsole, Cmd_cifHexDump));
+	registerCmd("cif_export", WRAP_METHOD(NancyConsole, Cmd_cifExport));
+	registerCmd("cif_list", WRAP_METHOD(NancyConsole, Cmd_cifList));
+	registerCmd("cif_info", WRAP_METHOD(NancyConsole, Cmd_cifInfo));
+	registerCmd("show_image", WRAP_METHOD(NancyConsole, Cmd_showImage));
 	registerCmd("play_video", WRAP_METHOD(NancyConsole, Cmd_playVideo));
 	registerCmd("play_audio", WRAP_METHOD(NancyConsole, Cmd_playAudio));
 }
@@ -65,7 +65,7 @@ void NancyConsole::postEnter() {
 	}
 }
 
-bool NancyConsole::Cmd_resHexDump(int argc, const char **argv) {
+bool NancyConsole::Cmd_cifHexDump(int argc, const char **argv) {
 	if (argc < 2 || argc > 3) {
 		debugPrintf("Dumps the specified resource to standard output\n");
 		debugPrintf("Usage: %s name [cal]\n", argv[0]);
@@ -84,7 +84,7 @@ bool NancyConsole::Cmd_resHexDump(int argc, const char **argv) {
 	return true;
 }
 
-bool NancyConsole::Cmd_exportCif(int argc, const char **argv) {
+bool NancyConsole::Cmd_cifExport(int argc, const char **argv) {
 	if (argc < 2 || argc > 3) {
 		debugPrintf("Exports the specified resource to .cif file\n");
 		debugPrintf("Usage: %s name [cal]\n", argv[0]);
@@ -97,7 +97,7 @@ bool NancyConsole::Cmd_exportCif(int argc, const char **argv) {
 	return true;
 }
 
-bool NancyConsole::Cmd_resList(int argc, const char **argv) {
+bool NancyConsole::Cmd_cifList(int argc, const char **argv) {
 	if (argc < 2 || argc > 3) {
 		debugPrintf("List resources of a certain type\n");
 		debugPrintf("Types - 0: all, 2: image, 3: script\n");
@@ -118,7 +118,7 @@ bool NancyConsole::Cmd_resList(int argc, const char **argv) {
 	return true;
 }
 
-bool NancyConsole::Cmd_resInfo(int argc, const char **argv) {
+bool NancyConsole::Cmd_cifInfo(int argc, const char **argv) {
 	if (argc < 2 || argc > 3) {
 		debugPrintf("Prints information about a resource\n");
 		debugPrintf("Usage: %s name [cal]\n", argv[0]);
@@ -129,7 +129,7 @@ bool NancyConsole::Cmd_resInfo(int argc, const char **argv) {
 	return true;
 }
 
-bool NancyConsole::Cmd_resShowImage(int argc, const char **argv) {
+bool NancyConsole::Cmd_showImage(int argc, const char **argv) {
 	if (argc < 2 || argc > 3) {
 		debugPrintf("Draws an image on the screen\n");
 		debugPrintf("Usage: %s name [cal]\n", argv[0]);
@@ -148,7 +148,7 @@ bool NancyConsole::Cmd_resShowImage(int argc, const char **argv) {
 	}
 }
 
-bool NancyConsole::Cmd_resLoadCal(int argc, const char **argv) {
+bool NancyConsole::Cmd_loadCal(int argc, const char **argv) {
 	if (argc != 2) {
 		debugPrintf("Loads a .cal file\n");
 		debugPrintf("Usage: %s <name>\n", argv[0]);
diff --git a/engines/nancy/console.h b/engines/nancy/console.h
index 398e98ad37..95f93eef82 100644
--- a/engines/nancy/console.h
+++ b/engines/nancy/console.h
@@ -38,12 +38,12 @@ public:
 
 private:
 	NancyEngine *_vm;
-	bool Cmd_resLoadCal(int argc, const char **argv);
-	bool Cmd_resHexDump(int argc, const char **argv);
-	bool Cmd_exportCif(int argc, const char **argv);
-	bool Cmd_resList(int argc, const char **argv);
-	bool Cmd_resInfo(int argc, const char **argv);
-	bool Cmd_resShowImage(int argc, const char **argv);
+	bool Cmd_loadCal(int argc, const char **argv);
+	bool Cmd_cifHexDump(int argc, const char **argv);
+	bool Cmd_cifExport(int argc, const char **argv);
+	bool Cmd_cifList(int argc, const char **argv);
+	bool Cmd_cifInfo(int argc, const char **argv);
+	bool Cmd_showImage(int argc, const char **argv);
 	bool Cmd_playVideo(int argc, const char **argv);
 	bool Cmd_playAudio(int argc, const char **argv);
 
diff --git a/engines/nancy/resource.cpp b/engines/nancy/resource.cpp
index 31ac002d04..4878df0d00 100644
--- a/engines/nancy/resource.cpp
+++ b/engines/nancy/resource.cpp
@@ -684,8 +684,8 @@ byte *ResourceManager::loadData(const Common::String &treeName, const Common::St
 	if (!buf)
 		return 0;
 
-	if (info.type != kResTypeData) {
-		warning("Resource '%s' is not data", name.c_str());
+	if (info.type != kResTypeScript) {
+		warning("Resource '%s' is not a script", name.c_str());
 		delete[] buf;
 		return 0;
 	}
diff --git a/engines/nancy/resource.h b/engines/nancy/resource.h
index 9aa140ca15..bfd4e398be 100644
--- a/engines/nancy/resource.h
+++ b/engines/nancy/resource.h
@@ -43,7 +43,7 @@ public:
 		kResTypeAny,
 		// Type 1 seems to be obsolete
 		kResTypeImage = 2,
-		kResTypeData
+		kResTypeScript
 	};
 
 	enum ResCompression {


Commit: bd7e83cf8efb23fe10728e8592481e5ef5748eb2
    https://github.com/scummvm/scummvm/commit/bd7e83cf8efb23fe10728e8592481e5ef5748eb2
Author: Walter van Niftrik (walter at scummvm.org)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Skip unsupported video frame types for now

Changed paths:
    engines/nancy/video.cpp
    engines/nancy/video.h


diff --git a/engines/nancy/video.cpp b/engines/nancy/video.cpp
index e5c1122c95..b408edea72 100644
--- a/engines/nancy/video.cpp
+++ b/engines/nancy/video.cpp
@@ -97,7 +97,8 @@ AVFDecoder::AVFVideoTrack::AVFVideoTrack(Common::SeekableReadStream *stream) {
 		info.offset = stream->readUint32LE();
 		info.compressedSize = stream->readUint32LE();
 		info.size = stream->readUint32LE();
-		stream->skip(5); // Unknown
+		info.type = stream->readByte();
+		stream->skip(4); // Unknown;
 		_chunkInfo.push_back(info);
 	}
 }
@@ -114,6 +115,11 @@ const Graphics::Surface *AVFDecoder::AVFVideoTrack::decodeNextFrame() {
 	_fileStream->seek(_chunkInfo[_curFrame].offset);
 	uint size = _chunkInfo[_curFrame].size;
 
+	if (_chunkInfo[_curFrame].type != 0) {
+		warning("Skipping frame type %d", _chunkInfo[_curFrame].type);
+		return _surface;
+	}
+
 	if (_compressed) {
 		Common::ReadStream *input = _fileStream->readStream(_chunkInfo[_curFrame].compressedSize);
 		Common::MemoryWriteStream output((byte *)_surface->getPixels(), size);
diff --git a/engines/nancy/video.h b/engines/nancy/video.h
index 07e0a01f17..f2d1b83246 100644
--- a/engines/nancy/video.h
+++ b/engines/nancy/video.h
@@ -67,6 +67,7 @@ private:
 			uint32 offset;
 			uint32 compressedSize;
 			uint32 size;
+			byte type;
 		};
 
 		bool decodeFrame(byte *rleData, int rleSize, byte *litData, int litSize, byte *dest, int left, int width, int height, int colorKey);


Commit: 2cc91e62acbc6b4fc1463d9fd1a04e8824f59737
    https://github.com/scummvm/scummvm/commit/2cc91e62acbc6b4fc1463d9fd1a04e8824f59737
Author: Walter van Niftrik (walter at scummvm.org)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add support for HIS versions 1.0 and 2.0

Changed paths:
    engines/nancy/audio.cpp


diff --git a/engines/nancy/audio.cpp b/engines/nancy/audio.cpp
index 743d3b6530..bff3fb2341 100644
--- a/engines/nancy/audio.cpp
+++ b/engines/nancy/audio.cpp
@@ -27,70 +27,149 @@
 
 #include "audio/audiostream.h"
 #include "audio/decoders/raw.h"
+#include "audio/decoders/vorbis.h"
 
 namespace Nancy {
 
-Audio::RewindableAudioStream *makeHISStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) {
-	// HIS files are just WAVE files with the first 22 bytes of the file
-	// overwritten with a string
-
-	char buf[22];
+enum SoundType {
+	kSoundTypeRaw,
+	kSoundTypeOgg
+};
+
+bool readWaveHeader(Common::SeekableReadStream *stream, SoundType &type, uint16 &numChannels,
+                    uint32 &samplesPerSec, uint16 &bitsPerSample, uint32 &size) {
+	// The earliest HIS files are just WAVE files with the first 22 bytes of
+	// the file overwritten with a string, so most of this is copied from the
+	// standard WAVE decoder
+	numChannels = stream->readUint16LE();
+	samplesPerSec = stream->readUint32LE();
+	stream->skip(6);
+	bitsPerSample = stream->readUint16LE();
 
-	stream->read(buf, 22);
-	buf[21] = 0;
+	char buf[4 + 1];
+	stream->read(buf, 4);
+	buf[4] = 0;
 
-	if (Common::String(buf) != "Her Interactive Sound") {
-		warning("Invalid header found in HIS file");
-		return 0;
+	if (Common::String(buf) != "data") {
+		warning("Data chunk not found in HIS file");
+		return false;
 	}
 
-	// Most of this is copied from the standard WAVE decoder
-	uint16 numChannels = stream->readUint16LE();
-	uint32 samplesPerSec = stream->readUint32LE();
-	stream->skip(6);
-	uint16 bitsPerSample = stream->readUint16LE();
+	size = stream->readUint32LE();
 
-	byte flags = 0;
-	if (bitsPerSample == 8)		// 8 bit data is unsigned
-		flags |= Audio::FLAG_UNSIGNED;
-	else if (bitsPerSample == 16)	// 16 bit data is signed little endian
-		flags |= (Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN);
-	else {
-		warning("Unsupported bitsPerSample %d found in HIS file", bitsPerSample);
-		return 0;
+	if (stream->eos() || stream->err()) {
+		warning("Error reading HIS file");
+		return false;
 	}
 
-	if (numChannels == 2)
-		flags |= Audio::FLAG_STEREO;
-	else if (numChannels != 1) {
-		warning("Unsupported number of channels %d found in HIS file", numChannels);
-		return 0;
-	}
+	type = kSoundTypeRaw;
 
-	stream->read(buf, 4);
-	buf[4] = 0;
+	return true;
+}
 
-	if (Common::String(buf) != "data") {
-		warning("Data chunk not found in HIS file");
-		return 0;
+bool readHISHeader(Common::SeekableReadStream *stream, SoundType &type, uint16 &numChannels,
+                    uint32 &samplesPerSec, uint16 &bitsPerSample, uint32 &size) {
+	uint32 ver;
+	ver = stream->readUint16LE() << 16;
+	ver |= stream->readUint16LE();
+	bool hasType = false;
+
+	switch (ver) {
+	case 0x00010000:
+		break;
+	case 0x00020000:
+		hasType = true;
+		break;
+	default:
+		warning("Unsupported version %d.%d found in HIS file", ver >> 16, ver & 0xffff);
+		return false;
 	}
 
-	uint32 size = stream->readUint32LE();
+	// Same data as Wave fmt chunk
+	stream->skip(2); // AudioFormat
+	numChannels = stream->readUint16LE();
+	samplesPerSec = stream->readUint32LE();
+	stream->skip(6); // ByteRate and BlockAlign
+	bitsPerSample = stream->readUint16LE();
+
+	size = stream->readUint32LE();
+
+	if (hasType) {
+		uint16 tp = stream->readUint16LE();
+		switch (tp) {
+		case 1:
+			type = kSoundTypeRaw;
+			break;
+		case 2:
+			type = kSoundTypeOgg;
+			break;
+		default:
+			warning("Unsupported sound type %d found in HIS file", tp);
+			return false;
+		}
+	} else
+		type = kSoundTypeRaw;
 
 	if (stream->eos() || stream->err()) {
 		warning("Error reading HIS file");
-		return 0;
+		return false;
+	}
+
+	return true;
+}
+
+Audio::RewindableAudioStream *makeHISStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) {
+	char buf[22];
+
+	stream->read(buf, 22);
+	buf[21] = 0;
+
+	uint16 numChannels, bitsPerSample;
+	uint32 samplesPerSec, size;
+	SoundType type;
+
+	if (Common::String(buf) == "Her Interactive Sound") {
+		// Early HIS file
+		if (!readWaveHeader(stream, type, numChannels, samplesPerSec, bitsPerSample, size))
+			return 0;
+	} else if (Common::String(buf) == "HIS") {
+		stream->seek(4);
+		if (!readHISHeader(stream, type, numChannels, samplesPerSec, bitsPerSample, size))
+			return 0;
 	}
 
-	// Raw PCM, make sure the last packet is complete
-	uint sampleSize = (flags & Audio::FLAG_16BITS ? 2 : 1) * (flags & Audio::FLAG_STEREO ? 2 : 1);
-	if (size % sampleSize != 0) {
-		warning("Trying to play an HIS file with an incomplete PCM packet");
-		size &= ~(sampleSize - 1);
+	byte flags = 0;
+	if (type == kSoundTypeRaw) {
+		if (bitsPerSample == 8)		// 8 bit data is unsigned
+			flags |= Audio::FLAG_UNSIGNED;
+		else if (bitsPerSample == 16)	// 16 bit data is signed little endian
+			flags |= (Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN);
+		else {
+			warning("Unsupported bitsPerSample %d found in HIS file", bitsPerSample);
+			return 0;
+		}
+	
+		if (numChannels == 2)
+			flags |= Audio::FLAG_STEREO;
+		else if (numChannels != 1) {
+			warning("Unsupported number of channels %d found in HIS file", numChannels);
+			return 0;
+		}
+	
+		// Raw PCM, make sure the last packet is complete
+		uint sampleSize = (flags & Audio::FLAG_16BITS ? 2 : 1) * (flags & Audio::FLAG_STEREO ? 2 : 1);
+		if (size % sampleSize != 0) {
+			warning("Trying to play an HIS file with an incomplete PCM packet");
+			size &= ~(sampleSize - 1);
+		}
 	}
 
-	Common::SeekableSubReadStream *subStream = new Common::SeekableSubReadStream(stream, stream->pos(), size, disposeAfterUse);
-	return Audio::makeRawStream(subStream, samplesPerSec, flags);
+	Common::SeekableSubReadStream *subStream = new Common::SeekableSubReadStream(stream, stream->pos(), stream->pos() + size, disposeAfterUse);
+
+	if (type == kSoundTypeRaw)
+		return Audio::makeRawStream(subStream, samplesPerSec, flags, DisposeAfterUse::YES);
+	else
+		return Audio::makeVorbisStream(subStream, DisposeAfterUse::YES);
 }
 
 } // End of namespace Nancy


Commit: 5fa61194059290c0b3363bbf08efed08656f38dd
    https://github.com/scummvm/scummvm/commit/5fa61194059290c0b3363bbf08efed08656f38dd
Author: Walter van Niftrik (walter at scummvm.org)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Return SeekableAudioStream instead of RewindableAudioStream

Changed paths:
    engines/nancy/audio.cpp
    engines/nancy/audio.h


diff --git a/engines/nancy/audio.cpp b/engines/nancy/audio.cpp
index bff3fb2341..5944780be3 100644
--- a/engines/nancy/audio.cpp
+++ b/engines/nancy/audio.cpp
@@ -118,7 +118,7 @@ bool readHISHeader(Common::SeekableReadStream *stream, SoundType &type, uint16 &
 	return true;
 }
 
-Audio::RewindableAudioStream *makeHISStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) {
+Audio::SeekableAudioStream *makeHISStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) {
 	char buf[22];
 
 	stream->read(buf, 22);
diff --git a/engines/nancy/audio.h b/engines/nancy/audio.h
index 8de4ae7ea6..fb1b8dcc78 100644
--- a/engines/nancy/audio.h
+++ b/engines/nancy/audio.h
@@ -29,9 +29,9 @@ class SeekableReadStream;
 
 namespace Nancy {
 
-class RewindableAudioStream;
+class SeekableAudioStream;
 
-Audio::RewindableAudioStream *makeHISStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse);
+Audio::SeekableAudioStream *makeHISStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse);
 
 } // End of namespace Nancy
 


Commit: 0503e651e5b5a4bf8a537e96d3fec9fa7304c3dd
    https://github.com/scummvm/scummvm/commit/0503e651e5b5a4bf8a537e96d3fec9fa7304c3dd
Author: Walter van Niftrik (walter at scummvm.org)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add event polling and timing to play_video debug command

Changed paths:
    engines/nancy/console.cpp


diff --git a/engines/nancy/console.cpp b/engines/nancy/console.cpp
index ccc88894c0..ef2688f3ca 100644
--- a/engines/nancy/console.cpp
+++ b/engines/nancy/console.cpp
@@ -21,6 +21,7 @@
  */
 
 #include "common/system.h"
+#include "common/events.h"
 #include "graphics/surface.h"
 #include "audio/audiostream.h"
 #include "nancy/console.h"
@@ -48,20 +49,32 @@ NancyConsole::~NancyConsole() {
 void NancyConsole::postEnter() {
 	if (!_videoFile.empty()) {
 		Video::VideoDecoder *dec = new AVFDecoder;
-	
+
 		if (dec->loadFile(_videoFile)) {
 			dec->start();
-			while (!dec->endOfVideo()) {
-				const Graphics::Surface *frame = dec->decodeNextFrame();
-				_vm->_system->fillScreen(0);
-				_vm->_system->copyRectToScreen(frame->getPixels(), frame->pitch, 0, 0, frame->w, frame->h);
-				_vm->_system->updateScreen();
-				_vm->_system->delayMillis(60);
+			_vm->_system->fillScreen(0);
+			Common::EventManager *ev = g_system->getEventManager();
+			while (!_vm->shouldQuit() && !dec->endOfVideo()) {
+				Common::Event event;
+				if (ev->pollEvent(event)) {
+					if (event.type == Common::EVENT_KEYDOWN || event.type == Common::EVENT_LBUTTONDOWN)
+						break;
+				}
+
+				if (dec->needsUpdate()) {
+					const Graphics::Surface *frame = dec->decodeNextFrame();
+					if (frame) {
+						_vm->_system->copyRectToScreen(frame->getPixels(), frame->pitch, 0, 0, frame->w, frame->h);
+						_vm->_system->updateScreen();
+					}
+				}
+				_vm->_system->delayMillis(10);
 			}
-		} else {
+		} else
 			debugPrintf("Failed to load '%s'\n", _videoFile.c_str());
-		}
+
 		_videoFile.clear();
+		delete dec;
 	}
 }
 


Commit: f670e5ad4f1d9904556350f3fd5fe3f6b783ca31
    https://github.com/scummvm/scummvm/commit/f670e5ad4f1d9904556350f3fd5fe3f6b783ca31
Author: Walter van Niftrik (walter at scummvm.org)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Load frame time from AVF files

Changed paths:
    engines/nancy/video.cpp
    engines/nancy/video.h


diff --git a/engines/nancy/video.cpp b/engines/nancy/video.cpp
index b408edea72..aea2010165 100644
--- a/engines/nancy/video.cpp
+++ b/engines/nancy/video.cpp
@@ -78,7 +78,7 @@ AVFDecoder::AVFVideoTrack::AVFVideoTrack(Common::SeekableReadStream *stream) {
 	_width = stream->readUint16LE();
 	_height = stream->readUint16LE();
 	_depth = stream->readByte();
-	stream->skip(4); // Unknown
+	_frameTime = stream->readUint32LE();
 
 	byte comp = stream->readByte();
 
@@ -117,7 +117,7 @@ const Graphics::Surface *AVFDecoder::AVFVideoTrack::decodeNextFrame() {
 
 	if (_chunkInfo[_curFrame].type != 0) {
 		warning("Skipping frame type %d", _chunkInfo[_curFrame].type);
-		return _surface;
+		return 0;
 	}
 
 	if (_compressed) {
diff --git a/engines/nancy/video.h b/engines/nancy/video.h
index f2d1b83246..77d3ff9cd9 100644
--- a/engines/nancy/video.h
+++ b/engines/nancy/video.h
@@ -59,7 +59,7 @@ private:
 		const Graphics::Surface *decodeNextFrame();
 
 	protected:
-		Common::Rational getFrameRate() const { return 20; }
+		Common::Rational getFrameRate() const { return Common::Rational(1000, _frameTime); }
 
 	private:
 		struct ChunkInfo {
@@ -78,6 +78,7 @@ private:
 		int _curFrame;
 		uint _frameCount;
 		bool _compressed;
+		uint32 _frameTime;
 		Graphics::Surface *_surface;
 		Common::Array<ChunkInfo> _chunkInfo;
 		Decompressor *_dec;


Commit: 1c93da869979fe6d41a7d20c5298de040c862717
    https://github.com/scummvm/scummvm/commit/1c93da869979fe6d41a7d20c5298de040c862717
Author: Walter van Niftrik (walter at scummvm.org)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add support for video frame types 1 and 2

Changed paths:
    engines/nancy/resource.h
    engines/nancy/video.cpp
    engines/nancy/video.h


diff --git a/engines/nancy/resource.h b/engines/nancy/resource.h
index bfd4e398be..5c4d58d353 100644
--- a/engines/nancy/resource.h
+++ b/engines/nancy/resource.h
@@ -28,7 +28,7 @@ class String;
 }
 
 namespace Graphics {
-class Surface;
+struct Surface;
 }
 
 namespace Nancy {
diff --git a/engines/nancy/video.cpp b/engines/nancy/video.cpp
index aea2010165..84295a7adf 100644
--- a/engines/nancy/video.cpp
+++ b/engines/nancy/video.cpp
@@ -26,6 +26,7 @@
 #include "common/textconsole.h"
 #include "common/debug.h"
 #include "common/memstream.h"
+#include "common/substream.h"
 
 #include "graphics/surface.h"
 
@@ -70,6 +71,7 @@ AVFDecoder::AVFVideoTrack::AVFVideoTrack(Common::SeekableReadStream *stream) {
 	assert(stream);
 	_fileStream = stream;
 	_curFrame = -1;
+	_refFrame = -1;
 	_dec = new Decompressor;
 
 	stream->skip(1); // Unknown
@@ -82,14 +84,13 @@ AVFDecoder::AVFVideoTrack::AVFVideoTrack(Common::SeekableReadStream *stream) {
 
 	byte comp = stream->readByte();
 
-	if (comp != 1 && comp != 2)
+	if (comp != 2)
 		error("Unknown compression type %d found in AVF", comp);
 
-	_compressed = comp == 2;
-
 	_surface = new Graphics::Surface();
 	_pixelFormat = Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0);
 	_surface->create(_width, _height, _pixelFormat);
+	_frameSize = _width * _height * _pixelFormat.bytesPerPixel;
 
 	for (uint i = 0; i < _frameCount; i++) {
 		ChunkInfo info;
@@ -110,28 +111,109 @@ AVFDecoder::AVFVideoTrack::~AVFVideoTrack() {
 	delete _dec;
 }
 
-const Graphics::Surface *AVFDecoder::AVFVideoTrack::decodeNextFrame() {
-	_curFrame++;
-	_fileStream->seek(_chunkInfo[_curFrame].offset);
-	uint size = _chunkInfo[_curFrame].size;
+bool AVFDecoder::AVFVideoTrack::decode(byte *outBuf, uint32 frameSize, Common::ReadStream &inBuf) const {
+	byte cmd = inBuf.readByte();
+	while (!inBuf.eos()) {
+		uint32 len, offset;
+		switch (cmd) {
+		case 0x20:
+			// Write literal block
+			offset = inBuf.readUint32LE() * 2;
+			len = inBuf.readUint32LE() * 2;
+			if (offset + len > frameSize)
+				return false;
+			inBuf.read(outBuf + offset, len);
+			break;
+		case 0x40: {
+			// Write literal value 'n' times
+			uint16 val = inBuf.readUint16LE();
+			offset = inBuf.readUint32LE() * 2;
+			len = inBuf.readUint32LE() * 2;
+			if (offset + len > frameSize)
+				return false;
+			for (uint i = 0; i < len; i += 2)
+				WRITE_LE_UINT16(outBuf + offset + i, val);
+			break;
+		}
+		case 0x80: {
+			// Write literal block 'n' times
+			len = inBuf.readByte() * 2;
+			uint32 offsetCount = inBuf.readUint32LE();
+			byte buf[510];
+
+			inBuf.read(buf, len);
+			for (uint i = 0; i < offsetCount; ++i) {
+				offset = inBuf.readUint32LE() * 2;
+				if (offset + len > frameSize)
+					return false;
+				memcpy(outBuf + offset, buf, len);
+			}
+			break;
+		}
+		default:
+			break;
+		}
+		cmd = inBuf.readByte();
+	}
+	return true;
+}
 
-	if (_chunkInfo[_curFrame].type != 0) {
-		warning("Skipping frame type %d", _chunkInfo[_curFrame].type);
+const Graphics::Surface *AVFDecoder::AVFVideoTrack::decodeFrame(uint frameNr) {
+	if (frameNr >= _chunkInfo.size()) {
+		warning("Frame %d doesn't exist", frameNr);
 		return 0;
 	}
 
-	if (_compressed) {
-		Common::ReadStream *input = _fileStream->readStream(_chunkInfo[_curFrame].compressedSize);
-		Common::MemoryWriteStream output((byte *)_surface->getPixels(), size);
-		if (!_dec->decompress(*input, output))
-			warning("Failed to decompress frame %d in AVF", _curFrame);
-		delete input;
+	const ChunkInfo &info = _chunkInfo[frameNr];
+
+	if (info.type == 2 && (_refFrame == -1 || _refFrame != (int)frameNr - 1)) {
+		warning("Cannot decode frame %d, reference frame is invalid", frameNr);
+		return 0;
+	}
+
+	if (!info.size && !info.compressedSize) {
+		if (info.type != 2) {
+			warning("Found empty frame %d of type %d", frameNr, info.type);
+			return 0;
+		}
+		// Return previous frame
+		_refFrame = frameNr;
+		return _surface;
+	}
+
+	byte *decompBuf = 0;
+	if (info.type == 0) {
+		// For type 0 we decompress straight to the surface, make sure we don't go out of bounds
+		if (info.size > _frameSize) {
+			warning("Decompressed size %d exceeds frame size %d", info.size, _frameSize);
+			return 0;
+		}
 	} else {
-		if (_fileStream->read(_surface->getPixels(), size) < size)
-			warning("Read error trying to copy uncompressed frame");
+		// For types 1 and 2, we decompress to a temp buffer for decoding
+		decompBuf = new byte[info.size];
+	}
+
+	Common::MemoryWriteStream output((info.type == 0 ? (byte *)_surface->getPixels() : decompBuf), info.size);
+	Common::SeekableSubReadStream input(_fileStream, info.offset, info.offset + info.compressedSize);
+
+	if (!_dec->decompress(input, output)) {
+		warning("Failed to decompress frame %d", frameNr);
+		delete[] decompBuf;
+		return 0;
 	}
 
+	if (info.type != 0) {
+		Common::MemoryReadStream decompStr(decompBuf, info.size);
+		decode((byte *)_surface->getPixels(), _frameSize, decompStr);
+	}
+
+	_refFrame = frameNr;
+	delete[] decompBuf;
 	return _surface;
 }
 
+const Graphics::Surface *AVFDecoder::AVFVideoTrack::decodeNextFrame() {
+	return decodeFrame(++_curFrame);
+}
+
 } // End of namespace Nancy
diff --git a/engines/nancy/video.h b/engines/nancy/video.h
index 77d3ff9cd9..e44d566cd0 100644
--- a/engines/nancy/video.h
+++ b/engines/nancy/video.h
@@ -70,16 +70,17 @@ private:
 			byte type;
 		};
 
-		bool decodeFrame(byte *rleData, int rleSize, byte *litData, int litSize, byte *dest, int left, int width, int height, int colorKey);
+		const Graphics::Surface *decodeFrame(uint frameNr);
+		bool decode(byte *outBuf, uint32 frameSize, Common::ReadStream &inBuf) const;
 
 		Common::SeekableReadStream *_fileStream;
 		Graphics::PixelFormat _pixelFormat;
-		uint _width, _height, _depth;
+		uint _width, _height, _depth, _frameSize;
 		int _curFrame;
 		uint _frameCount;
-		bool _compressed;
 		uint32 _frameTime;
 		Graphics::Surface *_surface;
+		int _refFrame;
 		Common::Array<ChunkInfo> _chunkInfo;
 		Decompressor *_dec;
 	};


Commit: c0fe9e83a00fece7280d2b78059dddaf8104129b
    https://github.com/scummvm/scummvm/commit/c0fe9e83a00fece7280d2b78059dddaf8104129b
Author: Walter van Niftrik (walter at scummvm.org)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add IFF parsing

Changed paths:
  A engines/nancy/iff.cpp
  A engines/nancy/iff.h
    engines/nancy/module.mk
    engines/nancy/nancy.cpp


diff --git a/engines/nancy/iff.cpp b/engines/nancy/iff.cpp
new file mode 100644
index 0000000000..9fa3fcf133
--- /dev/null
+++ b/engines/nancy/iff.cpp
@@ -0,0 +1,112 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "nancy/nancy.h"
+#include "nancy/iff.h"
+#include "nancy/resource.h"
+
+#include "common/memstream.h"
+#include "common/iff_container.h"
+#include "common/debug.h"
+
+namespace Nancy {
+
+IFF::~IFF() {
+	for (uint i = 0; i < _chunks.size(); i++)
+		delete[] _chunks[i].buf;
+}
+
+bool IFF::callback(Common::IFFChunk &c) {
+	Chunk chunk;
+	chunk.id = c._type;
+
+	if (chunk.id == ID_DATA) {
+		debugN(3, "IFF::callback: Skipping 'DATA' chunk\n");
+		return false;
+	}
+
+	chunk.size = c._size;
+	chunk.buf = new byte[chunk.size];
+	c._stream->read(chunk.buf, chunk.size);
+
+	if (c._stream->err())
+		error("IFF::callback: error reading '%s' chunk", idToString(chunk.id).c_str());
+
+	debugN(3, "IFF::callback: Adding '%s' chunk\n", idToString(chunk.id).c_str());
+	_chunks.push_back(chunk);
+
+	return false;
+}
+
+bool IFF::load() {
+	byte *data;
+	uint size;
+	data = _vm->_res->loadData("ciftree", _name, size);
+
+	if (!data)
+		return false;
+
+	// Scan the file for DATA chunks, completely ignoring IFF structure
+	// Presumably the string "DATA" is not allowed inside of chunks...
+	uint offset = 0;
+
+	while (offset < size - 3) {
+		uint32 id = READ_BE_UINT32(data + offset);
+		if (id == ID_DATA) {
+			// Replace 'DATA' with standard 'FORM' for the parser
+			WRITE_BE_UINT32(data + offset, ID_FORM);
+			Common::MemoryReadStream stream(data + offset, size - offset);
+			Common::IFFParser iff(&stream);
+			Common::Functor1Mem< Common::IFFChunk &, bool, IFF > c(this, &IFF::callback);
+			iff.parse(c);
+			offset += 16; // Original engine skips 16, while 12 seems more logical
+		} else
+			++offset;
+	}
+
+	delete[] data;
+	return true;
+}
+
+const byte *IFF::getChunk(uint32 id, uint &size) {
+	for (uint i = 0; i < _chunks.size(); ++i) {
+		const Chunk &chunk = _chunks[i];
+		if (chunk.id == id) {
+			size = chunk.size;
+			return chunk.buf;
+		}
+	}
+
+	warning("Failed to locate chunk");
+	return 0;
+}
+
+Common::String IFF::idToString(uint32 id) {
+	Common::String s;
+	while (id) {
+		s += id >> 24;
+		id <<= 8;
+	}
+	return s;
+}
+
+} // End of namespace Nancy
diff --git a/engines/nancy/iff.h b/engines/nancy/iff.h
new file mode 100644
index 0000000000..49950a14a6
--- /dev/null
+++ b/engines/nancy/iff.h
@@ -0,0 +1,64 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/array.h"
+#include "common/str.h"
+
+#ifndef NANCY_SCRIPT_H
+#define NANCY_SCRIPT_H
+
+namespace Common {
+struct IFFChunk;
+}
+
+namespace Nancy {
+
+class NancyEngine;
+
+#define ID_DATA     MKTAG('D', 'A', 'T', 'A')
+
+class IFF {
+public:
+	IFF(NancyEngine *vm, const Common::String &name) : _name(name), _vm(vm) { };
+	~IFF();
+
+	bool load();
+	const byte *getChunk(uint32 id, uint &size);
+
+private:
+	Common::String idToString(uint32 id);
+	bool callback(Common::IFFChunk &chunk);
+
+	struct Chunk {
+		uint32 id;
+		byte *buf;
+		uint32 size;
+	};
+
+	Common::Array<Chunk> _chunks;
+	const Common::String _name;
+	NancyEngine *_vm;
+};
+
+} // End of namespace Nancy
+
+#endif
diff --git a/engines/nancy/module.mk b/engines/nancy/module.mk
index 6b2d75cbec..5fe1dc0f9d 100644
--- a/engines/nancy/module.mk
+++ b/engines/nancy/module.mk
@@ -7,7 +7,8 @@ MODULE_OBJS = \
   resource.o \
   video.o \
   decompress.o \
-  audio.o
+  audio.o \
+  iff.o
 
 # This module can be built as a plugin
 ifeq ($(ENABLE_NANCY), DYNAMIC_PLUGIN)
diff --git a/engines/nancy/nancy.cpp b/engines/nancy/nancy.cpp
index 85d4d4ae7c..9d3e00030a 100644
--- a/engines/nancy/nancy.cpp
+++ b/engines/nancy/nancy.cpp
@@ -30,6 +30,7 @@
 
 #include "nancy/nancy.h"
 #include "nancy/resource.h"
+#include "nancy/iff.h"
 
 #include "engines/util.h"
 
@@ -104,6 +105,11 @@ Common::Error NancyEngine::run() {
 	// Setup mixer
 	syncSoundSettings();
 
+	IFF *boot = new IFF(this, "boot");
+	if (!boot->load())
+		error("Failed to load boot script");
+	delete boot;
+
 	Common::EventManager *ev = g_system->getEventManager();
 	bool quit = false;
 


Commit: d73103df45c6bad4313d97b5fcc41896adf1f4fe
    https://github.com/scummvm/scummvm/commit/d73103df45c6bad4313d97b5fcc41896adf1f4fe
Author: Walter van Niftrik (walter at scummvm.org)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add support for PCAL chunk

Changed paths:
    engines/nancy/iff.cpp
    engines/nancy/iff.h
    engines/nancy/nancy.cpp
    engines/nancy/nancy.h


diff --git a/engines/nancy/iff.cpp b/engines/nancy/iff.cpp
index 9fa3fcf133..4dbdf0db16 100644
--- a/engines/nancy/iff.cpp
+++ b/engines/nancy/iff.cpp
@@ -87,7 +87,7 @@ bool IFF::load() {
 	return true;
 }
 
-const byte *IFF::getChunk(uint32 id, uint &size) {
+const byte *IFF::getChunk(uint32 id, uint &size) const {
 	for (uint i = 0; i < _chunks.size(); ++i) {
 		const Chunk &chunk = _chunks[i];
 		if (chunk.id == id) {
@@ -96,7 +96,6 @@ const byte *IFF::getChunk(uint32 id, uint &size) {
 		}
 	}
 
-	warning("Failed to locate chunk");
 	return 0;
 }
 
diff --git a/engines/nancy/iff.h b/engines/nancy/iff.h
index 49950a14a6..3603c4ca62 100644
--- a/engines/nancy/iff.h
+++ b/engines/nancy/iff.h
@@ -35,6 +35,7 @@ namespace Nancy {
 class NancyEngine;
 
 #define ID_DATA     MKTAG('D', 'A', 'T', 'A')
+#define ID_PCAL     MKTAG('P', 'C', 'A', 'L')
 
 class IFF {
 public:
@@ -42,7 +43,7 @@ public:
 	~IFF();
 
 	bool load();
-	const byte *getChunk(uint32 id, uint &size);
+	const byte *getChunk(uint32 id, uint &size) const;
 
 private:
 	Common::String idToString(uint32 id);
diff --git a/engines/nancy/nancy.cpp b/engines/nancy/nancy.cpp
index 9d3e00030a..a5433b238c 100644
--- a/engines/nancy/nancy.cpp
+++ b/engines/nancy/nancy.cpp
@@ -27,6 +27,7 @@
 #include "common/debug-channels.h"
 #include "common/config-manager.h"
 #include "common/textconsole.h"
+#include "common/memstream.h"
 
 #include "nancy/nancy.h"
 #include "nancy/resource.h"
@@ -108,6 +109,7 @@ Common::Error NancyEngine::run() {
 	IFF *boot = new IFF(this, "boot");
 	if (!boot->load())
 		error("Failed to load boot script");
+	preloadCals(*boot);
 	delete boot;
 
 	Common::EventManager *ev = g_system->getEventManager();
@@ -143,6 +145,35 @@ void NancyEngine::initialize() {
 	_rnd->setSeed(42);                              // Kick random number generator
 }
 
+void NancyEngine::preloadCals(const IFF &boot) {
+	const byte *buf;
+	uint size;
+	buf = boot.getChunk(ID_PCAL, size);
+
+	if (buf) {
+		Common::MemoryReadStream stream(buf, size);
+		uint16 count = stream.readUint16LE();
+		debugC(1, kDebugEngine, "Preloading %d CALs", count);
+		int nameLen = size / count;
+
+		char *name = new char[nameLen];
+
+		for (uint i = 0; i < count; i++) {
+			stream.read(name, nameLen);
+			name[nameLen - 1] = 0;
+			debugC(1, kDebugEngine, "Preloading CAL '%s'", name);
+			if (!_res->loadCifTree(name, "cal"))
+				error("Failed to preload CAL '%s'", name);
+		}
+
+		delete[] name;
+
+		if (stream.err())
+			error("Error reading PCAL chunk");
+	} else
+		debugC(1, kDebugEngine, "No PCAL chunk found");
+}
+
 void NancyEngine::syncSoundSettings() {
 	Engine::syncSoundSettings();
 
diff --git a/engines/nancy/nancy.h b/engines/nancy/nancy.h
index 6a13f68b2e..8bb532b9e5 100644
--- a/engines/nancy/nancy.h
+++ b/engines/nancy/nancy.h
@@ -66,6 +66,7 @@ enum NancyDebugChannels {
 struct NancyGameDescription;
 
 class ResourceManager;
+class IFF;
 
 class NancyEngine : public Engine {
 public:
@@ -108,6 +109,7 @@ private:
 	Common::Platform _platform;
 
 	void initialize();
+	void preloadCals(const IFF &boot);
 };
 
 } // End of namespace Nancy


Commit: 369ab0136a72482040e0abbd844aad50fb270bd8
    https://github.com/scummvm/scummvm/commit/369ab0136a72482040e0abbd844aad50fb270bd8
Author: Walter van Niftrik (walter at scummvm.org)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add debug command for hexdumping chunks

Changed paths:
    engines/nancy/console.cpp
    engines/nancy/console.h


diff --git a/engines/nancy/console.cpp b/engines/nancy/console.cpp
index ef2688f3ca..da10b7eb30 100644
--- a/engines/nancy/console.cpp
+++ b/engines/nancy/console.cpp
@@ -29,6 +29,7 @@
 #include "nancy/resource.h"
 #include "nancy/video.h"
 #include "nancy/audio.h"
+#include "nancy/iff.h"
 
 namespace Nancy {
 
@@ -38,6 +39,7 @@ NancyConsole::NancyConsole(NancyEngine *vm) : GUI::Debugger(), _vm(vm) {
 	registerCmd("cif_export", WRAP_METHOD(NancyConsole, Cmd_cifExport));
 	registerCmd("cif_list", WRAP_METHOD(NancyConsole, Cmd_cifList));
 	registerCmd("cif_info", WRAP_METHOD(NancyConsole, Cmd_cifInfo));
+	registerCmd("chunk_hexdump", WRAP_METHOD(NancyConsole, Cmd_chunkHexDump));
 	registerCmd("show_image", WRAP_METHOD(NancyConsole, Cmd_showImage));
 	registerCmd("play_video", WRAP_METHOD(NancyConsole, Cmd_playVideo));
 	registerCmd("play_audio", WRAP_METHOD(NancyConsole, Cmd_playAudio));
@@ -142,6 +144,40 @@ bool NancyConsole::Cmd_cifInfo(int argc, const char **argv) {
 	return true;
 }
 
+bool NancyConsole::Cmd_chunkHexDump(int argc, const char **argv) {
+	if (argc != 3) {
+		debugPrintf("Hexdumps an IFF chunk\n");
+		debugPrintf("Usage: %s iffname chunkname\n", argv[0]);
+		return true;
+	}
+
+	IFF iff(_vm, argv[1]);
+	if (!iff.load()) {
+		debugPrintf("Failed to load IFF '%s'\n", argv[1]);
+		return true;
+	}
+
+	const byte *buf;
+	uint size;
+
+	uint32 id = 0;
+	for (uint i = 0; i < 4; i++) {
+		char c = argv[2][i];
+		if (!c)
+			break;
+		id |= toupper(c) << (24 - 8 * i);
+	}
+
+	buf = iff.getChunk(id, size);
+	if (!buf) {
+		debugPrintf("Failed to find chunk '%s' in IFF '%s'\n", argv[2], argv[1]);
+		return true;
+	}
+
+	Common::hexdump(buf, size);
+	return true;
+}
+
 bool NancyConsole::Cmd_showImage(int argc, const char **argv) {
 	if (argc < 2 || argc > 3) {
 		debugPrintf("Draws an image on the screen\n");
diff --git a/engines/nancy/console.h b/engines/nancy/console.h
index 95f93eef82..15c3836bfb 100644
--- a/engines/nancy/console.h
+++ b/engines/nancy/console.h
@@ -43,6 +43,7 @@ private:
 	bool Cmd_cifExport(int argc, const char **argv);
 	bool Cmd_cifList(int argc, const char **argv);
 	bool Cmd_cifInfo(int argc, const char **argv);
+	bool Cmd_chunkHexDump(int argc, const char **argv);
 	bool Cmd_showImage(int argc, const char **argv);
 	bool Cmd_playVideo(int argc, const char **argv);
 	bool Cmd_playAudio(int argc, const char **argv);


Commit: b393b234bed5233d8b509a2f822f89cb04b38271
    https://github.com/scummvm/scummvm/commit/b393b234bed5233d8b509a2f822f89cb04b38271
Author: Walter van Niftrik (walter at scummvm.org)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add debugger command for listing chunks inside an IFF

Changed paths:
    engines/nancy/console.cpp
    engines/nancy/console.h
    engines/nancy/iff.cpp
    engines/nancy/iff.h


diff --git a/engines/nancy/console.cpp b/engines/nancy/console.cpp
index da10b7eb30..f0afbb2e49 100644
--- a/engines/nancy/console.cpp
+++ b/engines/nancy/console.cpp
@@ -40,6 +40,7 @@ NancyConsole::NancyConsole(NancyEngine *vm) : GUI::Debugger(), _vm(vm) {
 	registerCmd("cif_list", WRAP_METHOD(NancyConsole, Cmd_cifList));
 	registerCmd("cif_info", WRAP_METHOD(NancyConsole, Cmd_cifInfo));
 	registerCmd("chunk_hexdump", WRAP_METHOD(NancyConsole, Cmd_chunkHexDump));
+	registerCmd("chunk_list", WRAP_METHOD(NancyConsole, Cmd_chunkList));
 	registerCmd("show_image", WRAP_METHOD(NancyConsole, Cmd_showImage));
 	registerCmd("play_video", WRAP_METHOD(NancyConsole, Cmd_playVideo));
 	registerCmd("play_audio", WRAP_METHOD(NancyConsole, Cmd_playAudio));
@@ -160,13 +161,10 @@ bool NancyConsole::Cmd_chunkHexDump(int argc, const char **argv) {
 	const byte *buf;
 	uint size;
 
-	uint32 id = 0;
-	for (uint i = 0; i < 4; i++) {
-		char c = argv[2][i];
-		if (!c)
-			break;
-		id |= toupper(c) << (24 - 8 * i);
-	}
+	char idStr[4] = { ' ', ' ', ' ', ' ' };
+	uint len = strlen(argv[2]);
+	memcpy(idStr, argv[2], (len <= 4 ? len : 4));
+	uint32 id = READ_BE_UINT32(idStr);
 
 	buf = iff.getChunk(id, size);
 	if (!buf) {
@@ -178,6 +176,32 @@ bool NancyConsole::Cmd_chunkHexDump(int argc, const char **argv) {
 	return true;
 }
 
+bool NancyConsole::Cmd_chunkList(int argc, const char **argv) {
+	if (argc != 2) {
+		debugPrintf("List chunks inside an IFF\n");
+		debugPrintf("Usage: %s iffname\n", argv[0]);
+		return true;
+	}
+
+	IFF iff(_vm, argv[1]);
+	if (!iff.load()) {
+		debugPrintf("Failed to load IFF '%s'\n", argv[1]);
+		return true;
+	}
+
+	Common::Array<Common::String> list;
+	iff.list(list);
+	for (uint i = 0; i < list.size(); i++) {
+		debugPrintf("%-6s", list[i].c_str());
+		if ((i % 13) == 12 && i + 1 != list.size())
+			debugPrintf("\n");
+	}
+
+	debugPrintf("\n");
+
+	return true;
+}
+
 bool NancyConsole::Cmd_showImage(int argc, const char **argv) {
 	if (argc < 2 || argc > 3) {
 		debugPrintf("Draws an image on the screen\n");
diff --git a/engines/nancy/console.h b/engines/nancy/console.h
index 15c3836bfb..7d0b48cd65 100644
--- a/engines/nancy/console.h
+++ b/engines/nancy/console.h
@@ -44,6 +44,7 @@ private:
 	bool Cmd_cifList(int argc, const char **argv);
 	bool Cmd_cifInfo(int argc, const char **argv);
 	bool Cmd_chunkHexDump(int argc, const char **argv);
+	bool Cmd_chunkList(int argc, const char **argv);
 	bool Cmd_showImage(int argc, const char **argv);
 	bool Cmd_playVideo(int argc, const char **argv);
 	bool Cmd_playAudio(int argc, const char **argv);
diff --git a/engines/nancy/iff.cpp b/engines/nancy/iff.cpp
index 4dbdf0db16..e13dce2e25 100644
--- a/engines/nancy/iff.cpp
+++ b/engines/nancy/iff.cpp
@@ -37,7 +37,15 @@ IFF::~IFF() {
 
 bool IFF::callback(Common::IFFChunk &c) {
 	Chunk chunk;
-	chunk.id = c._type;
+
+	// Replace invalid NULs with spaces
+	char id[4];
+	WRITE_BE_UINT32(id, c._type);
+	for (uint i = 0; i < 4; ++i) {
+		if (id[i] == 0)
+			id[i] = ' ';
+	}
+	chunk.id = READ_BE_UINT32(id);
 
 	if (chunk.id == ID_DATA) {
 		debugN(3, "IFF::callback: Skipping 'DATA' chunk\n");
@@ -108,4 +116,10 @@ Common::String IFF::idToString(uint32 id) {
 	return s;
 }
 
+void IFF::list(Common::Array<Common::String> &nameList) {
+	for (uint i = 0; i < _chunks.size(); ++i) {
+		nameList.push_back(idToString(_chunks[i].id));
+	}
+}
+
 } // End of namespace Nancy
diff --git a/engines/nancy/iff.h b/engines/nancy/iff.h
index 3603c4ca62..363237b932 100644
--- a/engines/nancy/iff.h
+++ b/engines/nancy/iff.h
@@ -45,6 +45,9 @@ public:
 	bool load();
 	const byte *getChunk(uint32 id, uint &size) const;
 
+	// Debugger functions
+	void list(Common::Array<Common::String> &nameList);
+
 private:
 	Common::String idToString(uint32 id);
 	bool callback(Common::IFFChunk &chunk);


Commit: e89c01713b85bcfe22ca7da08d2cee7cc7aaf143
    https://github.com/scummvm/scummvm/commit/e89c01713b85bcfe22ca7da08d2cee7cc7aaf143
Author: Walter van Niftrik (walter at scummvm.org)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add getting chunks with the same name by index

Changed paths:
    engines/nancy/console.cpp
    engines/nancy/iff.cpp
    engines/nancy/iff.h


diff --git a/engines/nancy/console.cpp b/engines/nancy/console.cpp
index f0afbb2e49..02010318b7 100644
--- a/engines/nancy/console.cpp
+++ b/engines/nancy/console.cpp
@@ -146,9 +146,9 @@ bool NancyConsole::Cmd_cifInfo(int argc, const char **argv) {
 }
 
 bool NancyConsole::Cmd_chunkHexDump(int argc, const char **argv) {
-	if (argc != 3) {
+	if (argc < 3 || argc > 4) {
 		debugPrintf("Hexdumps an IFF chunk\n");
-		debugPrintf("Usage: %s iffname chunkname\n", argv[0]);
+		debugPrintf("Usage: %s iffname chunkname [index]\n", argv[0]);
 		return true;
 	}
 
@@ -165,10 +165,14 @@ bool NancyConsole::Cmd_chunkHexDump(int argc, const char **argv) {
 	uint len = strlen(argv[2]);
 	memcpy(idStr, argv[2], (len <= 4 ? len : 4));
 	uint32 id = READ_BE_UINT32(idStr);
+	uint index = 0;
 
-	buf = iff.getChunk(id, size);
+	if (argc == 4)
+		index = atoi(argv[3]);
+
+	buf = iff.getChunk(id, size, index);
 	if (!buf) {
-		debugPrintf("Failed to find chunk '%s' in IFF '%s'\n", argv[2], argv[1]);
+		debugPrintf("Failed to find chunk '%s' (index %d) in IFF '%s'\n", argv[2], index, argv[1]);
 		return true;
 	}
 
diff --git a/engines/nancy/iff.cpp b/engines/nancy/iff.cpp
index e13dce2e25..4218d522b2 100644
--- a/engines/nancy/iff.cpp
+++ b/engines/nancy/iff.cpp
@@ -95,12 +95,16 @@ bool IFF::load() {
 	return true;
 }
 
-const byte *IFF::getChunk(uint32 id, uint &size) const {
+const byte *IFF::getChunk(uint32 id, uint &size, uint index) const {
+	uint found = 0;
 	for (uint i = 0; i < _chunks.size(); ++i) {
 		const Chunk &chunk = _chunks[i];
 		if (chunk.id == id) {
-			size = chunk.size;
-			return chunk.buf;
+			if (found == index) {
+				size = chunk.size;
+				return chunk.buf;
+			}
+			++found;
 		}
 	}
 
diff --git a/engines/nancy/iff.h b/engines/nancy/iff.h
index 363237b932..a23e0e27da 100644
--- a/engines/nancy/iff.h
+++ b/engines/nancy/iff.h
@@ -43,7 +43,7 @@ public:
 	~IFF();
 
 	bool load();
-	const byte *getChunk(uint32 id, uint &size) const;
+	const byte *getChunk(uint32 id, uint &size, uint index = 0) const;
 
 	// Debugger functions
 	void list(Common::Array<Common::String> &nameList);


Commit: 5decebb72ca005e0c8d1080e39b1953d85d6df74
    https://github.com/scummvm/scummvm/commit/5decebb72ca005e0c8d1080e39b1953d85d6df74
Author: Walter van Niftrik (walter at vanniftrik-it.nl)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add InstallShield detection entries for Nancy 3, 4 and 5

Changed paths:
    engines/nancy/detection.cpp


diff --git a/engines/nancy/detection.cpp b/engines/nancy/detection.cpp
index 2cc161f080..08ca167169 100644
--- a/engines/nancy/detection.cpp
+++ b/engines/nancy/detection.cpp
@@ -106,6 +106,22 @@ static const NancyGameDescription gameDescriptions[] = {
 		},
 		kGameTypeNancy1
 	},
+	{ // MD5 by waltervn
+		{
+			"nancy3", 0,
+			{
+				{"data1.hdr", 0, "44906f3d2242f73f16feb8eb6a5161cb", 207327},
+				{"data1.cab", 0, "e258cc871e5de5ae004d03c4e31431c7", 1555916},
+				{"data2.cab", 0, "364dfd25677026da505f1fa6edd5571f", 137373135},
+				AD_LISTEND
+			},
+			Common::EN_ANY,
+			Common::kPlatformWindows,
+			ADGF_NO_FLAGS,
+			GUIO0()
+		},
+		kGameTypeNancy1
+	},
 	{ // MD5 by waltervn
 		{
 			"nancy4", 0,
@@ -120,6 +136,22 @@ static const NancyGameDescription gameDescriptions[] = {
 		},
 		kGameTypeNancy1
 	},
+	{ // MD5 by waltervn
+		{
+			"nancy4", 0,
+			{
+				{"data1.hdr", 0, "fa4e7a1c411053557169a7731f287012", 263443},
+				{"data1.cab", 0, "8f689f92fcca443d6a03faa5de7e2f1c", 1568756},
+				{"data2.cab", 0, "5525aa428041f3f1421a6fb5d1b8dba1", 140518758},
+				AD_LISTEND
+			},
+			Common::EN_ANY,
+			Common::kPlatformWindows,
+			ADGF_NO_FLAGS,
+			GUIO0()
+		},
+		kGameTypeNancy1
+	},
 	{ // MD5 by waltervn
 		{
 			"nancy5", 0,
@@ -134,6 +166,22 @@ static const NancyGameDescription gameDescriptions[] = {
 		},
 		kGameTypeNancy1
 	},
+	{ // MD5 by waltervn
+		{
+			"nancy5", 0,
+			{
+				{"data1.hdr", 0, "261105fba2a1226eedb090c2ce79fd35", 284091},
+				{"data1.cab", 0, "7d27bb947ef7305831f1faaf1512a598", 1446301},
+				{"data2.cab", 0, "00719c86cab733c1094b27079ce030f3", 145857935},
+				AD_LISTEND
+			},
+			Common::EN_ANY,
+			Common::kPlatformWindows,
+			ADGF_NO_FLAGS,
+			GUIO0()
+		},
+		kGameTypeNancy1
+	},
 	{ // MD5 by Strangerke
 		{
 			"nancy6", 0,


Commit: 3079717143a54ebc07a1d890c4f948c3628a37d1
    https://github.com/scummvm/scummvm/commit/3079717143a54ebc07a1d890c4f948c3628a37d1
Author: Walter van Niftrik (walter at vanniftrik-it.nl)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add InstallShield cab files to SearchMan

Changed paths:
    engines/nancy/nancy.cpp


diff --git a/engines/nancy/nancy.cpp b/engines/nancy/nancy.cpp
index a5433b238c..d716e68798 100644
--- a/engines/nancy/nancy.cpp
+++ b/engines/nancy/nancy.cpp
@@ -28,6 +28,7 @@
 #include "common/config-manager.h"
 #include "common/textconsole.h"
 #include "common/memstream.h"
+#include "common/installshield_cab.h"
 
 #include "nancy/nancy.h"
 #include "nancy/resource.h"
@@ -43,14 +44,6 @@ NancyEngine::NancyEngine(OSystem *syst, const NancyGameDescription *gd) : Engine
 {
 	_system = syst;
 
-	const Common::FSNode gameDataDir(ConfMan.get("path"));
-	SearchMan.addSubDirectoryMatching(gameDataDir, "game");
-	SearchMan.addSubDirectoryMatching(gameDataDir, "datafiles");
-	SearchMan.addSubDirectoryMatching(gameDataDir, "hdsound");
-	SearchMan.addSubDirectoryMatching(gameDataDir, "cdsound");
-	SearchMan.addSubDirectoryMatching(gameDataDir, "hdvideo");
-	SearchMan.addSubDirectoryMatching(gameDataDir, "cdvideo");
-
 	DebugMan.addDebugChannel(kDebugSchedule, "Schedule", "Script Schedule debug level");
 	DebugMan.addDebugChannel(kDebugEngine, "Engine", "Engine debug level");
 	DebugMan.addDebugChannel(kDebugDisplay, "Display", "Display debug level");
@@ -99,6 +92,18 @@ Common::Error NancyEngine::run() {
 	initGraphics(640, 480, true, &format);
 	_console = new NancyConsole(this);
 
+	const Common::FSNode gameDataDir(ConfMan.get("path"));
+	SearchMan.addSubDirectoryMatching(gameDataDir, "game");
+	SearchMan.addSubDirectoryMatching(gameDataDir, "datafiles");
+	SearchMan.addSubDirectoryMatching(gameDataDir, "hdsound");
+	SearchMan.addSubDirectoryMatching(gameDataDir, "cdsound");
+	SearchMan.addSubDirectoryMatching(gameDataDir, "hdvideo");
+	SearchMan.addSubDirectoryMatching(gameDataDir, "cdvideo");
+
+	Common::SeekableReadStream *cabStream = SearchMan.createReadStreamForMember("data1.hdr");
+	if (cabStream)
+		SearchMan.add("data1.hdr", Common::makeInstallShieldArchive(cabStream));
+
 //	_mouse = new MouseHandler(this);
 	_res = new ResourceManager(this);
 	_res->initialize();


Commit: 50e016a826023c3b336ee6d9d71fd9cb5855fcbe
    https://github.com/scummvm/scummvm/commit/50e016a826023c3b336ee6d9d71fd9cb5855fcbe
Author: Walter van Niftrik (walter at vanniftrik-it.nl)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Show LOGO

Changed paths:
    engines/nancy/nancy.cpp


diff --git a/engines/nancy/nancy.cpp b/engines/nancy/nancy.cpp
index d716e68798..5782f12fd2 100644
--- a/engines/nancy/nancy.cpp
+++ b/engines/nancy/nancy.cpp
@@ -30,6 +30,8 @@
 #include "common/memstream.h"
 #include "common/installshield_cab.h"
 
+#include "graphics/surface.h"
+
 #include "nancy/nancy.h"
 #include "nancy/resource.h"
 #include "nancy/iff.h"
@@ -117,6 +119,13 @@ Common::Error NancyEngine::run() {
 	preloadCals(*boot);
 	delete boot;
 
+	// As we can't actually run anything yet, just show LOGO
+	Graphics::Surface surf;
+	if (_res->loadImage("ciftree", "LOGO", surf)) {
+		_system->copyRectToScreen(surf.getPixels(), surf.pitch, 0, 0, surf.w, surf.h);
+		surf.free();
+	}
+
 	Common::EventManager *ev = g_system->getEventManager();
 	bool quit = false;
 


Commit: e5646013b6c9603152c8375469006b1120d1c4f1
    https://github.com/scummvm/scummvm/commit/e5646013b6c9603152c8375469006b1120d1c4f1
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add md5s for another nancy5 version

Changed paths:
    engines/nancy/detection.cpp


diff --git a/engines/nancy/detection.cpp b/engines/nancy/detection.cpp
index 08ca167169..0954f5e2bb 100644
--- a/engines/nancy/detection.cpp
+++ b/engines/nancy/detection.cpp
@@ -182,6 +182,22 @@ static const NancyGameDescription gameDescriptions[] = {
 		},
 		kGameTypeNancy1
 	},
+	{ // MD5 by clone2727
+		{
+			"nancy5", 0,
+			{
+				{"data1.hdr", 0, "258e27792fa7cc7a7125fd74d89f8487", 284091},
+				{"data1.cab", 0, "70433b30b6114031d54d0c991ad44577", 1446301},
+				{"data2.cab", 0, "66f47e4f5e6d431f815aa5250eb044bc", 145857937},
+				AD_LISTEND
+			},
+			Common::EN_ANY,
+			Common::kPlatformWindows,
+			ADGF_NO_FLAGS,
+			GUIO0()
+		},
+		kGameTypeNancy1
+	},
 	{ // MD5 by Strangerke
 		{
 			"nancy6", 0,


Commit: 3655241864a02d7ec2dc00367b51e1ea8d00cb73
    https://github.com/scummvm/scummvm/commit/3655241864a02d7ec2dc00367b51e1ea8d00cb73
Author: Walter van Niftrik (walter at vanniftrik-it.nl)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add loading of BOOT/FR* and BOOT/LG*

Changed paths:
    engines/nancy/detection.cpp
    engines/nancy/iff.cpp
    engines/nancy/iff.h
    engines/nancy/nancy.cpp
    engines/nancy/nancy.h


diff --git a/engines/nancy/detection.cpp b/engines/nancy/detection.cpp
index 0954f5e2bb..ebb5081f09 100644
--- a/engines/nancy/detection.cpp
+++ b/engines/nancy/detection.cpp
@@ -90,7 +90,7 @@ static const NancyGameDescription gameDescriptions[] = {
 			ADGF_NO_FLAGS,
 			GUIO0()
 		},
-		kGameTypeNancy1
+		kGameTypeNancy2
 	},
 	{ // MD5 by waltervn
 		{
@@ -104,7 +104,7 @@ static const NancyGameDescription gameDescriptions[] = {
 			ADGF_NO_FLAGS,
 			GUIO0()
 		},
-		kGameTypeNancy1
+		kGameTypeNancy3
 	},
 	{ // MD5 by waltervn
 		{
@@ -120,7 +120,7 @@ static const NancyGameDescription gameDescriptions[] = {
 			ADGF_NO_FLAGS,
 			GUIO0()
 		},
-		kGameTypeNancy1
+		kGameTypeNancy3
 	},
 	{ // MD5 by waltervn
 		{
@@ -134,7 +134,7 @@ static const NancyGameDescription gameDescriptions[] = {
 			ADGF_NO_FLAGS,
 			GUIO0()
 		},
-		kGameTypeNancy1
+		kGameTypeNancy3
 	},
 	{ // MD5 by waltervn
 		{
@@ -150,7 +150,7 @@ static const NancyGameDescription gameDescriptions[] = {
 			ADGF_NO_FLAGS,
 			GUIO0()
 		},
-		kGameTypeNancy1
+		kGameTypeNancy3
 	},
 	{ // MD5 by waltervn
 		{
@@ -164,7 +164,7 @@ static const NancyGameDescription gameDescriptions[] = {
 			ADGF_NO_FLAGS,
 			GUIO0()
 		},
-		kGameTypeNancy1
+		kGameTypeNancy3
 	},
 	{ // MD5 by waltervn
 		{
@@ -180,7 +180,7 @@ static const NancyGameDescription gameDescriptions[] = {
 			ADGF_NO_FLAGS,
 			GUIO0()
 		},
-		kGameTypeNancy1
+		kGameTypeNancy3
 	},
 	{ // MD5 by clone2727
 		{
@@ -196,7 +196,7 @@ static const NancyGameDescription gameDescriptions[] = {
 			ADGF_NO_FLAGS,
 			GUIO0()
 		},
-		kGameTypeNancy1
+		kGameTypeNancy3
 	},
 	{ // MD5 by Strangerke
 		{
@@ -210,7 +210,7 @@ static const NancyGameDescription gameDescriptions[] = {
 			ADGF_NO_FLAGS,
 			GUIO0()
 		},
-		kGameTypeNancy1
+		kGameTypeNancy3
 	},
 	{AD_TABLE_END_MARKER, kGameTypeNone}
 };
@@ -241,7 +241,7 @@ public:
 
 bool NancyMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *gd) const {
 	if (gd) {
-		*engine = new NancyEngine(syst, (const NancyGameDescription *)gd);
+		*engine = NancyEngine::create(((const NancyGameDescription *)gd)->gameType, syst, (const NancyGameDescription *)gd);
 		((NancyEngine *)*engine)->initGame((const NancyGameDescription *)gd);
 	}
 	return gd != 0;
diff --git a/engines/nancy/iff.cpp b/engines/nancy/iff.cpp
index 4218d522b2..0c1cbaeb75 100644
--- a/engines/nancy/iff.cpp
+++ b/engines/nancy/iff.cpp
@@ -111,6 +111,19 @@ const byte *IFF::getChunk(uint32 id, uint &size, uint index) const {
 	return 0;
 }
 
+Common::SeekableReadStream *IFF::getChunkStream(Common::String id, uint index) const {
+	uint size;
+	const byte *chunk = getChunk(stringToId(id), size, index);
+
+	if (chunk) {
+		byte *dup = (byte *)malloc(size);
+		memcpy(dup, chunk, size);
+		return new Common::MemoryReadStream(dup, size, DisposeAfterUse::YES);
+	}
+
+	return nullptr;
+}
+
 Common::String IFF::idToString(uint32 id) {
 	Common::String s;
 	while (id) {
@@ -120,6 +133,15 @@ Common::String IFF::idToString(uint32 id) {
 	return s;
 }
 
+uint32 IFF::stringToId(const Common::String &s) {
+	uint32 id = 0;
+
+	for (uint i = 0; i < 4; ++i)
+		id |= (s.size() > i ? s[i] : ' ') << (3 - i) * 8;
+
+	return id;
+}
+
 void IFF::list(Common::Array<Common::String> &nameList) {
 	for (uint i = 0; i < _chunks.size(); ++i) {
 		nameList.push_back(idToString(_chunks[i].id));
diff --git a/engines/nancy/iff.h b/engines/nancy/iff.h
index a23e0e27da..9adcfe617f 100644
--- a/engines/nancy/iff.h
+++ b/engines/nancy/iff.h
@@ -28,6 +28,7 @@
 
 namespace Common {
 struct IFFChunk;
+class SeekableReadStream;
 }
 
 namespace Nancy {
@@ -44,12 +45,15 @@ public:
 
 	bool load();
 	const byte *getChunk(uint32 id, uint &size, uint index = 0) const;
+	Common::SeekableReadStream *getChunkStream(Common::String id, uint index = 0) const;
 
 	// Debugger functions
 	void list(Common::Array<Common::String> &nameList);
 
 private:
-	Common::String idToString(uint32 id);
+	static Common::String idToString(uint32 id);
+	static uint32 stringToId(const Common::String &s);
+
 	bool callback(Common::IFFChunk &chunk);
 
 	struct Chunk {
diff --git a/engines/nancy/nancy.cpp b/engines/nancy/nancy.cpp
index 5782f12fd2..ec498f3020 100644
--- a/engines/nancy/nancy.cpp
+++ b/engines/nancy/nancy.cpp
@@ -42,7 +42,10 @@ namespace Nancy {
 
 NancyEngine *NancyEngine::s_Engine = 0;
 
-NancyEngine::NancyEngine(OSystem *syst, const NancyGameDescription *gd) : Engine(syst), _gameDescription(gd)
+NancyEngine::NancyEngine(OSystem *syst, const NancyGameDescription *gd) :
+	Engine(syst),
+	_gameDescription(gd),
+	_bsum(nullptr)
 {
 	_system = syst;
 
@@ -117,11 +120,15 @@ Common::Error NancyEngine::run() {
 	if (!boot->load())
 		error("Failed to load boot script");
 	preloadCals(*boot);
+	_bsum = boot->getChunkStream("BSUM");
+	if (!_bsum)
+		error("Failed to load BOOT BSUM");
+	readBootSummary(*boot);
 	delete boot;
 
-	// As we can't actually run anything yet, just show LOGO
+	// Show logo
 	Graphics::Surface surf;
-	if (_res->loadImage("ciftree", "LOGO", surf)) {
+	if (_res->loadImage("ciftree", _logos[0].name, surf)) {
 		_system->copyRectToScreen(surf.getPixels(), surf.pitch, 0, 0, surf.w, surf.h);
 		surf.free();
 	}
@@ -198,6 +205,92 @@ Common::String NancyEngine::getSavegameFilename(int slot) {
 	return _targetName + Common::String::format("-%02d.SAV", slot);
 }
 
+void NancyEngine::readImageList(const IFF &boot, const Common::String &prefix, ImageList &list) {
+	byte count = _bsum->readByte();
+	debugC(1, kDebugEngine, "Found %i %s images", count, prefix.c_str());
 
+	for (int i = 0; i < count; ++i) {
+		Common::String chunkName = Common::String::format("%s%d", prefix.c_str(), i);
+		Common::SeekableReadStream *chunkStream = boot.getChunkStream(chunkName);
+
+		if (!chunkStream)
+			error("Failed to read BOOT %s", chunkName.c_str());
+
+		Image image;
+		char buf[kMaxFilenameLen + 1];
+		int read = chunkStream->read(buf, getFilenameLen());
+		buf[read] = 0;
+
+		image.name = Common::String(buf);
+		chunkStream->skip(1);
+		image.width = chunkStream->readUint16LE();
+		image.height = chunkStream->readUint16LE();
+
+		if (chunkStream->err())
+			error("Error reading %s%d", prefix.c_str(), i);
+
+		debugC(1, kDebugEngine, "Adding %s (%dx%d)", image.name.c_str(), image.width, image.height);
+		list.push_back(image);
+
+		delete chunkStream;
+	}
+}
+
+class NancyEngine_v0 : public NancyEngine {
+public:
+	NancyEngine_v0(OSystem *syst, const NancyGameDescription *gd) : NancyEngine(syst, gd) { }
+
+private:
+	virtual uint getFilenameLen() const { return 9; }
+	virtual void readBootSummary(const IFF &boot);
+};
+
+void NancyEngine_v0::readBootSummary(const IFF &boot) {
+	_bsum->seek(0x151);
+	readImageList(boot, "FR", _frames);
+	readImageList(boot, "LG", _logos);
+}
+
+class NancyEngine_v1 : public NancyEngine_v0 {
+public:
+	NancyEngine_v1(OSystem *syst, const NancyGameDescription *gd) : NancyEngine_v0(syst, gd) { }
+
+private:
+	virtual void readBootSummary(const IFF &boot);
+};
+
+void NancyEngine_v1::readBootSummary(const IFF &boot) {
+	_bsum->seek(0x14b);
+	readImageList(boot, "FR", _frames);
+	readImageList(boot, "LG", _logos);
+}
+
+class NancyEngine_v2 : public NancyEngine_v1 {
+public:
+	NancyEngine_v2(OSystem *syst, const NancyGameDescription *gd) : NancyEngine_v1(syst, gd) { }
+
+private:
+	virtual uint getFilenameLen() const { return 32; }
+	virtual void readBootSummary(const IFF &boot);
+};
+
+void NancyEngine_v2::readBootSummary(const IFF &boot) {
+	_bsum->seek(0xa7);
+	readImageList(boot, "FR", _frames);
+	readImageList(boot, "LG", _logos);
+}
+
+NancyEngine *NancyEngine::create(GameType type, OSystem *syst, const NancyGameDescription *gd) {
+	switch(type) {
+	case kGameTypeNancy1:
+		return new NancyEngine_v0(syst, gd);
+	case kGameTypeNancy2:
+		return new NancyEngine_v1(syst, gd);
+	case kGameTypeNancy3:
+		return new NancyEngine_v2(syst, gd);
+	default:
+		error("Unknown GameType");
+	}
+}
 
 } // End of namespace Nancy
diff --git a/engines/nancy/nancy.h b/engines/nancy/nancy.h
index 8bb532b9e5..54133eb9be 100644
--- a/engines/nancy/nancy.h
+++ b/engines/nancy/nancy.h
@@ -45,9 +45,10 @@ namespace Nancy {
 static const int kSavegameVersion = 1;
 
 enum GameType {
-	kGameTypeNone  = 0,
+	kGameTypeNone = 0,
 	kGameTypeNancy1,
-	kGameTypeNancy2
+	kGameTypeNancy2,
+	kGameTypeNancy3
 };
 
 enum NancyDebugChannels {
@@ -96,11 +97,33 @@ public:
 	Common::String getSavegameFilename(int slot);
 	void syncSoundSettings();
 
-protected:
+	static NancyEngine *create(GameType type, OSystem *syst, const NancyGameDescription *gd);
 
+protected:
 	// Engine APIs
 	Common::Error run();
 
+	enum {
+		kMaxFilenameLen = 32
+	};
+
+	struct Image {
+		Common::String name;
+		uint16 width;
+		uint16 height;
+	};
+
+	typedef Common::Array<Image> ImageList;
+
+	Common::SeekableReadStream *_bsum;
+	ImageList _logos, _frames;
+
+	void preloadCals(const IFF &boot);
+	void readImageList(const IFF &boot, const Common::String &prefix, ImageList &list);
+
+	virtual uint getFilenameLen() const = 0;
+	virtual void readBootSummary(const IFF &boot) = 0;
+
 private:
 	static NancyEngine *s_Engine;
 
@@ -109,7 +132,6 @@ private:
 	Common::Platform _platform;
 
 	void initialize();
-	void preloadCals(const IFF &boot);
 };
 
 } // End of namespace Nancy


Commit: af8078ceb8f1ed7b0ce18b3f5675f54dcdce6e2d
    https://github.com/scummvm/scummvm/commit/af8078ceb8f1ed7b0ce18b3f5675f54dcdce6e2d
Author: Walter van Niftrik (walter at vanniftrik-it.nl)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add loading and playing of BOOT/MSND

Changed paths:
    engines/nancy/audio.h
    engines/nancy/nancy.cpp
    engines/nancy/nancy.h


diff --git a/engines/nancy/audio.h b/engines/nancy/audio.h
index fb1b8dcc78..23e75bced5 100644
--- a/engines/nancy/audio.h
+++ b/engines/nancy/audio.h
@@ -27,9 +27,11 @@ namespace Common {
 class SeekableReadStream;
 }
 
-namespace Nancy {
-
+namespace Audio {
 class SeekableAudioStream;
+}
+
+namespace Nancy {
 
 Audio::SeekableAudioStream *makeHISStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse);
 
diff --git a/engines/nancy/nancy.cpp b/engines/nancy/nancy.cpp
index ec498f3020..e3930f9b25 100644
--- a/engines/nancy/nancy.cpp
+++ b/engines/nancy/nancy.cpp
@@ -32,9 +32,13 @@
 
 #include "graphics/surface.h"
 
+#include "audio/mixer.h"
+#include "audio/audiostream.h"
+
 #include "nancy/nancy.h"
 #include "nancy/resource.h"
 #include "nancy/iff.h"
+#include "nancy/audio.h"
 
 #include "engines/util.h"
 
@@ -116,16 +120,29 @@ Common::Error NancyEngine::run() {
 	// Setup mixer
 	syncSoundSettings();
 
+	// Some bits and pieces of the engine in order to make something happen
 	IFF *boot = new IFF(this, "boot");
 	if (!boot->load())
 		error("Failed to load boot script");
 	preloadCals(*boot);
+	readSound(*boot, "MSND", _menuSound);
 	_bsum = boot->getChunkStream("BSUM");
 	if (!_bsum)
 		error("Failed to load BOOT BSUM");
 	readBootSummary(*boot);
 	delete boot;
 
+	// Play music
+	Common::SeekableReadStream *mSnd = SearchMan.createReadStreamForMember(_menuSound.name + ".his");
+	if (mSnd) {
+		Audio::RewindableAudioStream *aStr = makeHISStream(mSnd, DisposeAfterUse::YES);
+		if (aStr) {
+			Audio::AudioStream *aStrLoop = Audio::makeLoopingAudioStream(aStr, 0);
+			Audio::SoundHandle handle;
+			_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &handle, aStrLoop);
+		}
+	}
+
 	// Show logo
 	Graphics::Surface surf;
 	if (_res->loadImage("ciftree", _logos[0].name, surf)) {
@@ -205,6 +222,13 @@ Common::String NancyEngine::getSavegameFilename(int slot) {
 	return _targetName + Common::String::format("-%02d.SAV", slot);
 }
 
+Common::String NancyEngine::readFilename(Common::ReadStream *stream) const {
+	char buf[kMaxFilenameLen + 1];
+	int read = stream->read(buf, getFilenameLen());
+	buf[read] = 0;
+	return Common::String(buf);
+}
+
 void NancyEngine::readImageList(const IFF &boot, const Common::String &prefix, ImageList &list) {
 	byte count = _bsum->readByte();
 	debugC(1, kDebugEngine, "Found %i %s images", count, prefix.c_str());
@@ -217,11 +241,7 @@ void NancyEngine::readImageList(const IFF &boot, const Common::String &prefix, I
 			error("Failed to read BOOT %s", chunkName.c_str());
 
 		Image image;
-		char buf[kMaxFilenameLen + 1];
-		int read = chunkStream->read(buf, getFilenameLen());
-		buf[read] = 0;
-
-		image.name = Common::String(buf);
+		image.name = readFilename(chunkStream);
 		chunkStream->skip(1);
 		image.width = chunkStream->readUint16LE();
 		image.height = chunkStream->readUint16LE();
@@ -236,6 +256,15 @@ void NancyEngine::readImageList(const IFF &boot, const Common::String &prefix, I
 	}
 }
 
+void NancyEngine::readSound(const IFF &boot, const Common::String &name, NancyEngine::Sound &sound) {
+	Common::SeekableReadStream *stream = boot.getChunkStream(name);
+
+	if (!stream)
+		error("Failed to read BOOT %s", name.c_str());
+
+	sound.name = readFilename(stream);
+}
+
 class NancyEngine_v0 : public NancyEngine {
 public:
 	NancyEngine_v0(OSystem *syst, const NancyGameDescription *gd) : NancyEngine(syst, gd) { }
diff --git a/engines/nancy/nancy.h b/engines/nancy/nancy.h
index 54133eb9be..b0048e4c03 100644
--- a/engines/nancy/nancy.h
+++ b/engines/nancy/nancy.h
@@ -113,13 +113,20 @@ protected:
 		uint16 height;
 	};
 
+	struct Sound {
+		Common::String name;
+	};
+
 	typedef Common::Array<Image> ImageList;
 
 	Common::SeekableReadStream *_bsum;
 	ImageList _logos, _frames;
+	Sound _menuSound;
 
 	void preloadCals(const IFF &boot);
 	void readImageList(const IFF &boot, const Common::String &prefix, ImageList &list);
+	void readSound(const IFF &boot, const Common::String &name, Sound &sound);
+	Common::String readFilename(Common::ReadStream *stream) const;
 
 	virtual uint getFilenameLen() const = 0;
 	virtual void readBootSummary(const IFF &boot) = 0;


Commit: 0284d2172fe07ad7b3a8176c33839de267641a73
    https://github.com/scummvm/scummvm/commit/0284d2172fe07ad7b3a8176c33839de267641a73
Author: Walter van Niftrik (walter at vanniftrik-it.nl)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: WIP

Changed paths:
  A engines/nancy/logo.cpp
  A engines/nancy/logo.h
    engines/nancy/detection.cpp
    engines/nancy/module.mk
    engines/nancy/nancy.cpp
    engines/nancy/nancy.h
    engines/nancy/resource.cpp
    engines/nancy/resource.h


diff --git a/engines/nancy/detection.cpp b/engines/nancy/detection.cpp
index ebb5081f09..5ef2f33db0 100644
--- a/engines/nancy/detection.cpp
+++ b/engines/nancy/detection.cpp
@@ -41,7 +41,7 @@ uint32 NancyEngine::getFeatures() const {
 }
 
 const char *NancyEngine::getGameId() const {
-	return _gameDescription->desc.gameid;
+	return _gameDescription->desc.gameId;
 }
 
 
@@ -327,8 +327,10 @@ SaveStateDescriptor NancyMetaEngine::querySaveMetaInfos(const char *target, int
 
 		SaveStateDescriptor desc(slot, saveName);
 
-		Graphics::Surface *const thumbnail = Graphics::loadThumbnail(*file);
-		desc.setThumbnail(thumbnail);
+		Graphics::Surface *thumbnail = nullptr;
+
+		if (Graphics::loadThumbnail(*file, thumbnail))
+			desc.setThumbnail(thumbnail);
 
 		desc.setDeletableFlag(true);
 		desc.setWriteProtectedFlag(false);
diff --git a/engines/nancy/logo.cpp b/engines/nancy/logo.cpp
new file mode 100644
index 0000000000..7c3603c1c3
--- /dev/null
+++ b/engines/nancy/logo.cpp
@@ -0,0 +1,105 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/error.h"
+#include "common/system.h"
+#include "common/events.h"
+
+#include "audio/audiostream.h"
+#include "audio/mixer.h"
+
+#include "graphics/surface.h"
+
+#include "nancy/logo.h"
+#include "nancy/nancy.h"
+#include "nancy/resource.h"
+#include "nancy/audio.h"
+
+namespace Nancy {
+
+void LogoSequence::doIt() {
+	switch(_state) {
+	case kInit:
+		init();
+		break;
+	case kStartSound:
+		startSound();
+		break;
+	case kRunning:
+		run();
+		break;
+	case kStop:
+		stop();
+	}
+}
+
+void LogoSequence::init() {
+	_surf = new Graphics::Surface;
+
+	if (!_engine->_res->loadImage("ciftree", _engine->_logos[0].name, *_surf))
+		error("Failed to load %s", _engine->_logos[0].name.c_str());
+
+	_state = kStartSound;
+}
+
+void LogoSequence::startSound() {
+	Common::SeekableReadStream *mSnd = SearchMan.createReadStreamForMember(_engine->_menuSound.name + ".his");
+	if (mSnd) {
+		Audio::RewindableAudioStream *aStr = makeHISStream(mSnd, DisposeAfterUse::YES);
+		if (aStr) {
+			Audio::AudioStream *aStrLoop = Audio::makeLoopingAudioStream(aStr, 0);
+			Audio::SoundHandle handle;
+			_engine->_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &handle, aStrLoop);
+		}
+	}
+
+	_startTicks = _engine->_system->getMillis();
+	_state = kRunning;
+}
+
+void LogoSequence::run() {
+	switch(_runState) {
+	case kBlit:
+		_engine->_system->copyRectToScreen(_surf->getPixels(), _surf->pitch, 0, 0, _surf->w, _surf->h);
+		_runState = kWait;
+		break;
+	case kWait:
+		int buttons = g_system->getEventManager()->getButtonState();
+
+		if (_engine->_system->getMillis() - _startTicks >= 7000 || (buttons & Common::EventManager::LBUTTON))
+			_state = kStop;
+	}
+}
+
+void LogoSequence::stop() {
+	_surf->free();
+	delete _surf;
+
+	// The original engine checks for N+D and N+C key combos here.
+	// For the N+C key combo it looks for some kind of cheat file
+	// to initialize the game state with.
+
+	_engine->_gameFlow.minGameState = NancyEngine::kIdle;
+	_engine->_system->fillScreen(0);
+}
+
+} // End of namespace Nancy
diff --git a/engines/nancy/logo.h b/engines/nancy/logo.h
new file mode 100644
index 0000000000..fb0a082d9b
--- /dev/null
+++ b/engines/nancy/logo.h
@@ -0,0 +1,72 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef NANCY_LOGO_H
+#define NANCY_LOGO_H
+
+namespace Graphics {
+	class Surface;
+}
+
+namespace Nancy {
+
+class NancyEngine;
+
+class LogoSequence {
+public:
+	LogoSequence(NancyEngine *engine) :
+		_engine(engine),
+		_state(kInit),
+		_runState(kBlit),
+		_startTicks(0),
+		_surf(nullptr) { }
+	void doIt();
+
+private:
+	void init();
+	void startSound();
+	void run();
+	void stop();
+
+	enum State {
+		kInit,
+		kStartSound,
+		kRunning,
+		kStop
+	};
+
+	enum RunState {
+		// First four states are related to testing mode
+		kBlit = 4,
+		kWait
+	};
+
+	NancyEngine *_engine;
+	State _state;
+	RunState _runState;
+	uint _startTicks;
+	Graphics::Surface *_surf;
+};
+
+} // End of namespace Nancy
+
+#endif // NANCY_LOGO_H
diff --git a/engines/nancy/module.mk b/engines/nancy/module.mk
index 5fe1dc0f9d..4b3d16ac47 100644
--- a/engines/nancy/module.mk
+++ b/engines/nancy/module.mk
@@ -1,14 +1,15 @@
 MODULE := engines/nancy
 
 MODULE_OBJS = \
+  audio.o \
   console.o \
-  nancy.o \
+  decompress.o \
   detection.o \
+  iff.o \
+  logo.o \
+  nancy.o \
   resource.o \
-  video.o \
-  decompress.o \
-  audio.o \
-  iff.o
+  video.o
 
 # This module can be built as a plugin
 ifeq ($(ENABLE_NANCY), DYNAMIC_PLUGIN)
diff --git a/engines/nancy/nancy.cpp b/engines/nancy/nancy.cpp
index e3930f9b25..accab2c23c 100644
--- a/engines/nancy/nancy.cpp
+++ b/engines/nancy/nancy.cpp
@@ -39,6 +39,7 @@
 #include "nancy/resource.h"
 #include "nancy/iff.h"
 #include "nancy/audio.h"
+#include "nancy/logo.h"
 
 #include "engines/util.h"
 
@@ -65,6 +66,7 @@ NancyEngine::NancyEngine(OSystem *syst, const NancyGameDescription *gd) :
 	DebugMan.addDebugChannel(kDebugMusic, "Music", "Music debug level");
 
 	_console = new NancyConsole(this);
+	_logoSequence = new LogoSequence(this);
 	_rnd = 0;
 }
 
@@ -97,8 +99,9 @@ Common::Platform NancyEngine::getPlatform() const {
 
 Common::Error NancyEngine::run() {
 	s_Engine = this;
+
 	Graphics::PixelFormat format(2, 5, 5, 5, 0, 10, 5, 0, 0);
-	initGraphics(640, 480, true, &format);
+	initGraphics(640, 480, &format);
 	_console = new NancyConsole(this);
 
 	const Common::FSNode gameDataDir(ConfMan.get("path"));
@@ -132,28 +135,21 @@ Common::Error NancyEngine::run() {
 	readBootSummary(*boot);
 	delete boot;
 
-	// Play music
-	Common::SeekableReadStream *mSnd = SearchMan.createReadStreamForMember(_menuSound.name + ".his");
-	if (mSnd) {
-		Audio::RewindableAudioStream *aStr = makeHISStream(mSnd, DisposeAfterUse::YES);
-		if (aStr) {
-			Audio::AudioStream *aStrLoop = Audio::makeLoopingAudioStream(aStr, 0);
-			Audio::SoundHandle handle;
-			_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &handle, aStrLoop);
-		}
-	}
-
-	// Show logo
-	Graphics::Surface surf;
-	if (_res->loadImage("ciftree", _logos[0].name, surf)) {
-		_system->copyRectToScreen(surf.getPixels(), surf.pitch, 0, 0, surf.w, surf.h);
-		surf.free();
-	}
-
 	Common::EventManager *ev = g_system->getEventManager();
 	bool quit = false;
 
+	_gameFlow.minGameState = kLogo;
+
 	while (!shouldQuit() && !quit) {
+		switch(_gameFlow.minGameState) {
+		case kLogo:
+			_logoSequence->doIt();
+			break;
+		case kIdle:
+		default:
+			break;
+		}
+
 		Common::Event event;
 		if (ev->pollEvent(event)) {
 			if (event.type == Common::EVENT_KEYDOWN && (event.kbd.flags & Common::KBD_CTRL)) {
@@ -168,10 +164,29 @@ Common::Error NancyEngine::run() {
 				}
 			}
 		}
-		_console->onFrame();
+
 		_system->updateScreen();
 		_system->delayMillis(16);
 	}
+#if 0
+	// Play music
+	Common::SeekableReadStream *mSnd = SearchMan.createReadStreamForMember(_menuSound.name + ".his");
+	if (mSnd) {
+		Audio::RewindableAudioStream *aStr = makeHISStream(mSnd, DisposeAfterUse::YES);
+		if (aStr) {
+			Audio::AudioStream *aStrLoop = Audio::makeLoopingAudioStream(aStr, 0);
+			Audio::SoundHandle handle;
+			_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &handle, aStrLoop);
+		}
+	}
+
+	// Show logo
+	Graphics::Surface surf;
+	if (_res->loadImage("ciftree", _logos[0].name, surf)) {
+		_system->copyRectToScreen(surf.getPixels(), surf.pitch, 0, 0, surf.w, surf.h);
+		surf.free();
+	}
+#endif
 
 	return Common::kNoError;
 }
diff --git a/engines/nancy/nancy.h b/engines/nancy/nancy.h
index b0048e4c03..5b61e15839 100644
--- a/engines/nancy/nancy.h
+++ b/engines/nancy/nancy.h
@@ -25,7 +25,9 @@
 
 #include "engines/engine.h"
 #include "common/file.h"
+
 #include "nancy/console.h"
+#include "nancy/logo.h"
 
 namespace Common {
 class RandomSource;
@@ -68,9 +70,12 @@ struct NancyGameDescription;
 
 class ResourceManager;
 class IFF;
+class LogoSequence;
 
 class NancyEngine : public Engine {
 public:
+	friend class LogoSequence;
+
 	NancyEngine(OSystem *syst, const NancyGameDescription *gd);
 	~NancyEngine();
 
@@ -117,11 +122,22 @@ protected:
 		Common::String name;
 	};
 
+	enum GameState {
+		kBoot,
+		kLogo,
+		kIdle
+	};
+
+	struct GameFlow {
+		GameState minGameState;
+	};
+
 	typedef Common::Array<Image> ImageList;
 
 	Common::SeekableReadStream *_bsum;
 	ImageList _logos, _frames;
 	Sound _menuSound;
+	GameFlow _gameFlow;
 
 	void preloadCals(const IFF &boot);
 	void readImageList(const IFF &boot, const Common::String &prefix, ImageList &list);
@@ -138,6 +154,8 @@ private:
 	GameType _gameType;
 	Common::Platform _platform;
 
+	LogoSequence *_logoSequence;
+
 	void initialize();
 };
 
diff --git a/engines/nancy/resource.cpp b/engines/nancy/resource.cpp
index 4878df0d00..63cea43692 100644
--- a/engines/nancy/resource.cpp
+++ b/engines/nancy/resource.cpp
@@ -722,6 +722,10 @@ bool ResourceManager::loadImage(const Common::String &treeName, const Common::St
 	return true;
 }
 
+void ResourceManager::freeImage(Graphics::Surface &surf) {
+	delete[] (byte *)surf.getPixels();
+}
+
 void ResourceManager::list(const Common::String &treeName, Common::Array<Common::String> &nameList, uint type) {
 	const CifTree *cifTree = findCifTree(treeName);
 
diff --git a/engines/nancy/resource.h b/engines/nancy/resource.h
index 5c4d58d353..f7b53ad38e 100644
--- a/engines/nancy/resource.h
+++ b/engines/nancy/resource.h
@@ -66,6 +66,7 @@ public:
 	void initialize();
 	bool loadCifTree(const Common::String &name, const Common::String &ext);
 	bool loadImage(const Common::String &treeName, const Common::String &name, Graphics::Surface &surf);
+	void freeImage(Graphics::Surface &surf);
 	byte *loadData(const Common::String &treeName, const Common::String &name, uint &size);
 
 	// Debugger functions


Commit: d97ca12d6cd279af71e75a4d87d0c6455b8c5760
    https://github.com/scummvm/scummvm/commit/d97ca12d6cd279af71e75a4d87d0c6455b8c5760
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Initial commit

Changed paths:
  A engines/nancy/action/actionrecord.h
  A engines/nancy/action/arfactory_v1.cpp
  A engines/nancy/action/recordtypes.cpp
  A engines/nancy/action/recordtypes.h
  A engines/nancy/datatypes.cpp
  A engines/nancy/datatypes.h
  A engines/nancy/detection.h
  A engines/nancy/graphics.cpp
  A engines/nancy/graphics.h
  A engines/nancy/logic.cpp
  A engines/nancy/logic.h
  A engines/nancy/menu.cpp
  A engines/nancy/menu.h
  A engines/nancy/metaengine.cpp
  A engines/nancy/playstate.h
  A engines/nancy/scene.cpp
  A engines/nancy/scene.h
  A engines/nancy/time.h
    engines/nancy/audio.cpp
    engines/nancy/audio.h
    engines/nancy/console.cpp
    engines/nancy/console.h
    engines/nancy/decompress.cpp
    engines/nancy/decompress.h
    engines/nancy/detection.cpp
    engines/nancy/iff.cpp
    engines/nancy/iff.h
    engines/nancy/logo.cpp
    engines/nancy/logo.h
    engines/nancy/module.mk
    engines/nancy/nancy.cpp
    engines/nancy/nancy.h
    engines/nancy/resource.cpp
    engines/nancy/resource.h
    engines/nancy/video.cpp
    engines/nancy/video.h


diff --git a/engines/nancy/action/actionrecord.h b/engines/nancy/action/actionrecord.h
new file mode 100644
index 0000000000..7fe2cd0595
--- /dev/null
+++ b/engines/nancy/action/actionrecord.h
@@ -0,0 +1,100 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef NANCY_ACTION_ACTIONRECORD_H
+#define NANCY_ACTION_ACTIONRECORD_H
+
+#include "common/str.h"
+#include "common/stream.h"
+
+namespace Nancy {
+
+class NancyEngine;
+    
+enum DependencyType : byte {
+    kNone               = 0,
+    kInventory          = 1,
+    kEventFlag          = 2,
+    // ...
+    kPlayTime           = 4,
+    kSceneTime          = 5,
+    kSceneCount         = 9,
+    // ...
+    kTimerNotDone       = 13,
+    kTimerDone          = 14,
+    kDifficultyLevel    = 15
+};
+
+struct DependencyRecord {
+    DependencyType type;    // 0x00
+    byte label;             // 0x01
+    byte condition;         // 0x02
+    bool orFlag;            // 0x03
+    int16 hours;            // 0x04
+    int16 minutes;          // 0x06
+    int16 seconds;          // 0x08
+    int16 milliseconds;     // 0x0A
+};
+
+class ActionRecord {
+public:
+    enum ExecutionState { Start }; // TODO has 4 states
+    ActionRecord() :
+        type(0),
+        execType(0),
+        dependencies(nullptr),
+        numDependencies(0),
+        hasNoDependencies(0),
+        hasSatisfiedCondition(false),
+        isDone(false),
+        state(ExecutionState::Start) {}
+    virtual ~ActionRecord() { delete[] dependencies; delete rawData; }
+
+    virtual uint16 readData(Common::SeekableReadStream &stream) =0;
+    virtual void execute(NancyEngine *engine) {};
+
+protected:
+    // TODO these are temporary until every data class is figured out
+    uint16 readRaw(Common::SeekableReadStream &stream, uint16 bytes) {
+        rawData = new byte[bytes];
+        stream.read(rawData, bytes);
+        return bytes;
+    }
+    byte *rawData = nullptr;
+
+public:
+    Common::String description;     // 0x00
+    byte type;                      // 0x30
+    byte execType;                  // 0x31
+    // 0x32 data
+    DependencyRecord *dependencies; // 0x36
+    byte numDependencies;           // 0x3A
+    bool hasNoDependencies;         // 0x3B, not sure abt this one
+    bool hasSatisfiedCondition;     // 0x3C
+    int32 timers[12];               // 0x48, not sure how many there can be
+    bool isDone;                    // 0x84
+    ExecutionState state;           // 0x91
+    };
+
+} // End of namespace Nancy
+
+#endif // NANCY_ACTION_ACTIONRECORD_H
\ No newline at end of file
diff --git a/engines/nancy/action/arfactory_v1.cpp b/engines/nancy/action/arfactory_v1.cpp
new file mode 100644
index 0000000000..47e291286a
--- /dev/null
+++ b/engines/nancy/action/arfactory_v1.cpp
@@ -0,0 +1,139 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/nancy/logic.h"
+#include "engines/nancy/action/recordtypes.h"
+#include "engines/nancy/action/actionrecord.h"
+
+namespace Nancy {
+
+// TODO put this function in a subclass
+ActionRecord *Logic::createActionRecord(uint16 type) {
+    type -= 0xA;
+    switch (type) {
+        case 0x00:
+            return new Hot1FrSceneChange();
+        case 0x01:
+            return new HotMultiframeSceneChange();
+        case 0x02:
+            return new SceneChange();
+        case 0x03:
+            return new HotMultiframeMultisceneChange();
+        case 0x04:
+            return new Hot1frExitSceneChange();
+        case 0x0C:
+            return new StartFrameNextScene();
+        case 0x14:
+            return new StartStopPlayerScrolling(); // TODO
+        case 0x15:
+            return new StartStopPlayerScrolling(); // TODO
+        case 0x28:
+            return new PlayPrimaryVideoChan0();
+        case 0x29:
+            return new PlaySecondaryVideoChan0();
+        case 0x2A:
+            return new PlaySecondaryVideoChan1();
+        case 0x2B:
+            return new PlaySecondaryMovie();
+        case 0x2C:
+            return new PlayStaticBitmapAnimation();
+        case 0x2D:
+            return new PlayIntStaticBitmapAnimation();
+        case 0x32:
+            return new MapCall();
+        case 0x33:
+            return new MapCallHot1Fr();
+        case 0x34:
+            return new MapCallHotMultiframe();
+        case 0x35:
+            return new MapLocationAccess();
+        case 0x38:
+            return new MapSound();
+        case 0x39:
+            return new MapAviOverride();
+        case 0x3A:
+            return new MapAviOverrideOff();
+        case 0x41:
+            return new TextBoxWrite();
+        case 0x42:
+            return new TextBoxClear();
+        case 0x5A:
+            return new BumpPlayerClock();
+        case 0x5B:
+            return new SaveContinueGame();
+        case 0x5C:
+            return new TurnOffMainRendering();
+        case 0x5D:
+            return new TurnOnMainRendering();
+        case 0x5E:
+            return new ResetAndStartTimer();
+        case 0x5F:
+            return new StopTimer();
+        case 0x60:
+            return new EventFlagsMultiHS();
+        case 0x61:
+            return new EventFlags();
+        case 0x62:
+            return new OrderingPuzzle();
+        case 0x63:
+            return new LoseGame();
+        case 0x64:
+            return new PushScene();
+        case 0x65:
+            return new PopScene();
+        case 0x66:
+            return new WinGame();
+        case 0x67:
+            return new DifficultyLevel();
+        case 0x68:
+            return new RotatingLockPuzzle();
+        case 0x69:
+            return new LeverPuzzle();
+        case 0x6A:
+            return new Telephone();
+        case 0x6B:
+            return new SliderPuzzle();
+        case 0x6C:
+            return new PasswordPuzzle();
+        case 0x6E:
+            return new AddInventoryNoHS();
+        case 0x6F:
+            return new RemoveInventoryNoHS();
+        case 0x70:
+            return new ShowInventoryItem();
+        case 0x8C:
+            return new PlayDigiSoundAndDie(); // TODO
+        case 0x8D:
+            return new PlayDigiSoundAndDie(); // TODO
+        case 0x8E:
+            return new PlaySoundPanFrameAnchorAndDie();
+        case 0x8F:
+            return new PlaySoundMultiHS();
+        case 0x96:
+            return new HintSystem();
+        default:
+            error("Action Record type %i is invalid!", type+0xA);
+            return nullptr;
+    }
+}
+
+} // End of namespace Nancy
\ No newline at end of file
diff --git a/engines/nancy/action/recordtypes.cpp b/engines/nancy/action/recordtypes.cpp
new file mode 100644
index 0000000000..03fc7de6ab
--- /dev/null
+++ b/engines/nancy/action/recordtypes.cpp
@@ -0,0 +1,399 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/nancy/action/recordtypes.h"
+#include "engines/nancy/nancy.h"
+#include "engines/nancy/scene.h"
+#include "engines/nancy/logic.h"
+#include "engines/nancy/nancy.h"
+
+namespace Nancy {
+
+uint16 Hot1FrSceneChange::readData(Common::SeekableReadStream &stream) {
+    return readRaw(stream, 0x1A); // TODO
+}
+
+uint16 HotMultiframeSceneChange::readData(Common::SeekableReadStream &stream) {
+    stream.seek(8, SEEK_CUR);
+    int16 size = stream.readSint16LE() * 0x12 + 0xA;
+    stream.seek(-10, SEEK_CUR);
+
+    return readRaw(stream, size); // TODO
+}
+
+uint16 SceneChange::readData(Common::SeekableReadStream &stream) {
+    return readRaw(stream, 0x8); // TODO
+}
+
+uint16 HotMultiframeMultisceneChange::readData(Common::SeekableReadStream &stream) {
+    stream.seek(0x14, SEEK_CUR);
+    uint size = stream.readUint16LE() * 0x12 + 0x16;
+    stream.seek(-0x16, SEEK_CUR);
+
+    return readRaw(stream, size); // TODO
+}
+
+uint16 Hot1frExitSceneChange::readData(Common::SeekableReadStream &stream) {
+    return readRaw(stream, 0x1A); // TODO, could be same struct as Hot1FrSceneChange
+}
+
+uint16 StartFrameNextScene::readData(Common::SeekableReadStream &stream) {
+    return readRaw(stream, 0x4); // TODO
+}
+
+uint16 StartStopPlayerScrolling::readData(Common::SeekableReadStream &stream) {
+    type = stream.readByte();    
+    return 1;
+}
+
+uint16 PlayPrimaryVideoChan0::readData(Common::SeekableReadStream &stream) {
+    uint16 bytesRead = stream.pos();
+    stream.read(videoData, 0x69C);
+
+    uint16 numResponses = stream.readUint16LE();
+    if (numResponses > 0) {
+        for (uint i = 0; i < numResponses; ++i) {
+            uint16 numConditionFlags = stream.readUint32LE();
+            responses.push_back(ResponseStruct());
+            ResponseStruct &response = responses[responses.size()-1];
+
+            if (numConditionFlags > 0) {
+                for (uint16 j = 0; j < numConditionFlags; ++j) {
+                    response.conditionFlags.push_back(ConditionFlags());
+                    ConditionFlags &flags = response.conditionFlags[response.conditionFlags.size()-1];
+                    stream.read(flags.unknown, 5);
+                }
+            }
+
+            stream.read(response.unknown, 0x1D8);
+        }
+    }
+
+    uint16 numSceneBranchStructs = stream.readUint16LE();
+    if (numSceneBranchStructs > 0) {
+        // TODO
+    }
+
+    uint16 numFlagsStructs = stream.readUint16LE();
+    if (numFlagsStructs > 0)  {
+        for (uint16 i = 0; i < numFlagsStructs; ++i) {
+            uint16 numConditionFlags = stream.readUint16LE();
+            flagsStructs.push_back(FlagsStruct());
+            FlagsStruct &flagsStruct = flagsStructs[flagsStructs.size()-1];
+
+            if (numConditionFlags > 0) {
+                // Not sure about this
+                if (numConditionFlags > 0) {
+                for (uint16 j = 0; j < numConditionFlags; ++j) {
+                    flagsStruct.conditionFlags.push_back(ConditionFlags());
+                    ConditionFlags &flags = flagsStruct.conditionFlags[flagsStruct.conditionFlags.size()-1];
+                    stream.read(flags.unknown, 5);
+                }
+            }
+            }
+
+            flagsStruct.unknown = stream.readUint32LE();
+        }
+    }
+
+    bytesRead = stream.pos() - bytesRead;
+    return bytesRead;
+}
+
+uint16 PlaySecondaryVideo::readData(Common::SeekableReadStream &stream) {
+    stream.seek(0x33, SEEK_CUR);
+    int16 size = stream.readSint16LE() * 0x42 + 0x35;
+    stream.seek(-0x35, SEEK_CUR);
+
+    return readRaw(stream, size); // TODO
+}
+
+uint16 PlaySecondaryMovie::readData(Common::SeekableReadStream &stream) {
+    stream.seek(0xD2, SEEK_CUR);
+    uint16 size = stream.readUint16LE() * 0x42 + 0xD4;
+    stream.seek(-0xD4, SEEK_CUR);
+
+    return readRaw(stream, size); // TODO
+}
+
+uint16 PlayStaticBitmapAnimation::readData(Common::SeekableReadStream &stream) {
+    // TODO
+    uint16 bytesRead = stream.pos();
+    byte *seek = bitmapData;
+    
+    uint16 currentSize = 0x72;
+    stream.read(seek, currentSize);
+
+    seek += currentSize;
+    currentSize = (uint16)(bitmapData[0x18]) - (uint16)(bitmapData[0x16]);
+    ++currentSize;
+    currentSize *= 16;
+    stream.read(seek, currentSize);
+
+    seek += 0x252;
+    currentSize = (uint16)(bitmapData[0x70]);
+    currentSize *= 34;
+    stream.read(seek, currentSize);
+
+    bytesRead= stream.pos() - bytesRead;
+    return bytesRead;
+}
+
+uint16 PlayIntStaticBitmapAnimation::readData(Common::SeekableReadStream &stream) {
+    // TODO
+    uint16 bytesRead = stream.pos();
+    byte *seek = bitmapData;
+    
+    uint16 currentSize = 0x76;
+    stream.read(seek, currentSize);
+
+    seek += currentSize;
+    currentSize = (uint16)(bitmapData[0x18]) - (uint16)(bitmapData[0x16]);
+    ++currentSize;
+    currentSize *= 16;
+    stream.read(seek, currentSize);
+
+    seek += 0x256;
+    currentSize = (uint16)(bitmapData[0x74]);
+    currentSize *= 34;
+    stream.read(seek, currentSize);
+
+    bytesRead= stream.pos() - bytesRead;
+    return bytesRead;
+}
+
+uint16 MapCall::readData(Common::SeekableReadStream &stream) {
+    mapData = stream.readByte();
+    return 1;
+}
+
+uint16 MapCallHot1Fr::readData(Common::SeekableReadStream &stream) {
+    return readRaw(stream, 0x12); // TODO
+}
+
+uint16 MapCallHotMultiframe::readData(Common::SeekableReadStream &stream) {
+    uint16 size = stream.readUint16LE() * 0x12 + 0x2;
+    stream.seek(-2, SEEK_CUR);
+
+    return readRaw(stream, size); // TODO
+}
+
+uint16 MapLocationAccess::readData(Common::SeekableReadStream &stream) {
+    return readRaw(stream, 0x4); // TODO
+}
+
+uint16 MapSound::readData(Common::SeekableReadStream &stream) {
+    return readRaw(stream, 0x10); // TODO
+}
+
+uint16 MapAviOverride::readData(Common::SeekableReadStream &stream) {
+    return readRaw(stream, 0x2); // TODO
+}
+
+uint16 MapAviOverrideOff::readData(Common::SeekableReadStream &stream) {
+    overrideOffData = stream.readByte();
+    return 1;
+}
+
+uint16 TextBoxWrite::readData(Common::SeekableReadStream &stream) {
+    uint16 size = stream.readUint16LE();
+    stream.seek(-2, SEEK_CUR);
+
+    if (size > 0x2710) {
+        error("Action Record atTextboxWrite has too many text box chars: %d", size);;
+    }
+
+    return readRaw(stream, size+2); // TODO
+}
+
+uint16 TextBoxClear::readData(Common::SeekableReadStream &stream) {
+    clearData = stream.readByte();
+    return 1;
+}
+
+uint16 BumpPlayerClock::readData(Common::SeekableReadStream &stream) {
+    return readRaw(stream, 0x5); // TODO
+}
+
+uint16 SaveContinueGame::readData(Common::SeekableReadStream &stream) {
+    saveContinueData = stream.readByte();
+    return 1;
+}
+
+uint16 TurnOffMainRendering::readData(Common::SeekableReadStream &stream) {
+    turnOffData = stream.readByte();
+    return 1;
+}
+
+uint16 TurnOnMainRendering::readData(Common::SeekableReadStream &stream) {
+    turnOnData = stream.readByte();
+    return 1;
+}
+
+uint16 ResetAndStartTimer::readData(Common::SeekableReadStream &stream) {
+    stream.skip(1);
+    return 1;
+}
+
+void ResetAndStartTimer::execute(NancyEngine *engine) {
+    engine->sceneManager->playState.timerIsActive = true;
+    engine->sceneManager->playState.timerTime = 0;
+    isDone = true;
+}
+
+uint16 StopTimer::readData(Common::SeekableReadStream &stream) {
+    stream.skip(1);
+    return 1;
+}
+
+void StopTimer::execute(NancyEngine *engine) {
+    engine->sceneManager->playState.timerIsActive = false;
+    engine->sceneManager->playState.timerTime = 0;
+    isDone = true;
+}
+
+uint16 EventFlagsMultiHS::readData(Common::SeekableReadStream &stream) {
+    stream.seek(0x28, SEEK_CUR);
+    uint16 size = stream.readUint16LE() * 0x12 + 0x2A;
+    stream.seek(-0x2A, SEEK_CUR);
+
+    return readRaw(stream, size); // TODO
+}
+
+uint16 EventFlags::readData(Common::SeekableReadStream &stream) {
+    for (uint i = 0; i < 10; ++i) {
+        descs[i].label = stream.readSint16LE();
+        descs[i].flag = (PlayState::Flag)(stream.readUint16LE());
+    }
+    return 0x28;
+}
+
+void EventFlags::execute(NancyEngine *engine) {
+    for (uint i = 0; i < 10; ++i) {
+        if (descs[i].label != -1) {
+            engine->sceneManager->playState.eventFlags[descs[i].label] = descs[i].flag;
+        }
+    }
+    isDone = true;
+}
+
+uint16 OrderingPuzzle::readData(Common::SeekableReadStream &stream) {
+    return readRaw(stream, 0x26D); // TODO
+}
+
+uint16 LoseGame::readData(Common::SeekableReadStream &stream) {
+    loseData = stream.readByte();
+    return 1;
+}
+
+uint16 PushScene::readData(Common::SeekableReadStream &stream) {
+    pushData = stream.readByte();
+    return 1;
+}
+
+uint16 PopScene::readData(Common::SeekableReadStream &stream) {
+    popData = stream.readByte();
+    return 1;
+}
+
+uint16 WinGame::readData(Common::SeekableReadStream &stream) {
+    winData = stream.readByte();
+    return 1;
+}
+
+uint16 LeverPuzzle::readData(Common::SeekableReadStream &stream) {
+    return readRaw(stream, 0x192); // TODO
+}
+
+uint16 Telephone::readData(Common::SeekableReadStream &stream) {
+    rawData = new byte[0x2016];
+    stream.read(rawData, 0x48C);
+
+    int32 sizeNext = (int16)(rawData[0x48A]) * 235;
+    stream.read(rawData + 0x48C, sizeNext);
+    return sizeNext + 0x48C;
+}
+
+uint16 SliderPuzzle::readData(Common::SeekableReadStream &stream) {
+    return readRaw(stream, 0x544); // TODO
+}
+
+uint16 PasswordPuzzle::readData(Common::SeekableReadStream &stream) {
+    return readRaw(stream, 0xD7); // TODO
+}
+
+uint16 AddInventoryNoHS::readData(Common::SeekableReadStream &stream) {
+    return readRaw(stream, 0x2); // TODO
+}
+
+uint16 RemoveInventoryNoHS::readData(Common::SeekableReadStream &stream) {
+    return readRaw(stream, 0x2); // TODO
+}
+
+uint16 DifficultyLevel::readData(Common::SeekableReadStream &stream) {
+    difficulty = stream.readUint16LE();
+    flagLabel = stream.readSint16LE();
+    flagCondition = stream.readUint16LE();
+    return 6;
+}
+
+void DifficultyLevel::execute(NancyEngine *engine) {
+    engine->sceneManager->playState.difficulty = difficulty;
+    if (flagLabel != -1) {
+        engine->sceneManager->playState.eventFlags[flagLabel] = (PlayState::Flag)flagCondition;
+    }
+    isDone = true;
+}
+
+uint16 RotatingLockPuzzle::readData(Common::SeekableReadStream &stream) {
+    return readRaw(stream, 0x2A4); // TODO
+}
+
+uint16 ShowInventoryItem::readData(Common::SeekableReadStream &stream) {
+    stream.seek(0xC, SEEK_CUR);
+    uint16 size = stream.readUint16LE() * 0x22 + 0xE;
+    stream.seek(-0xE, SEEK_CUR);
+
+    return readRaw(stream, size); // TODO
+}
+
+uint16 PlayDigiSoundAndDie::readData(Common::SeekableReadStream &stream) {
+    return readRaw(stream, 0x2B); // TODO
+}
+
+uint16 PlaySoundPanFrameAnchorAndDie::readData(Common::SeekableReadStream &stream) {
+    return readRaw(stream, 0x20); // TODO
+}
+
+uint16 PlaySoundMultiHS::readData(Common::SeekableReadStream &stream) {
+    stream.seek(0x2F, SEEK_CUR);
+    uint16 size = stream.readUint16LE() * 0x12 + 0x31;
+    stream.seek(-0x31, SEEK_CUR);
+
+    return readRaw(stream, size); // TODO
+}
+
+uint16 HintSystem::readData(Common::SeekableReadStream &stream) {
+    return readRaw(stream, 0x23); // TODO
+}
+
+} // End of namespace Nancy
\ No newline at end of file
diff --git a/engines/nancy/action/recordtypes.h b/engines/nancy/action/recordtypes.h
new file mode 100644
index 0000000000..b5d9a45b24
--- /dev/null
+++ b/engines/nancy/action/recordtypes.h
@@ -0,0 +1,344 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef NANCY_ACTION_RECORDTYPES_H
+#define NANCY_ACTION_RECORDTYPES_H
+
+#include "engines/nancy/action/actionrecord.h"
+#include "engines/nancy/playstate.h"
+
+#include "common/stream.h"
+#include "common/array.h"
+
+namespace Nancy {
+
+class NancyEngine;
+
+class Hot1FrSceneChange : public ActionRecord {
+public:
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+};
+
+class HotMultiframeSceneChange : public ActionRecord {
+public:
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+};
+
+class SceneChange : public ActionRecord {
+public:
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+};
+
+class HotMultiframeMultisceneChange : public ActionRecord {
+public:
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+};
+
+class Hot1frExitSceneChange : public ActionRecord {
+public:
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+};
+
+class StartFrameNextScene : public ActionRecord {
+public:
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+};
+
+class StartStopPlayerScrolling : public ActionRecord {
+public:
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    // TODO add a Start and Stop subclass
+
+    byte type = 0;
+};
+
+class PlayPrimaryVideoChan0 : public ActionRecord {
+
+struct ConditionFlags {
+    byte unknown[5];
+};
+
+struct ResponseStruct {
+    Common::Array<ConditionFlags> conditionFlags;
+    byte unknown[0x1d8]; // TODO
+};
+
+struct FlagsStruct {
+    Common::Array<ConditionFlags> conditionFlags;
+    uint32 unknown; // TODO
+};
+
+public:
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+
+    byte videoData[0x69C]; // TODO this is its own class
+    Common::Array<ResponseStruct> responses;
+    Common::Array<FlagsStruct> flagsStructs;
+};
+
+// Base class for PlaySecondaryVideoChan0 and PlaySecondaryVideoChan1
+class PlaySecondaryVideo : public ActionRecord {
+public:
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+};
+
+class PlaySecondaryVideoChan0 : public PlaySecondaryVideo {
+    // TODO
+};
+
+class PlaySecondaryVideoChan1 : public PlaySecondaryVideo {
+    // TODO
+};
+
+class PlaySecondaryMovie : public ActionRecord {
+public:
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+};
+
+class PlayStaticBitmapAnimation : public ActionRecord {
+public:
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+
+    byte bitmapData[0xA88];
+};
+
+// TODO should inherit from above as the only difference is one int of data
+class PlayIntStaticBitmapAnimation : public ActionRecord {
+public:
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+
+    byte bitmapData[0xA8C];
+};
+
+class MapCall : public ActionRecord {
+public:
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+
+    byte mapData = 0;
+};
+
+class MapCallHot1Fr : public ActionRecord {
+public:
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+};
+
+class MapCallHotMultiframe : public ActionRecord {
+public:
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+};
+
+class MapLocationAccess : public ActionRecord {
+public:
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+};
+
+class MapSound : public ActionRecord {
+public:
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+};
+
+class MapAviOverride : public ActionRecord {
+public:
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+};
+
+class MapAviOverrideOff : public ActionRecord {
+public:
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+
+    byte overrideOffData = 0;
+};
+
+class TextBoxWrite : public ActionRecord {
+public:
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+};
+
+class TextBoxClear : public ActionRecord {
+public:
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+
+    byte clearData = 0;
+};
+
+class BumpPlayerClock : public ActionRecord {
+public:
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+};
+
+class SaveContinueGame : public ActionRecord {
+public:
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+
+    byte saveContinueData = 0;
+};
+
+class TurnOffMainRendering : public ActionRecord {
+public:
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+
+    byte turnOffData = 0;
+};
+
+class TurnOnMainRendering : public ActionRecord {
+public:
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+
+    byte turnOnData = 0;
+};
+
+class ResetAndStartTimer : public ActionRecord {
+public:
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void execute(NancyEngine *engine) override;
+};
+
+class StopTimer : public ActionRecord {
+public:
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void execute(NancyEngine *engine) override;
+};
+
+class EventFlagsMultiHS : public ActionRecord {
+public:
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+};
+
+class EventFlags : public ActionRecord {
+public:
+    struct FlagDesc {
+        int16 label;
+        PlayState::Flag flag;
+    };
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void execute(NancyEngine *engine) override;
+
+    FlagDesc descs[10];
+};
+
+class OrderingPuzzle : public ActionRecord {
+public:
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+};
+
+class LoseGame : public ActionRecord {
+public:
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+
+    byte loseData = 0;
+};
+
+class PushScene : public ActionRecord {
+public:
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+
+    byte pushData = 0;
+};
+
+class PopScene : public ActionRecord {
+public:
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+
+    byte popData = 0;
+};
+
+class WinGame : public ActionRecord {
+public:
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+
+    byte winData = 0;
+};
+
+class LeverPuzzle : public ActionRecord {
+public:
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+};
+
+class Telephone : public ActionRecord {
+public:
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+};
+
+class SliderPuzzle : public ActionRecord {
+public:
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+};
+
+class PasswordPuzzle : public ActionRecord {
+public:
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+};
+
+class AddInventoryNoHS : public ActionRecord {
+public:
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+};
+
+class RemoveInventoryNoHS : public ActionRecord {
+public:
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+};
+
+class DifficultyLevel : public ActionRecord {
+public:
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void execute(NancyEngine *engine) override;
+
+    uint16 difficulty = 0;
+    int16 flagLabel = 0;
+    uint16 flagCondition = 0;
+};
+
+class RotatingLockPuzzle : public ActionRecord {
+public:
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+};
+
+class ShowInventoryItem : public ActionRecord {
+public:
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+};
+
+class PlayDigiSoundAndDie : public ActionRecord {
+public:
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    // TODO subclass into Play and Stop (?)
+};
+
+class PlaySoundPanFrameAnchorAndDie : public ActionRecord {
+public:
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+};
+
+class PlaySoundMultiHS : public ActionRecord {
+public:
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+};
+
+class HintSystem : public ActionRecord {
+public:
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+};
+
+} // End of namespace Nancy
+
+#endif // NANCY_ACTION_RECORDTYPES_H
\ No newline at end of file
diff --git a/engines/nancy/audio.cpp b/engines/nancy/audio.cpp
index 5944780be3..498125de26 100644
--- a/engines/nancy/audio.cpp
+++ b/engines/nancy/audio.cpp
@@ -172,4 +172,4 @@ Audio::SeekableAudioStream *makeHISStream(Common::SeekableReadStream *stream, Di
 		return Audio::makeVorbisStream(subStream, DisposeAfterUse::YES);
 }
 
-} // End of namespace Nancy
+} // End of namespace Nancy
\ No newline at end of file
diff --git a/engines/nancy/audio.h b/engines/nancy/audio.h
index 23e75bced5..fcbfbb71d5 100644
--- a/engines/nancy/audio.h
+++ b/engines/nancy/audio.h
@@ -31,10 +31,14 @@ namespace Audio {
 class SeekableAudioStream;
 }
 
+namespace DisposeAfterUse {
+enum Flag;
+}
+
 namespace Nancy {
 
 Audio::SeekableAudioStream *makeHISStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse);
 
 } // End of namespace Nancy
 
-#endif
+#endif // NANCY_AUDIO_H
\ No newline at end of file
diff --git a/engines/nancy/console.cpp b/engines/nancy/console.cpp
index 02010318b7..96aab34fbd 100644
--- a/engines/nancy/console.cpp
+++ b/engines/nancy/console.cpp
@@ -20,16 +20,19 @@
  *
  */
 
+#include "engines/nancy/console.h"
+#include "engines/nancy/nancy.h"
+#include "engines/nancy/resource.h"
+#include "engines/nancy/video.h"
+#include "engines/nancy/audio.h"
+#include "engines/nancy/iff.h"
+
 #include "common/system.h"
 #include "common/events.h"
+
 #include "graphics/surface.h"
+
 #include "audio/audiostream.h"
-#include "nancy/console.h"
-#include "nancy/nancy.h"
-#include "nancy/resource.h"
-#include "nancy/video.h"
-#include "nancy/audio.h"
-#include "nancy/iff.h"
 
 namespace Nancy {
 
@@ -50,6 +53,7 @@ NancyConsole::~NancyConsole() {
 }
 
 void NancyConsole::postEnter() {
+	GUI::Debugger::postEnter();
 	if (!_videoFile.empty()) {
 		Video::VideoDecoder *dec = new AVFDecoder;
 
@@ -218,6 +222,7 @@ bool NancyConsole::Cmd_showImage(int argc, const char **argv) {
 		_vm->_system->fillScreen(0);
 		_vm->_system->copyRectToScreen(surf.getPixels(), surf.pitch, 0, 0, surf.w > 640 ? 640 : surf.w, surf.h > 480 ? 480 : surf.h);
 		surf.free();
+		_vm->_gameFlow.minGameState = NancyEngine::kIdle;
 		return cmdExit(0, 0);
 	} else {
 		debugPrintf("Failed to load image\n");
@@ -274,4 +279,4 @@ bool NancyConsole::Cmd_playAudio(int argc, const char **argv) {
 	return true;
 }
 
-} // End of namespace Nancy
+} // End of namespace Nancy
\ No newline at end of file
diff --git a/engines/nancy/console.h b/engines/nancy/console.h
index 7d0b48cd65..8b6201c33a 100644
--- a/engines/nancy/console.h
+++ b/engines/nancy/console.h
@@ -61,4 +61,4 @@ private:
 
 } // End of namespace Nancy
 
-#endif
+#endif // NANCY_CONSOLE_H
\ No newline at end of file
diff --git a/engines/nancy/datatypes.cpp b/engines/nancy/datatypes.cpp
new file mode 100644
index 0000000000..969880e8b7
--- /dev/null
+++ b/engines/nancy/datatypes.cpp
@@ -0,0 +1,84 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/nancy/datatypes.h"
+
+#include "common/stream.h"
+
+namespace Nancy {
+
+SceneSummary::SceneSummary(Common::SeekableReadStream *stream) {
+    char *buf = new char[0x32];
+
+    stream->read(buf, 0x31);
+    buf[32] = 0;
+    description = Common::String(buf);
+
+    stream->seek(1, SEEK_CUR);
+    stream->read(buf, 9);
+    buf[9] = 0;
+    videoFile = Common::String(buf);
+
+    // skip 1 extra byte & 2 unknown bytes
+    stream->seek(3, SEEK_CUR);
+    videoFormat = stream->readUint16LE();
+
+    stream->read(buf, 9);
+    buf[9] = 0;
+    audioFile = Common::String(buf);
+
+    stream->seek(0x74);
+    sceneHasRotation = stream->readUint16LE();
+    sceneHasMovement = stream->readUint16LE();
+    unknown78 = stream->readUint16LE();
+    unknown7A = stream->readUint16LE();
+    unknown7C = stream->readByte();
+
+    // put the entire chunk into a temp buffer until we figure out the rest of the structure
+    stream->seek(0);
+    chunkData = new byte[stream->size()];
+    stream->read(chunkData, stream->size());
+
+    delete[] buf;
+}
+
+View::View(Common::SeekableReadStream *stream) {
+    stream->seek(0);
+    destLeft = stream->readUint32LE();
+    destTop = stream->readUint32LE();
+    destRight = stream->readUint32LE();
+    destBottom = stream->readUint32LE();
+    srcLeft = stream->readUint32LE();
+    srcTop = stream->readUint32LE();
+    srcRight = stream->readUint32LE();
+    srcBottom = stream->readUint32LE();
+    f1Left = stream->readUint32LE();
+    f1Top = stream->readUint32LE();
+    f1Right = stream->readUint32LE();
+    f1Bottom = stream->readUint32LE();
+    f2Left = stream->readUint32LE();
+    f2Top = stream->readUint32LE();
+    f2Right = stream->readUint32LE();
+    f2Bottom = stream->readUint32LE();
+}
+
+} // End of namespace Nancy
\ No newline at end of file
diff --git a/engines/nancy/datatypes.h b/engines/nancy/datatypes.h
new file mode 100644
index 0000000000..fc08954fa9
--- /dev/null
+++ b/engines/nancy/datatypes.h
@@ -0,0 +1,85 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef NANCY_DATATYPES_H
+#define NANCY_DATATYPES_H
+
+#include "common/str.h"
+
+namespace Common {
+class SeekableReadStream;
+}
+
+namespace Nancy {
+
+// Various data types we need to extract from chunks go here
+
+// Describes a scene
+struct SceneSummary {
+    SceneSummary() { chunkData = nullptr; }
+    SceneSummary(Common::SeekableReadStream *stream);
+    ~SceneSummary() { delete[] chunkData; }
+
+    Common::String description; // 0x00
+    Common::String videoFile;   // 0x32
+    //
+    uint16 videoFormat;         // 0x3E, value is 1 or 2
+    Common::String audioFile;   // 0x40
+    //
+    uint16 sceneHasRotation;    // 0x74, could be an enum?
+    uint16 sceneHasMovement;    // 0x76, also an enum??
+    uint16 unknown78;           // 0x78
+    uint16 unknown7A;           // 0x7A
+    byte unknown7C;             // 0x7C, enum with 4 values
+    //
+    byte *chunkData;
+};
+
+// Describes the viewport
+struct View {
+    View() =default;
+    View(Common::SeekableReadStream *stream);
+    // The bounds of the destination rectangle on screen
+    uint32 destLeft;        // 0x00
+    uint32 destTop;         // 0x04
+    uint32 destRight;       // 0x08
+    uint32 destBottom;      // 0x0C
+    // The bounds of the source rectangle (Background -> screen)
+    uint32 srcLeft;         // 0x10
+    uint32 srcTop;          // 0x14
+    uint32 srcRight;        // 0x18
+    uint32 srcBottom;       // 0x1C
+    // VideoFileFormat 1 rectangle bounds (video -> Background)
+    uint32 f1Left;          // 0x20
+    uint32 f1Top;           // 0x24
+    uint32 f1Right;         // 0x28
+    uint32 f1Bottom;        // 0x2C
+    // VideoFileFormat 2 rectangle bounds (video -> Background)
+    uint32 f2Left;          // 0x30
+    uint32 f2Top;           // 0x34
+    uint32 f2Right;         // 0x38
+    uint32 f2Bottom;        // 0x3C
+};
+
+} // End of namespace Nancy
+
+#endif // NANCY_DATATYPES_H
\ No newline at end of file
diff --git a/engines/nancy/decompress.cpp b/engines/nancy/decompress.cpp
index 7135794374..ad20d278ef 100644
--- a/engines/nancy/decompress.cpp
+++ b/engines/nancy/decompress.cpp
@@ -20,9 +20,10 @@
  *
  */
 
+#include "engines/nancy/decompress.h"
+
 #include "common/memstream.h"
 #include "common/textconsole.h"
-#include "nancy/decompress.h"
 
 namespace Nancy {
 
@@ -99,4 +100,4 @@ bool Decompressor::decompress(Common::ReadStream &input, Common::MemoryWriteStre
 	return true;
 }
 
-} // End of namespace Nancy
+} // End of namespace Nancy
\ No newline at end of file
diff --git a/engines/nancy/decompress.h b/engines/nancy/decompress.h
index 94ffe5e314..1fc89e46c3 100644
--- a/engines/nancy/decompress.h
+++ b/engines/nancy/decompress.h
@@ -23,9 +23,12 @@
 #ifndef NANCY_DECOMPRESS_H
 #define NANCY_DECOMPRESS_H
 
+#include "common/scummsys.h"
+
 namespace Common {
 	class ReadStream;
 	class WriteStream;
+	class MemoryWriteStream;
 }
 
 namespace Nancy {
@@ -56,4 +59,4 @@ private:
 
 } // End of namespace Nancy
 
-#endif
+#endif // NANCY_DECOMPRESS_H
\ No newline at end of file
diff --git a/engines/nancy/detection.cpp b/engines/nancy/detection.cpp
index 5ef2f33db0..e21ed18209 100644
--- a/engines/nancy/detection.cpp
+++ b/engines/nancy/detection.cpp
@@ -20,30 +20,16 @@
  *
  */
 
-#include "engines/advancedDetector.h"
+#include "engines/nancy/detection.h"
+
 #include "common/system.h"
 #include "common/savefile.h"
 #include "common/textconsole.h"
+
 #include "graphics/thumbnail.h"
 #include "graphics/surface.h"
 
-#include "nancy/nancy.h"
-
-namespace Nancy {
-
-struct NancyGameDescription {
-	ADGameDescription desc;
-	GameType gameType;
-};
-
-uint32 NancyEngine::getFeatures() const {
-	return _gameDescription->desc.flags;
-}
-
-const char *NancyEngine::getGameId() const {
-	return _gameDescription->desc.gameId;
-}
-
+#include "engines/advancedDetector.h"
 
 const char *const directoryGlobs[] = {
 	"game",
@@ -53,16 +39,16 @@ const char *const directoryGlobs[] = {
 
 static const PlainGameDescriptor nancyGames[] = {
 	// Games
-	{"nancy1", "Nancy Drew 1: Secrets Can Kill"},
-	{"nancy2", "Nancy Drew 2: Stay Tuned for Danger"},
-	{"nancy3", "Nancy Drew 3: Message in a Haunted Mansion"},
-	{"nancy4", "Nancy Drew 4: Treasure in the Royal Tower"},
-	{"nancy5", "Nancy Drew 5: The Final Scene"},
-	{"nancy6", "Nancy Drew 6: Secret of the Scarlet Hand"},
+	{"nancy1", "Nancy Drew: Secrets Can Kill"},
+	{"nancy2", "Nancy Drew: Stay Tuned for Danger"},
+	{"nancy3", "Nancy Drew: Message in a Haunted Mansion"},
+	{"nancy4", "Nancy Drew: Treasure in the Royal Tower"},
+	{"nancy5", "Nancy Drew: The Final Scene"},
+	{"nancy6", "Nancy Drew: Secret of the Scarlet Hand"},
 	{0, 0}
 };
 
-static const NancyGameDescription gameDescriptions[] = {
+static const Nancy::NancyGameDescription gameDescriptions[] = {
 
 	{ // MD5 by waltervn
 		{
@@ -76,7 +62,7 @@ static const NancyGameDescription gameDescriptions[] = {
 			ADGF_NO_FLAGS,
 			GUIO0()
 		},
-		kGameTypeNancy1
+		Nancy::GameType::kGameTypeNancy1
 	},
 	{ // MD5 by waltervn
 		{
@@ -90,7 +76,7 @@ static const NancyGameDescription gameDescriptions[] = {
 			ADGF_NO_FLAGS,
 			GUIO0()
 		},
-		kGameTypeNancy2
+		Nancy::GameType::kGameTypeNancy2
 	},
 	{ // MD5 by waltervn
 		{
@@ -104,7 +90,7 @@ static const NancyGameDescription gameDescriptions[] = {
 			ADGF_NO_FLAGS,
 			GUIO0()
 		},
-		kGameTypeNancy3
+		Nancy::GameType::kGameTypeNancy3
 	},
 	{ // MD5 by waltervn
 		{
@@ -120,7 +106,7 @@ static const NancyGameDescription gameDescriptions[] = {
 			ADGF_NO_FLAGS,
 			GUIO0()
 		},
-		kGameTypeNancy3
+		Nancy::GameType::kGameTypeNancy3
 	},
 	{ // MD5 by waltervn
 		{
@@ -134,7 +120,7 @@ static const NancyGameDescription gameDescriptions[] = {
 			ADGF_NO_FLAGS,
 			GUIO0()
 		},
-		kGameTypeNancy3
+		Nancy::GameType::kGameTypeNancy3
 	},
 	{ // MD5 by waltervn
 		{
@@ -150,7 +136,7 @@ static const NancyGameDescription gameDescriptions[] = {
 			ADGF_NO_FLAGS,
 			GUIO0()
 		},
-		kGameTypeNancy3
+		Nancy::GameType::kGameTypeNancy3
 	},
 	{ // MD5 by waltervn
 		{
@@ -164,7 +150,7 @@ static const NancyGameDescription gameDescriptions[] = {
 			ADGF_NO_FLAGS,
 			GUIO0()
 		},
-		kGameTypeNancy3
+		Nancy::GameType::kGameTypeNancy3
 	},
 	{ // MD5 by waltervn
 		{
@@ -180,7 +166,7 @@ static const NancyGameDescription gameDescriptions[] = {
 			ADGF_NO_FLAGS,
 			GUIO0()
 		},
-		kGameTypeNancy3
+		Nancy::GameType::kGameTypeNancy3
 	},
 	{ // MD5 by clone2727
 		{
@@ -196,7 +182,7 @@ static const NancyGameDescription gameDescriptions[] = {
 			ADGF_NO_FLAGS,
 			GUIO0()
 		},
-		kGameTypeNancy3
+		Nancy::GameType::kGameTypeNancy3
 	},
 	{ // MD5 by Strangerke
 		{
@@ -210,173 +196,30 @@ static const NancyGameDescription gameDescriptions[] = {
 			ADGF_NO_FLAGS,
 			GUIO0()
 		},
-		kGameTypeNancy3
+		Nancy::GameType::kGameTypeNancy3
 	},
-	{AD_TABLE_END_MARKER, kGameTypeNone}
+	{AD_TABLE_END_MARKER, Nancy::GameType::kGameTypeNone}
 };
 
-class NancyMetaEngine : public AdvancedMetaEngine {
+class NancyMetaEngineDetection : public AdvancedMetaEngineDetection {
 public:
-	NancyMetaEngine() : AdvancedMetaEngine(gameDescriptions, sizeof(NancyGameDescription), nancyGames) {
+	NancyMetaEngineDetection() : AdvancedMetaEngineDetection(gameDescriptions, sizeof(Nancy::NancyGameDescription), nancyGames) {
 		_maxScanDepth = 2;
 		_directoryGlobs = directoryGlobs;
 	}
 
-	const char *getName() const {
-		return "Nancy Drew";
+	const char *getEngineId() const override {
+		return "nancy";
 	}
 
+	const char *getName() const override {
+		return "Nancy Drew";
+	}
+    
 	const char *getOriginalCopyright() const {
 		return "Nancy Drew Engine copyright Her Interactive, 1995-2012";
 	}
-
-	bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *gd) const;
-	bool hasFeature(MetaEngineFeature f) const;
-
-	int getMaximumSaveSlot() const;
-	SaveStateList listSaves(const char *target) const;
-	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
-	void removeSaveState(const char *target, int slot) const;
+	
 };
 
-bool NancyMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *gd) const {
-	if (gd) {
-		*engine = NancyEngine::create(((const NancyGameDescription *)gd)->gameType, syst, (const NancyGameDescription *)gd);
-		((NancyEngine *)*engine)->initGame((const NancyGameDescription *)gd);
-	}
-	return gd != 0;
-}
-
-bool NancyMetaEngine::hasFeature(MetaEngineFeature f) const {
-	return
-	    (f == kSupportsListSaves) ||
-	    (f == kSupportsLoadingDuringStartup) ||
-	    (f == kSupportsDeleteSave) ||
-	    (f == kSavesSupportMetaInfo) ||
-	    (f == kSavesSupportThumbnail) ||
-	    (f == kSavesSupportCreationDate);
-}
-
-int NancyMetaEngine::getMaximumSaveSlot() const { return 99; }
-
-SaveStateList NancyMetaEngine::listSaves(const char *target) const {
-	Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
-	Common::StringArray filenames;
-	Common::String pattern = target;
-	pattern += "-??.SAV";
-
-	filenames = saveFileMan->listSavefiles(pattern);
-	sort(filenames.begin(), filenames.end());   // Sort (hopefully ensuring we are sorted numerically..)
-
-	SaveStateList saveList;
-	char slot[3];
-	int slotNum = 0;
-	for (Common::StringArray::const_iterator filename = filenames.begin(); filename != filenames.end(); ++filename) {
-		slot[0] = filename->c_str()[filename->size() - 6];
-		slot[1] = filename->c_str()[filename->size() - 5];
-		slot[2] = '\0';
-		// Obtain the last 2 digits of the filename (without extension), since they correspond to the save slot
-		slotNum = atoi(slot);
-		if (slotNum >= 0 && slotNum <= getMaximumSaveSlot()) {
-			Common::InSaveFile *file = saveFileMan->openForLoading(*filename);
-			if (file) {
-				int saveVersion = file->readByte();
-
-				if (saveVersion != kSavegameVersion) {
-					warning("Savegame of incompatible version");
-					delete file;
-					continue;
-				}
-
-				// read name
-				uint16 nameSize = file->readUint16BE();
-				if (nameSize >= 255) {
-					delete file;
-					continue;
-				}
-				char name[256];
-				file->read(name, nameSize);
-				name[nameSize] = 0;
-
-				saveList.push_back(SaveStateDescriptor(slotNum, name));
-				delete file;
-			}
-		}
-	}
-
-	return saveList;
-}
-
-SaveStateDescriptor NancyMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
-	Common::String fileName = Common::String::format("%s-%02d.SAV", target, slot);
-	Common::InSaveFile *file = g_system->getSavefileManager()->openForLoading(fileName);
-
-	if (file) {
-		int saveVersion = file->readByte();
-
-		if (saveVersion != kSavegameVersion) {
-			warning("Savegame of incompatible version");
-			delete file;
-			return SaveStateDescriptor();
-		}
-
-		uint32 saveNameLength = file->readUint16BE();
-		char saveName[256];
-		file->read(saveName, saveNameLength);
-		saveName[saveNameLength] = 0;
-
-		SaveStateDescriptor desc(slot, saveName);
-
-		Graphics::Surface *thumbnail = nullptr;
-
-		if (Graphics::loadThumbnail(*file, thumbnail))
-			desc.setThumbnail(thumbnail);
-
-		desc.setDeletableFlag(true);
-		desc.setWriteProtectedFlag(false);
-
-		uint32 saveDate = file->readUint32BE();
-		uint16 saveTime = file->readUint16BE();
-
-		int day = (saveDate >> 24) & 0xFF;
-		int month = (saveDate >> 16) & 0xFF;
-		int year = saveDate & 0xFFFF;
-
-		desc.setSaveDate(year, month, day);
-
-		int hour = (saveTime >> 8) & 0xFF;
-		int minutes = saveTime & 0xFF;
-
-		desc.setSaveTime(hour, minutes);
-
-		// Slot 0 is used for the 'restart game' save in all Nancy games, thus
-		// we prevent it from being deleted.
-		desc.setDeletableFlag(slot != 0);
-		desc.setWriteProtectedFlag(slot == 0);
-
-		delete file;
-		return desc;
-	}
-	return SaveStateDescriptor();
-}
-
-void NancyMetaEngine::removeSaveState(const char *target, int slot) const {
-	Common::String fileName = Common::String::format("%s-%02d.SAV", target, slot);
-	g_system->getSavefileManager()->removeSavefile(fileName);
-}
-} // End of namespace Nancy
-
-#if PLUGIN_ENABLED_DYNAMIC(NANCY)
-REGISTER_PLUGIN_DYNAMIC(NANCY, PLUGIN_TYPE_ENGINE, Nancy::NancyMetaEngine);
-#else
-REGISTER_PLUGIN_STATIC(NANCY, PLUGIN_TYPE_ENGINE, Nancy::NancyMetaEngine);
-#endif
-
-namespace Nancy {
-
-void NancyEngine::initGame(const NancyGameDescription *gd) {
-	_gameType = gd->gameType;
-	_platform = gd->desc.platform;
-}
-
-} // End of namespace Nancy
+REGISTER_PLUGIN_STATIC(NANCY_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, NancyMetaEngineDetection);
\ No newline at end of file
diff --git a/engines/nancy/detection.h b/engines/nancy/detection.h
new file mode 100644
index 0000000000..6a2b7b483c
--- /dev/null
+++ b/engines/nancy/detection.h
@@ -0,0 +1,44 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef NANCY_DETECTION_H
+#define NANCY_DETECTION_H
+
+#include "engines/advancedDetector.h"
+
+namespace Nancy {
+
+enum GameType {
+	kGameTypeNone = 0,
+	kGameTypeNancy1,
+	kGameTypeNancy2,
+	kGameTypeNancy3
+};
+
+struct NancyGameDescription {
+	ADGameDescription desc;
+	GameType gameType;
+};
+
+} // End of namespace Nancy
+
+#endif // NANCY_DETECTION_H
\ No newline at end of file
diff --git a/engines/nancy/graphics.cpp b/engines/nancy/graphics.cpp
new file mode 100644
index 0000000000..b4973b5cc0
--- /dev/null
+++ b/engines/nancy/graphics.cpp
@@ -0,0 +1,243 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/nancy/graphics.h"
+
+#include "common/error.h"
+#include "common/system.h"
+
+#include "graphics/managed_surface.h"
+
+namespace Nancy {
+
+// TODO the original engine uses a sixth byte for the green; 
+// so this should be Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0)
+// and transColor should be 0x7C0
+// so the colors are gonna be slightly wrong for now
+//const Graphics::PixelFormat GraphicsManager::pixelFormat = Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0);
+//const uint GraphicsManager::transColor = 0x7C0;
+const Graphics::PixelFormat GraphicsManager::pixelFormat = Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0);
+const uint GraphicsManager::transColor = 0x3E0;
+
+GraphicsManager::GraphicsManager(NancyEngine *engine) :
+        _engine(engine) {
+    _screen.create(640, 480, pixelFormat);
+}
+
+void GraphicsManager::init() {  
+    Common::SeekableReadStream *chunk = _engine->getBootChunkStream("VIEW");
+    viewportDesc = View(chunk);
+    uint32 width = viewportDesc.destRight - viewportDesc.destLeft;
+    uint32 height = viewportDesc.destBottom - viewportDesc.destTop;
+    _background.create(width, height, pixelFormat);
+
+    // TODO make a TBOX struct
+    chunk = _engine->getBootChunkStream("TBOX");
+    chunk->seek(0x28);
+    width = chunk->readUint32LE();
+    height = chunk->readUint32LE();
+    chunk->seek(0x20);
+    width -= chunk->readUint32LE();
+    height -= chunk->readUint32LE();
+    _frameTextBox.create(width, height, pixelFormat);
+}
+
+GraphicsManager::~GraphicsManager() {
+    _background.free();
+    _frameTextBox.free();
+    _screen.free();
+
+    for (auto st : _ZRender) {
+        delete st.sourceRect;
+        delete st.destPoint;
+        delete st.renderFunction;
+    }
+}
+
+void GraphicsManager::clearGenericZRenderStruct(uint id) {
+    ZRenderStruct *st = getGenericZRenderStruct(id);
+    st->isActive = false;
+    st->isInitialized = false;
+}
+
+ZRenderStruct *GraphicsManager::getGenericZRenderStruct(uint id) {
+    if (id > 60)
+        error("Bad ZRender ID!");
+    
+    return &_ZRender[id];
+}
+
+void GraphicsManager::initGenericZRenderStruct(uint id, char const *name, uint32 z, bool isActive, ZRenderStruct::BltType bltType, Graphics::Surface *surface, RenderFunction *func, Common::Rect *sourceRect, Common::Point *destPoint) {
+    clearGenericZRenderStruct(id);
+    ZRenderStruct *st = getGenericZRenderStruct(id);
+    delete st->sourceRect;
+    delete st->destPoint;
+    delete st->renderFunction;
+    st->name = name;
+    st->z = z;
+    st->isActive = isActive;
+    st->isInitialized = true;
+    st->bltType = bltType;
+    st->sourceSurface = surface;
+    st->sourceRect = sourceRect;
+    st->destPoint = destPoint;
+    st->renderFunction = func;
+}
+
+void GraphicsManager::renderDisplay(uint last) {
+    int *mask = new int[last+2];
+    for (uint i = 0; i <= last; ++i) {
+        mask[i] = i;
+    }
+    // set final member to -1
+    mask[last+1] = -1;
+    renderDisplay(mask);
+    delete[] mask;
+}
+
+void GraphicsManager::renderDisplay(int *idMask) {
+    char hasBeenRendered[60]{};
+    if (!idMask)
+        error("Bad ZRender ID Mask!");
+
+    for (uint currentZ = _startingZ; currentZ < 12; ++currentZ) {
+        for (uint i = 0; idMask[i] != -1; ++i) {
+            int maskCur = idMask[i];
+            ZRenderStruct &current = _ZRender[maskCur];
+            if (!hasBeenRendered[i] && current.isActive && current.isInitialized && current.z == currentZ) {
+                if (current.renderFunction && current.renderFunction->isValid()) {
+                    current.renderFunction->operator()();
+                }
+                else {
+                    switch (current.bltType) {
+                        // making some assumptions here
+                        case ZRenderStruct::BltType::kNoTrans:
+                            _screen.blitFrom(*current.sourceSurface, *current.sourceRect, *current.destPoint);
+                            break;
+                        case ZRenderStruct::BltType::kTrans: {
+                            
+                            _screen.transBlitFrom(*current.sourceSurface, *current.sourceRect, *current.destPoint, transColor);
+                            break; }
+                        default:
+                            error("Bad ZRender Blt type!");
+                    }
+                }
+                hasBeenRendered[i] = 1;
+            }
+        }
+    }
+    _screen.update();
+}
+
+void GraphicsManager::loadBackgroundVideo(const Common::String &filename) {
+    if (_videoDecoder.isVideoLoaded()) {
+        _videoDecoder.close();
+    }
+    _videoDecoder.loadFile(filename + ".avf");
+}
+
+const Graphics::Surface *GraphicsManager::getBackgroundFrame(uint16 frameId)  {
+    if (!_videoDecoder.isVideoLoaded()) {
+        error("No video loaded");
+        return nullptr;
+    }
+    return _videoDecoder.decodeFrame(frameId);
+}
+
+uint32 GraphicsManager::getBackgroundFrameCount() {
+    return _videoDecoder.getFrameCount();
+}
+
+uint32 GraphicsManager::getBackgroundWidth() {
+    return _videoDecoder.getWidth();
+}
+
+uint32 GraphicsManager::getBackgroundHeight() {
+    return _videoDecoder.getHeight();
+}
+
+void GraphicsManager::renderFrame() {
+    Graphics::Surface n = *_ZRender[0].sourceSurface->convertTo(pixelFormat);
+    _screen.blitFrom(n, *_ZRender[0].sourceRect, *_ZRender[0].destPoint);
+    n.free();
+
+    // not sure why we do this
+    _numTimesRenderedFrame += 1;
+    if (_numTimesRenderedFrame > 1) {
+        _startingZ = 2;
+        _numTimesRenderedFrame = 0;
+    } else {
+        _startingZ = 1;
+    }
+}
+
+void GraphicsManager::renderResTBBatSlider() {
+    // not sure why this is its own function
+    ZRenderStruct &st = _ZRender[10];
+    _screen.blitFrom(*st.sourceSurface, *st.sourceRect, *st.destPoint);
+}
+
+void GraphicsManager::renderFrameInvBox() {
+    // TODO
+}
+
+void GraphicsManager::renderPrimaryVideo() {
+    // TODO
+}
+
+void GraphicsManager::renderSecVideo0() {
+    // TODO
+}
+
+void GraphicsManager::renderSecVideo1() {
+    // TODO
+}
+
+void GraphicsManager::renderSecMovie() {
+    // TODO
+}
+
+void GraphicsManager::renderOrderingPuzzle() {
+    // TODO
+}
+
+void GraphicsManager::renderRotatingLockPuzzle() {
+    // TODO
+}
+
+void GraphicsManager::renderLeverPuzzle() {
+    // TODO
+}
+
+void GraphicsManager::renderTelephone() {
+    // TODO
+}
+
+void GraphicsManager::renderSliderPuzzle() {
+    // TODO
+}
+
+void GraphicsManager::renderPasswordPuzzle() {
+    // TODO
+}
+
+} // End of namespace Nancy
\ No newline at end of file
diff --git a/engines/nancy/graphics.h b/engines/nancy/graphics.h
new file mode 100644
index 0000000000..6629b7d545
--- /dev/null
+++ b/engines/nancy/graphics.h
@@ -0,0 +1,113 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef NANCY_GRAPHICS_H
+#define NANCY_GRAPHICS_H
+
+#include "engines/nancy/nancy.h"
+#include "engines/nancy/video.h"
+#include "engines/nancy/datatypes.h"
+
+#include "common/func.h"
+
+#include "graphics/screen.h"
+
+namespace Nancy {
+
+typedef Common::Functor0<void> RenderFunction;
+
+struct ZRenderStruct {
+public:
+    enum BltType { kNone, kNoTrans, kTrans };
+
+    uint32 z = 0;
+    RenderFunction *renderFunction = nullptr;
+    Graphics::Surface *sourceSurface = nullptr;
+    Common::Rect *sourceRect = nullptr;
+    Common::Point *destPoint = nullptr;
+    bool isActive = false;
+    bool isInitialized = false;
+    BltType bltType = kNone;
+    Common::String name;
+};
+
+class GraphicsManager {
+public:
+    GraphicsManager(NancyEngine *engine);
+    ~GraphicsManager();
+
+    void init();
+
+    void clearGenericZRenderStruct(uint id);
+    ZRenderStruct *getGenericZRenderStruct(uint id);
+    void initGenericZRenderStruct(uint id, char const *name, uint32 z, bool isActive, ZRenderStruct::BltType bltType, Graphics::Surface *surface = nullptr, RenderFunction *func = nullptr, Common::Rect *sourceRect = nullptr, Common::Point *destPoint = nullptr);
+
+    void renderDisplay(uint last);
+    void renderDisplay(int *idMask);
+
+    void loadBackgroundVideo(const Common::String &filename);
+    const Graphics::Surface *getBackgroundFrame(uint16 frameId);
+    uint32 getBackgroundFrameCount();
+    uint32 getBackgroundWidth();
+    uint32 getBackgroundHeight();
+
+    Graphics::Surface _background;
+    Graphics::Surface _frameTextBox;
+    Graphics::Surface _primaryFrameSurface;
+    Graphics::Surface _object0Surface;
+    Graphics::Surface _inventoryBoxIconsSurface;
+    Graphics::Surface _inventoryCursorsSurface;
+
+    View viewportDesc;
+
+    static const Graphics::PixelFormat pixelFormat;
+    static const uint transColor;
+
+private:
+    NancyEngine *_engine;
+    ZRenderStruct _ZRender[60];
+    Graphics::Screen _screen;
+    AVFDecoder _videoDecoder;
+
+    uint _startingZ = 1;
+    uint _numTimesRenderedFrame = 0; // no idea why we're doing this
+
+public:
+    // custom render functions
+    void renderFrame();
+    void renderResTBBatSlider();
+    void renderFrameInvBox();
+    void renderPrimaryVideo();
+    void renderSecVideo0();
+    void renderSecVideo1();
+    void renderSecMovie();
+    void renderOrderingPuzzle();
+    void renderRotatingLockPuzzle();
+    void renderLeverPuzzle();
+    void renderTelephone();
+    void renderSliderPuzzle();
+    void renderPasswordPuzzle();
+};
+
+} // End of namespace Nancy
+
+#endif // NANCY_GRAPHICS_H
\ No newline at end of file
diff --git a/engines/nancy/iff.cpp b/engines/nancy/iff.cpp
index 0c1cbaeb75..3fd6ceadea 100644
--- a/engines/nancy/iff.cpp
+++ b/engines/nancy/iff.cpp
@@ -20,9 +20,9 @@
  *
  */
 
-#include "nancy/nancy.h"
-#include "nancy/iff.h"
-#include "nancy/resource.h"
+#include "engines/nancy/nancy.h"
+#include "engines/nancy/iff.h"
+#include "engines/nancy/resource.h"
 
 #include "common/memstream.h"
 #include "common/iff_container.h"
@@ -108,7 +108,7 @@ const byte *IFF::getChunk(uint32 id, uint &size, uint index) const {
 		}
 	}
 
-	return 0;
+	return nullptr;
 }
 
 Common::SeekableReadStream *IFF::getChunkStream(Common::String id, uint index) const {
@@ -148,4 +148,4 @@ void IFF::list(Common::Array<Common::String> &nameList) {
 	}
 }
 
-} // End of namespace Nancy
+} // End of namespace Nancy
\ No newline at end of file
diff --git a/engines/nancy/iff.h b/engines/nancy/iff.h
index 9adcfe617f..7e5e75035f 100644
--- a/engines/nancy/iff.h
+++ b/engines/nancy/iff.h
@@ -20,12 +20,12 @@
  *
  */
 
+#ifndef NANCY_IFF_H
+#define NANCY_IFF_H
+
 #include "common/array.h"
 #include "common/str.h"
 
-#ifndef NANCY_SCRIPT_H
-#define NANCY_SCRIPT_H
-
 namespace Common {
 struct IFFChunk;
 class SeekableReadStream;
@@ -69,4 +69,4 @@ private:
 
 } // End of namespace Nancy
 
-#endif
+#endif // NANCY_IFF_H
\ No newline at end of file
diff --git a/engines/nancy/logic.cpp b/engines/nancy/logic.cpp
new file mode 100644
index 0000000000..770acfda51
--- /dev/null
+++ b/engines/nancy/logic.cpp
@@ -0,0 +1,90 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/nancy/logic.h"
+
+#include "common/memstream.h"
+
+namespace Nancy {
+
+bool Logic::addNewActionRecord(Common::SeekableReadStream &inputData) {
+    inputData.seek(0x30);
+    byte ARType = inputData.readByte();
+    ActionRecord *newRecord = createActionRecord(ARType);
+
+    inputData.seek(0);
+    char *descBuf = new char[0x30];
+    inputData.read(descBuf, 0x30);
+    newRecord->description = Common::String(descBuf);
+    delete[] descBuf;
+
+    newRecord->type = inputData.readByte(); // redundant
+    newRecord->execType = inputData.readByte();
+
+    uint16 localChunkSize = newRecord->readData(inputData);
+    localChunkSize += 0x32;
+
+    // If the localChunkSize is less than the total data, there must be dependencies at the end of the chunk
+    uint16 depsDataSize = (uint16)inputData.size() - localChunkSize;
+    if (depsDataSize > 0) {
+        // Each dependency is 0x0C bytes long (in v1)
+        newRecord->numDependencies = depsDataSize / 0xC;
+        if (depsDataSize % 0xC) {
+            error("Invalid dependency data size!");
+        }
+
+        newRecord->dependencies = new DependencyRecord[newRecord->numDependencies];
+
+        // Initialize the dependencies data
+        inputData.seek(/*0x32 + */localChunkSize);
+        for (uint16 i = 0; i < newRecord->numDependencies; ++i) {
+            newRecord->dependencies[i].type = (DependencyType)inputData.readByte();
+            newRecord->dependencies[i].label = inputData.readByte();
+            newRecord->dependencies[i].condition = inputData.readByte();
+            newRecord->dependencies[i].orFlag = inputData.readByte();
+            newRecord->dependencies[i].hours = inputData.readSint16LE();
+            newRecord->dependencies[i].minutes = inputData.readSint16LE();
+            newRecord->dependencies[i].seconds = inputData.readSint16LE();
+            newRecord->dependencies[i].milliseconds = inputData.readSint16LE();
+        }
+
+        for (uint16 i = 0; i < newRecord->numDependencies; ++i) {
+            DependencyRecord &current = newRecord->dependencies[i];
+            if (current.type != 9 || current.hours != -1 || current.minutes != -1 || current.seconds != -1) {
+                newRecord->timers[i] = ((current.hours * 60 + current.minutes) * 60 + current.seconds) * 1000 + current.milliseconds;
+            }
+        }
+
+        for (uint16 i = 0; i < newRecord->numDependencies; ++i) {
+            if (newRecord->dependencies[i].orFlag == 1) {
+                // newRecord->0x78+i = true
+            } else {
+                // 0x78+i = false
+            }
+        }
+    }
+
+    _records.push_back(newRecord);
+    return true;
+}
+
+} // End of namespace Nancy
\ No newline at end of file
diff --git a/engines/nancy/logic.h b/engines/nancy/logic.h
new file mode 100644
index 0000000000..c7e65ac923
--- /dev/null
+++ b/engines/nancy/logic.h
@@ -0,0 +1,55 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef NANCY_LOGIC_H
+#define NANCY_LOGIC_H
+
+#include "engines/nancy/action/actionrecord.h"
+
+#include "common/str.h"
+#include "common/stream.h"
+#include "common/array.h"
+#include "common/func.h"
+
+namespace Nancy {
+
+class NancyEngine;
+class SceneManager;
+
+class Logic {
+    friend class SceneManager;
+
+public:
+    Logic(NancyEngine* engine): _engine(engine) {}
+    virtual ~Logic() {}
+
+    bool addNewActionRecord(Common::SeekableReadStream &inputData);
+
+protected:
+    virtual ActionRecord *createActionRecord(uint16 type);
+    NancyEngine *_engine;
+    Common::Array<ActionRecord *> _records;
+};
+
+} // End of namespace Nancy
+
+#endif // NANCY_LOGIC_H
\ No newline at end of file
diff --git a/engines/nancy/logo.cpp b/engines/nancy/logo.cpp
index 7c3603c1c3..af8d73788f 100644
--- a/engines/nancy/logo.cpp
+++ b/engines/nancy/logo.cpp
@@ -20,6 +20,11 @@
  *
  */
 
+#include "engines/nancy/logo.h"
+#include "engines/nancy/nancy.h"
+#include "engines/nancy/resource.h"
+#include "engines/nancy/audio.h"
+
 #include "common/error.h"
 #include "common/system.h"
 #include "common/events.h"
@@ -29,22 +34,17 @@
 
 #include "graphics/surface.h"
 
-#include "nancy/logo.h"
-#include "nancy/nancy.h"
-#include "nancy/resource.h"
-#include "nancy/audio.h"
-
 namespace Nancy {
 
-void LogoSequence::doIt() {
-	switch(_state) {
+void LogoSequence::process() {
+	switch (_state) {
 	case kInit:
 		init();
 		break;
 	case kStartSound:
 		startSound();
 		break;
-	case kRunning:
+	case kRun:
 		run();
 		break;
 	case kStop:
@@ -73,11 +73,11 @@ void LogoSequence::startSound() {
 	}
 
 	_startTicks = _engine->_system->getMillis();
-	_state = kRunning;
+	_state = kRun;
 }
 
 void LogoSequence::run() {
-	switch(_runState) {
+	switch (_runState) {
 	case kBlit:
 		_engine->_system->copyRectToScreen(_surf->getPixels(), _surf->pitch, 0, 0, _surf->w, _surf->h);
 		_runState = kWait;
@@ -98,8 +98,8 @@ void LogoSequence::stop() {
 	// For the N+C key combo it looks for some kind of cheat file
 	// to initialize the game state with.
 
-	_engine->_gameFlow.minGameState = NancyEngine::kIdle;
+	_engine->_gameFlow.minGameState = NancyEngine::kScene;
 	_engine->_system->fillScreen(0);
 }
 
-} // End of namespace Nancy
+} // End of namespace Nancy
\ No newline at end of file
diff --git a/engines/nancy/logo.h b/engines/nancy/logo.h
index fb0a082d9b..899078eba6 100644
--- a/engines/nancy/logo.h
+++ b/engines/nancy/logo.h
@@ -23,6 +23,8 @@
 #ifndef NANCY_LOGO_H
 #define NANCY_LOGO_H
 
+#include "common/scummsys.h"
+
 namespace Graphics {
 	class Surface;
 }
@@ -39,7 +41,8 @@ public:
 		_runState(kBlit),
 		_startTicks(0),
 		_surf(nullptr) { }
-	void doIt();
+		
+	void process();
 
 private:
 	void init();
@@ -50,7 +53,7 @@ private:
 	enum State {
 		kInit,
 		kStartSound,
-		kRunning,
+		kRun,
 		kStop
 	};
 
@@ -69,4 +72,4 @@ private:
 
 } // End of namespace Nancy
 
-#endif // NANCY_LOGO_H
+#endif // NANCY_LOGO_H
\ No newline at end of file
diff --git a/engines/nancy/menu.cpp b/engines/nancy/menu.cpp
new file mode 100644
index 0000000000..bde90bfab6
--- /dev/null
+++ b/engines/nancy/menu.cpp
@@ -0,0 +1,51 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/nancy/menu.h"
+#include "engines/nancy/nancy.h"
+
+#include "graphics/surface.h"
+
+namespace Nancy {
+
+void MainMenu::process() {
+    switch(_state) {
+    case kInit:
+        init();
+        break;
+    case kStartSound:
+        startSound();
+        break;
+    case kRun:
+        run();
+        break;
+    }
+}
+
+void MainMenu::init() {
+    _surf = new Graphics::Surface;
+    _menuImage = new Graphics::Surface;
+    _cursor = new Graphics::Surface;
+    
+}
+
+} // End of namespace Nancy
\ No newline at end of file
diff --git a/engines/nancy/menu.h b/engines/nancy/menu.h
new file mode 100644
index 0000000000..16bc8575da
--- /dev/null
+++ b/engines/nancy/menu.h
@@ -0,0 +1,62 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef NANCY_MENU_H
+#define NANCY_MENU_H
+
+namespace Graphics {
+class Surface;
+}
+
+namespace Nancy {
+
+class NancyEngine;
+
+class MainMenu {
+public:
+    MainMenu(NancyEngine *engine) :
+        _engine(engine),
+        _state(kInit),
+        _surf(nullptr) {}
+
+    void process();
+
+private:
+    void init();
+    void startSound();
+    void run();
+
+    enum State {
+        kInit,
+        kStartSound,
+        kRun
+    };
+
+    NancyEngine *_engine;
+    State _state;
+    Graphics::Surface *_surf;
+    Graphics::Surface *_menuImage;
+    Graphics::Surface *_cursor;
+};
+} // End of namespace Nancy
+
+#endif // NANCY_MENU_H
\ No newline at end of file
diff --git a/engines/nancy/metaengine.cpp b/engines/nancy/metaengine.cpp
new file mode 100644
index 0000000000..bab7c73ad5
--- /dev/null
+++ b/engines/nancy/metaengine.cpp
@@ -0,0 +1,200 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/nancy/nancy.h"
+
+#include "common/config-manager.h"
+#include "common/savefile.h"
+#include "common/system.h"
+
+#include "graphics/thumbnail.h"
+
+#include "base/plugins.h"
+
+#include "engines/advancedDetector.h"
+
+namespace Nancy {
+
+uint32 NancyEngine::getFeatures() const {
+	return _gameDescription->desc.flags;
+}
+
+const char *NancyEngine::getGameId() const {
+	return _gameDescription->desc.gameId;
+}
+
+void NancyEngine::initGame(const NancyGameDescription *gd) {
+	_gameType = gd->gameType;
+	_platform = gd->desc.platform;
+}
+
+} // End of namespace Nancy
+
+class NancyMetaEngine : public AdvancedMetaEngine {
+public:
+    const char *getName() const override {
+		return "nancy";
+	}
+
+    bool hasFeature(MetaEngineFeature f) const override;
+    Common::Error createInstance(OSystem *syst, Engine **engine, const ADGameDescription *gd) const override;
+
+    int getMaximumSaveSlot() const override;
+	SaveStateList listSaves(const char *target) const override;
+	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const override;
+	void removeSaveState(const char *target, int slot) const override;
+};
+
+bool NancyMetaEngine::hasFeature(MetaEngineFeature f) const {
+	return
+	    (f == kSupportsListSaves) ||
+	    (f == kSupportsLoadingDuringStartup) ||
+	    (f == kSupportsDeleteSave) ||
+	    (f == kSavesSupportMetaInfo) ||
+	    (f == kSavesSupportThumbnail) ||
+	    (f == kSavesSupportCreationDate);
+}
+
+Common::Error NancyMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *gd) const {
+	if (gd) {
+		*engine = Nancy::NancyEngine::create(((const Nancy::NancyGameDescription *)gd)->gameType, syst, (const Nancy::NancyGameDescription *)gd);
+		((Nancy::NancyEngine *)*engine)->initGame((const Nancy::NancyGameDescription *)gd);
+	}
+	if (gd) {
+		return Common::kNoError;
+	}
+	else return Common::Error();
+}
+
+int NancyMetaEngine::getMaximumSaveSlot() const { return 99; }
+
+SaveStateList NancyMetaEngine::listSaves(const char *target) const {
+	Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
+	Common::StringArray filenames;
+	Common::String pattern = target;
+	pattern += "-??.SAV";
+
+	filenames = saveFileMan->listSavefiles(pattern);
+	sort(filenames.begin(), filenames.end());   // Sort (hopefully ensuring we are sorted numerically..)
+
+	SaveStateList saveList;
+	char slot[3];
+	int slotNum = 0;
+	for (Common::StringArray::const_iterator filename = filenames.begin(); filename != filenames.end(); ++filename) {
+		slot[0] = filename->c_str()[filename->size() - 6];
+		slot[1] = filename->c_str()[filename->size() - 5];
+		slot[2] = '\0';
+		// Obtain the last 2 digits of the filename (without extension), since they correspond to the save slot
+		slotNum = atoi(slot);
+		if (slotNum >= 0 && slotNum <= getMaximumSaveSlot()) {
+			Common::InSaveFile *file = saveFileMan->openForLoading(*filename);
+			if (file) {
+				int saveVersion = file->readByte();
+
+				if (saveVersion != Nancy::kSavegameVersion) {
+					warning("Savegame of incompatible version");
+					delete file;
+					continue;
+				}
+
+				// read name
+				uint16 nameSize = file->readUint16BE();
+				if (nameSize >= 255) {
+					delete file;
+					continue;
+				}
+				char name[256];
+				file->read(name, nameSize);
+				name[nameSize] = 0;
+
+				saveList.push_back(SaveStateDescriptor(slotNum, name));
+				delete file;
+			}
+		}
+	}
+
+	return saveList;
+}
+
+SaveStateDescriptor NancyMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
+	Common::String fileName = Common::String::format("%s-%02d.SAV", target, slot);
+	Common::InSaveFile *file = g_system->getSavefileManager()->openForLoading(fileName);
+
+	if (file) {
+		int saveVersion = file->readByte();
+
+		if (saveVersion != Nancy::kSavegameVersion) {
+			warning("Savegame of incompatible version");
+			delete file;
+			return SaveStateDescriptor();
+		}
+
+		uint32 saveNameLength = file->readUint16BE();
+		char saveName[256];
+		file->read(saveName, saveNameLength);
+		saveName[saveNameLength] = 0;
+
+		SaveStateDescriptor desc(slot, saveName);
+
+		Graphics::Surface *thumbnail = nullptr;
+
+		if (Graphics::loadThumbnail(*file, thumbnail))
+			desc.setThumbnail(thumbnail);
+
+		desc.setDeletableFlag(true);
+		desc.setWriteProtectedFlag(false);
+
+		uint32 saveDate = file->readUint32BE();
+		uint16 saveTime = file->readUint16BE();
+
+		int day = (saveDate >> 24) & 0xFF;
+		int month = (saveDate >> 16) & 0xFF;
+		int year = saveDate & 0xFFFF;
+
+		desc.setSaveDate(year, month, day);
+
+		int hour = (saveTime >> 8) & 0xFF;
+		int minutes = saveTime & 0xFF;
+
+		desc.setSaveTime(hour, minutes);
+
+		// Slot 0 is used for the 'restart game' save in all Nancy games, thus
+		// we prevent it from being deleted.
+		desc.setDeletableFlag(slot != 0);
+		desc.setWriteProtectedFlag(slot == 0);
+
+		delete file;
+		return desc;
+	}
+	return SaveStateDescriptor();
+}
+
+void NancyMetaEngine::removeSaveState(const char *target, int slot) const {
+	Common::String fileName = Common::String::format("%s-%02d.SAV", target, slot);
+	g_system->getSavefileManager()->removeSavefile(fileName);
+}
+
+#if PLUGIN_ENABLED_DYNAMIC(NANCY)
+    REGISTER_PLUGIN_DYNAMIC(NANCY, PLUGIN_TYPE_ENGINE, NancyMetaEngine);
+#else
+    REGISTER_PLUGIN_STATIC(NANCY, PLUGIN_TYPE_ENGINE, NancyMetaEngine);
+#endif
\ No newline at end of file
diff --git a/engines/nancy/module.mk b/engines/nancy/module.mk
index 4b3d16ac47..679ba8af11 100644
--- a/engines/nancy/module.mk
+++ b/engines/nancy/module.mk
@@ -1,14 +1,20 @@
 MODULE := engines/nancy
 
 MODULE_OBJS = \
+  action/recordtypes.o \
+  action/arfactory_v1.o \
   audio.o \
   console.o \
+  datatypes.o \
   decompress.o \
-  detection.o \
+  graphics.o \
   iff.o \
+  logic.o \
   logo.o \
+  metaengine.o \
   nancy.o \
   resource.o \
+  scene.o \
   video.o
 
 # This module can be built as a plugin
@@ -18,3 +24,6 @@ endif
 
 # Include common rules
 include $(srcdir)/rules.mk
+
+# Detection objects
+DETECT_OBJS += $(MODULE)/detection.o
diff --git a/engines/nancy/nancy.cpp b/engines/nancy/nancy.cpp
index accab2c23c..b042337478 100644
--- a/engines/nancy/nancy.cpp
+++ b/engines/nancy/nancy.cpp
@@ -20,6 +20,15 @@
  *
  */
 
+#include "engines/nancy/nancy.h"
+#include "engines/nancy/resource.h"
+#include "engines/nancy/iff.h"
+#include "engines/nancy/audio.h"
+#include "engines/nancy/logic.h"
+#include "engines/nancy/logo.h"
+#include "engines/nancy/scene.h"
+#include "engines/nancy/graphics.h"
+
 #include "common/system.h"
 #include "common/random.h"
 #include "common/error.h"
@@ -29,28 +38,22 @@
 #include "common/textconsole.h"
 #include "common/memstream.h"
 #include "common/installshield_cab.h"
+#include "common/str.h"
 
 #include "graphics/surface.h"
 
 #include "audio/mixer.h"
 #include "audio/audiostream.h"
 
-#include "nancy/nancy.h"
-#include "nancy/resource.h"
-#include "nancy/iff.h"
-#include "nancy/audio.h"
-#include "nancy/logo.h"
-
 #include "engines/util.h"
 
 namespace Nancy {
 
-NancyEngine *NancyEngine::s_Engine = 0;
+NancyEngine *NancyEngine::s_Engine = nullptr;
 
 NancyEngine::NancyEngine(OSystem *syst, const NancyGameDescription *gd) :
 	Engine(syst),
-	_gameDescription(gd),
-	_bsum(nullptr)
+	_gameDescription(gd)
 {
 	_system = syst;
 
@@ -67,14 +70,21 @@ NancyEngine::NancyEngine(OSystem *syst, const NancyGameDescription *gd) :
 
 	_console = new NancyConsole(this);
 	_logoSequence = new LogoSequence(this);
-	_rnd = 0;
+	_rnd = nullptr;
+
+	logic = new Logic(this);
+	sceneManager = new SceneManager(this);
+	graphics = new GraphicsManager(this);
 }
 
 NancyEngine::~NancyEngine() {
-
+	clearBootChunks();
 	DebugMan.clearAllDebugChannels();
 	delete _console;
 	delete _rnd;
+	
+	delete logic;
+	delete sceneManager;
 }
 
 GUI::Debugger *NancyEngine::getDebugger() {
@@ -82,7 +92,7 @@ GUI::Debugger *NancyEngine::getDebugger() {
 }
 
 bool NancyEngine::hasFeature(EngineFeature f) const {
-	return (f == kSupportsRTL) || (f == kSupportsLoadingDuringRuntime) || (f == kSupportsSavingDuringRuntime);
+	return (f == kSupportsReturnToLauncher) || (f == kSupportsLoadingDuringRuntime) || (f == kSupportsSavingDuringRuntime);
 }
 
 const char *NancyEngine::getCopyrightString() const {
@@ -100,8 +110,7 @@ Common::Platform NancyEngine::getPlatform() const {
 Common::Error NancyEngine::run() {
 	s_Engine = this;
 
-	Graphics::PixelFormat format(2, 5, 5, 5, 0, 10, 5, 0, 0);
-	initGraphics(640, 480, &format);
+	initGraphics(640, 480, &GraphicsManager::pixelFormat);
 	_console = new NancyConsole(this);
 
 	const Common::FSNode gameDataDir(ConfMan.get("path"));
@@ -112,9 +121,13 @@ Common::Error NancyEngine::run() {
 	SearchMan.addSubDirectoryMatching(gameDataDir, "hdvideo");
 	SearchMan.addSubDirectoryMatching(gameDataDir, "cdvideo");
 
-	Common::SeekableReadStream *cabStream = SearchMan.createReadStreamForMember("data1.hdr");
-	if (cabStream)
-		SearchMan.add("data1.hdr", Common::makeInstallShieldArchive(cabStream));
+	Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember("data1.cab");
+		if (!stream)
+			error("Failed to open data1.cab");
+
+		Common::Archive *cab = Common::makeInstallShieldArchive(stream);
+	if (cab)
+		SearchMan.add("data1.hdr", cab);
 
 //	_mouse = new MouseHandler(this);
 	_res = new ResourceManager(this);
@@ -123,28 +136,26 @@ Common::Error NancyEngine::run() {
 	// Setup mixer
 	syncSoundSettings();
 
-	// Some bits and pieces of the engine in order to make something happen
-	IFF *boot = new IFF(this, "boot");
-	if (!boot->load())
-		error("Failed to load boot script");
-	preloadCals(*boot);
-	readSound(*boot, "MSND", _menuSound);
-	_bsum = boot->getChunkStream("BSUM");
-	if (!_bsum)
-		error("Failed to load BOOT BSUM");
-	readBootSummary(*boot);
-	delete boot;
-
 	Common::EventManager *ev = g_system->getEventManager();
 	bool quit = false;
 
-	_gameFlow.minGameState = kLogo;
+	_gameFlow.minGameState = kBoot;
 
 	while (!shouldQuit() && !quit) {
-		switch(_gameFlow.minGameState) {
+		switch (_gameFlow.minGameState) {
+		case kBoot:
+			bootGameEngine();
+			graphics->init();
+			_gameFlow.minGameState = kLogo;
+			break;
 		case kLogo:
-			_logoSequence->doIt();
+			_logoSequence->process();
 			break;
+		case kMainMenu:
+			// TODO
+			break;
+		case kScene:
+			sceneManager->process();
 		case kIdle:
 		default:
 			break;
@@ -153,7 +164,7 @@ Common::Error NancyEngine::run() {
 		Common::Event event;
 		if (ev->pollEvent(event)) {
 			if (event.type == Common::EVENT_KEYDOWN && (event.kbd.flags & Common::KBD_CTRL)) {
-				switch(event.kbd.keycode) {
+				switch (event.kbd.keycode) {
 				case Common::KEYCODE_q:
 					quit = true;
 					break;
@@ -165,6 +176,8 @@ Common::Error NancyEngine::run() {
 			}
 		}
 
+		_console->onFrame();
+
 		_system->updateScreen();
 		_system->delayMillis(16);
 	}
@@ -191,6 +204,70 @@ Common::Error NancyEngine::run() {
 	return Common::kNoError;
 }
 
+void NancyEngine::bootGameEngine() {
+	clearBootChunks();
+	IFF *boot = new IFF(this, "boot");
+	if (!boot->load())
+		error("Failed to load boot script");
+	preloadCals(*boot);
+	readSound(*boot, "MSND", _menuSound);
+
+	addBootChunk("BSUM", boot->getChunkStream("BSUM"));
+	readBootSummary(*boot);
+
+	Common::String names[] = {
+		"INTR", "HINT", "LOGO", "SPUZ", "INV",
+		"FONT", "MENU", "HELP", "CRED", "LOAD",
+		"MAP", "CD", "TBOX", "CURS", "VIEW", "MSND",
+		"BUOK", "BUDE", "BULS", "GLOB", "SLID",
+		"SET", "CURT", "CANT", "TH1", "TH2",
+		"QUOT", "TMOD"
+		};
+
+	for (auto const &n : names) {
+		addBootChunk(n, boot->getChunkStream(n));
+	}
+
+	// The FR, LG and OB chunks get added here	
+
+	Common::SeekableReadStream *font = getBootChunkStream("FONT");
+	if (_fontSize != font->size()) {
+		error("Mismatch NumFonts and FONT memory... %i, %i", _fontSize, font->size());
+	}
+	// TODO a loop that uses FONT
+	// TODO another loop that does the same thing but with CURS
+	// TODO reset some vars
+
+	for (uint i = 0; i <= 60; ++i) {
+		graphics->clearGenericZRenderStruct(i);
+	}
+
+	// TODO reset some more vars
+
+	delete boot;
+}
+
+bool NancyEngine::addBootChunk(const Common::String &name, Common::SeekableReadStream *stream) {
+	if (!stream)
+		return false;
+	_bootChunks[name] = stream;
+	return true;
+}
+
+Common::SeekableReadStream *NancyEngine::getBootChunkStream(const Common::String &name) {
+	if (_bootChunks.contains(name)) {
+		return _bootChunks[name];
+	}
+	else return nullptr;
+}
+
+void NancyEngine::clearBootChunks() {
+	for (auto const& i : _bootChunks) {
+		delete i._value;
+	}
+	_bootChunks.clear();
+}
+
 void NancyEngine::initialize() {
 	debugC(1, kDebugEngine, "initialize");
 
@@ -245,7 +322,8 @@ Common::String NancyEngine::readFilename(Common::ReadStream *stream) const {
 }
 
 void NancyEngine::readImageList(const IFF &boot, const Common::String &prefix, ImageList &list) {
-	byte count = _bsum->readByte();
+	Common::SeekableReadStream *bsum = getBootChunkStream("BSUM");
+	byte count = bsum->readByte();
 	debugC(1, kDebugEngine, "Found %i %s images", count, prefix.c_str());
 
 	for (int i = 0; i < count; ++i) {
@@ -290,9 +368,15 @@ private:
 };
 
 void NancyEngine_v0::readBootSummary(const IFF &boot) {
-	_bsum->seek(0x151);
+	Common::SeekableReadStream *bsum = getBootChunkStream("BSUM");
+	bsum->seek(0xa3);
+	_firstSceneID = bsum->readUint16LE();
+	bsum->seek(0x151);
 	readImageList(boot, "FR", _frames);
 	readImageList(boot, "LG", _logos);
+	readImageList(boot, "OB", _objects);
+	bsum->seek(0x1D1);
+	_fontSize = bsum->readSint32LE() * 1346;
 }
 
 class NancyEngine_v1 : public NancyEngine_v0 {
@@ -304,7 +388,10 @@ private:
 };
 
 void NancyEngine_v1::readBootSummary(const IFF &boot) {
-	_bsum->seek(0x14b);
+	Common::SeekableReadStream *bsum = getBootChunkStream("BSUM");
+	bsum->seek(0xa3);
+	_firstSceneID = bsum->readUint16LE();
+	bsum->seek(0x14b);
 	readImageList(boot, "FR", _frames);
 	readImageList(boot, "LG", _logos);
 }
@@ -319,13 +406,14 @@ private:
 };
 
 void NancyEngine_v2::readBootSummary(const IFF &boot) {
-	_bsum->seek(0xa7);
+	Common::SeekableReadStream *bsum = getBootChunkStream("BSUM");
+	bsum->seek(0xa7);
 	readImageList(boot, "FR", _frames);
 	readImageList(boot, "LG", _logos);
 }
 
 NancyEngine *NancyEngine::create(GameType type, OSystem *syst, const NancyGameDescription *gd) {
-	switch(type) {
+	switch (type) {
 	case kGameTypeNancy1:
 		return new NancyEngine_v0(syst, gd);
 	case kGameTypeNancy2:
@@ -337,4 +425,4 @@ NancyEngine *NancyEngine::create(GameType type, OSystem *syst, const NancyGameDe
 	}
 }
 
-} // End of namespace Nancy
+} // End of namespace Nancy
\ No newline at end of file
diff --git a/engines/nancy/nancy.h b/engines/nancy/nancy.h
index 5b61e15839..82f3d19df3 100644
--- a/engines/nancy/nancy.h
+++ b/engines/nancy/nancy.h
@@ -23,12 +23,12 @@
 #ifndef NANCY_H
 #define NANCY_H
 
+#include "nancy/console.h"
+#include "nancy/detection.h"
+
 #include "engines/engine.h"
 #include "common/file.h"
 
-#include "nancy/console.h"
-#include "nancy/logo.h"
-
 namespace Common {
 class RandomSource;
 }
@@ -46,13 +46,6 @@ namespace Nancy {
 
 static const int kSavegameVersion = 1;
 
-enum GameType {
-	kGameTypeNone = 0,
-	kGameTypeNancy1,
-	kGameTypeNancy2,
-	kGameTypeNancy3
-};
-
 enum NancyDebugChannels {
 	kDebugSchedule  = 1 <<  0,
 	kDebugEngine    = 1 <<  1,
@@ -71,10 +64,16 @@ struct NancyGameDescription;
 class ResourceManager;
 class IFF;
 class LogoSequence;
+class SceneManager;
+class Logic;
+class GraphicsManager;
 
 class NancyEngine : public Engine {
 public:
+	friend class Logic;
 	friend class LogoSequence;
+	friend class SceneManager;
+	friend class NancyConsole;
 
 	NancyEngine(OSystem *syst, const NancyGameDescription *gd);
 	~NancyEngine();
@@ -85,7 +84,6 @@ public:
 
 	Common::RandomSource *_rnd;
 
-	ResourceManager *_res;
 
 	const NancyGameDescription *_gameDescription;
 	uint32 getFeatures() const;
@@ -103,11 +101,25 @@ public:
 	void syncSoundSettings();
 
 	static NancyEngine *create(GameType type, OSystem *syst, const NancyGameDescription *gd);
+	
+	// Chunks found in BOOT get extracted and cached at startup, this function lets other classes access them
+	Common::SeekableReadStream *getBootChunkStream(const Common::String &name);
+
+	// Managers
+	ResourceManager *_res;
+	Logic *logic;
+	SceneManager *sceneManager;
+	GraphicsManager *graphics;
 
 protected:
 	// Engine APIs
 	Common::Error run();
 
+	void bootGameEngine();
+
+	bool addBootChunk(const Common::String &name, Common::SeekableReadStream *stream);
+	void clearBootChunks();
+
 	enum {
 		kMaxFilenameLen = 32
 	};
@@ -124,18 +136,35 @@ protected:
 
 	enum GameState {
 		kBoot,
+		kPartnerLogo, // v2 only
 		kLogo,
+		kCredits,
+		kMap, // v0, v1 only
+		kMainMenu,
+		kLoadSave,
+		kSetup,
+		// unknown/invalid
+		kHelp,
+		kScene,
+		// CD change
+		kCheat,
+		kQuit,
+		// regain focus
 		kIdle
 	};
 
 	struct GameFlow {
 		GameState minGameState;
+		GameState previousGameState;
 	};
 
 	typedef Common::Array<Image> ImageList;
 
-	Common::SeekableReadStream *_bsum;
-	ImageList _logos, _frames;
+	ImageList _logos;
+	ImageList _frames;
+	ImageList _objects;
+	uint16 _firstSceneID;
+	int32 _fontSize;
 	Sound _menuSound;
 	GameFlow _gameFlow;
 
@@ -155,10 +184,11 @@ private:
 	Common::Platform _platform;
 
 	LogoSequence *_logoSequence;
+	Common::HashMap<Common::String, Common::SeekableReadStream *> _bootChunks;
 
 	void initialize();
 };
 
 } // End of namespace Nancy
 
-#endif // Nancy_H
+#endif // NANCY_H
\ No newline at end of file
diff --git a/engines/nancy/playstate.h b/engines/nancy/playstate.h
new file mode 100644
index 0000000000..67ebce2741
--- /dev/null
+++ b/engines/nancy/playstate.h
@@ -0,0 +1,53 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef NANCY_PLAYSTATE_H
+#define NANCY_PLAYSTATE_H
+
+#include "engines/nancy/time.h"
+
+namespace Nancy {
+
+// A catch-all struct for storing all player progress and related variables
+// TODO split to PlayerState/SceneState
+struct PlayState {
+    enum Flag { kFalse = 1, kTrue = 2 };
+
+    Flag inventory[11];
+    Flag eventFlags[672];
+    // Second array with the same size as EventFlags that never gets used?
+    bool sceneHitCount[1000];
+    uint16 difficulty; // 0, 1, 2
+    Time totalTime;
+    Time sceneTime;
+    Time timerTime;
+    bool timerIsActive = false;
+    uint16 currentViewFrame = 0;
+    uint16 queuedViewFrame = 0;
+    uint16 currentMaxVerticalScroll = 0;
+    uint16 queuedMaxVerticalScroll = 0;
+    uint16 verticalScroll = 0; // This replaces rDisplayed
+};
+
+} // End of namespace Nancy
+
+#endif // NANCY_PLAYSTATE_H
\ No newline at end of file
diff --git a/engines/nancy/resource.cpp b/engines/nancy/resource.cpp
index 63cea43692..1fe57e1373 100644
--- a/engines/nancy/resource.cpp
+++ b/engines/nancy/resource.cpp
@@ -20,18 +20,21 @@
  *
  */
 
+#include "engines/nancy/resource.h"
+#include "engines/nancy/decompress.h"
+#include "engines/nancy/graphics.h"
+
 #include "common/file.h"
 #include "common/textconsole.h"
 #include "common/debug.h"
 #include "common/stream.h"
 #include "common/memstream.h"
+
 #include "graphics/surface.h"
-#include "nancy/resource.h"
-#include "nancy/decompress.h"
 
 namespace Nancy {
 
-static void readCifInfo20(Common::File &f, ResourceManager::CifInfo &info, uint32 *dataOffset = 0) {
+static void readCifInfo20(Common::File &f, ResourceManager::CifInfo &info, uint32 *dataOffset = nullptr) {
 	info.width = f.readUint16LE();
 	info.pitch = f.readUint16LE();
 	info.height = f.readUint16LE();
@@ -53,7 +56,7 @@ public:
 	virtual ~CifFile();
 
 	bool initialize();
-	byte *getCifData(ResourceManager::CifInfo &info, uint *size = 0) const;
+	byte *getCifData(ResourceManager::CifInfo &info, uint *size = nullptr) const;
 	void getCifInfo(ResourceManager::CifInfo &info) const;
 
 	static const CifFile *load(const Common::String &name);
@@ -129,7 +132,7 @@ void CifFile21::readCifInfo(Common::File &f) {
 
 const CifFile *CifFile::load(const Common::String &name) {
 	Common::File *f = new Common::File;
-	CifFile *cifFile = 0;
+	CifFile *cifFile = nullptr;
 
 	if (!f->open(name + ".cif")) {
 		delete f;
@@ -154,7 +157,7 @@ const CifFile *CifFile::load(const Common::String &name) {
 	ver = f->readUint16LE() << 16;
 	ver |= f->readUint16LE();
 
-	switch(ver) {
+	switch (ver) {
 	case 0x00020000:
 		cifFile = new CifFile20(name, f);
 		break;
@@ -183,8 +186,8 @@ public:
 
 	bool initialize();
 	void list(Common::Array<Common::String> &nameList, uint type) const;
-	byte *getCifData(const Common::String &name, ResourceManager::CifInfo &info, uint *size = 0) const;
-	bool getCifInfo(const Common::String &name, ResourceManager::CifInfo &info, uint32 *dataOffset = 0) const;
+	byte *getCifData(const Common::String &name, ResourceManager::CifInfo &info, uint *size = nullptr) const;
+	bool getCifInfo(const Common::String &name, ResourceManager::CifInfo &info, uint32 *dataOffset = nullptr) const;
 	const Common::String &getName() const { return _name; }
 
 	static const CifTree *load(const Common::String &name, const Common::String &ext);
@@ -420,7 +423,7 @@ void CifTree21::determineSubtype(Common::File &f) {
 
 const CifTree *CifTree::load(const Common::String &name, const Common::String &ext) {
 	Common::File f;
-	CifTree *cifTree = 0;
+	CifTree *cifTree = nullptr;
 
 	if (!f.open(name + '.' + ext)) {
 		warning("Failed to open CifTree '%s'", name.c_str());
@@ -447,7 +450,7 @@ const CifTree *CifTree::load(const Common::String &name, const Common::String &e
 
 	f.close();
 
-	switch(ver) {
+	switch (ver) {
 	case 0x00020000:
 		cifTree = new CifTree20(name, ext);
 		break;
@@ -461,7 +464,7 @@ const CifTree *CifTree::load(const Common::String &name, const Common::String &e
 	if (cifTree && !cifTree->initialize()) {
 		warning("Failed to read CifTree '%s'", name.c_str());
 		delete cifTree;
-		cifTree = 0;
+		cifTree = nullptr;
 	}
 
 	return cifTree;
@@ -546,7 +549,7 @@ void CifExporter21::writeCifInfo(Common::DumpFile &f, const ResourceManager::Cif
 const CifExporter *CifExporter::create(uint32 version) {
 	const CifExporter *exp;
 
-	switch(version) {
+	switch (version) {
 	case 0x00020000:
 		exp = new CifExporter20;
 		break;
@@ -713,12 +716,14 @@ bool ResourceManager::loadImage(const Common::String &treeName, const Common::St
 		return false;
 	}
 
-	Graphics::PixelFormat format(2, 5, 5, 5, 0, 10, 5, 0, 0);
 	surf.w = info.width;
 	surf.h = info.height;
 	surf.pitch = info.pitch;
+	// Surface's conversion functions do not work the exact way as this
+	//use after fixing pixel format
+	//colorCorrect(buf, info.size);
 	surf.setPixels(buf);
-	surf.format = format;
+	surf.format = GraphicsManager::pixelFormat;
 	return true;
 }
 
@@ -753,4 +758,14 @@ Common::String ResourceManager::getCifDescription(const Common::String &treeName
 	return desc;
 }
 
-} // End of namespace Nancy
+void ResourceManager::colorCorrect(byte *buf, uint size) {
+	byte *last = buf + size;
+	for (byte *cur = buf; cur < last; cur += 2) {
+		uint16 pre = *cur | (*(cur+1) << 8);
+		uint16 post = ((pre & 0xFFE0) << 1) | (pre & 0x1F);
+		*cur = post & 0xFF;
+		*(cur+1) = post >> 8;
+	}
+}
+
+} // End of namespace Nancy
\ No newline at end of file
diff --git a/engines/nancy/resource.h b/engines/nancy/resource.h
index f7b53ad38e..121cc8ce7d 100644
--- a/engines/nancy/resource.h
+++ b/engines/nancy/resource.h
@@ -23,9 +23,8 @@
 #ifndef NANCY_RESOURCE_H
 #define NANCY_RESOURCE_H
 
-namespace Common {
-class String;
-}
+#include "common/str.h"
+#include "common/array.h"
 
 namespace Graphics {
 struct Surface;
@@ -78,13 +77,15 @@ private:
 	NancyEngine *_vm;
 	Decompressor *_dec;
 
-	byte *getCifData(const Common::String &treeName, const Common::String &name, CifInfo &info, uint *size = 0);
+	byte *getCifData(const Common::String &treeName, const Common::String &name, CifInfo &info, uint *size = nullptr);
 	bool getCifInfo(const Common::String &treeName, const Common::String &name, CifInfo &info);
 	const CifTree *findCifTree(const Common::String &name) const;
 
+	void colorCorrect(byte *buf, uint size);
+
 	Common::Array<const CifTree *> _cifTrees;
 };
 
 } // End of namespace Nancy
 
-#endif
+#endif // NANCY_RESOURCE_H
\ No newline at end of file
diff --git a/engines/nancy/scene.cpp b/engines/nancy/scene.cpp
new file mode 100644
index 0000000000..24fb71958b
--- /dev/null
+++ b/engines/nancy/scene.cpp
@@ -0,0 +1,439 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/nancy/scene.h"
+#include "engines/nancy/nancy.h"
+#include "engines/nancy/resource.h"
+#include "engines/nancy/iff.h"
+#include "engines/nancy/logic.h"
+#include "engines/nancy/graphics.h"
+
+#include "common/memstream.h"
+#include "common/rect.h"
+#include "common/func.h"
+
+#include "graphics/surface.h"
+
+namespace Nancy {
+
+SceneManager::~SceneManager() {
+    clearSceneData();
+}
+
+void SceneManager::process() {
+    switch (_state) {
+    case kInit:
+        init();
+        break;
+    case kLoad:
+        load();
+        break;
+    case kStartSound:
+        // TODO
+        break;
+    case kRun:
+        run();
+        break;
+    case kLoadNew:
+        _state = kLoad;
+        break;
+    }
+}
+
+void SceneManager::init() {
+    for (uint i = 0; i < 672; ++i) {
+        playState.eventFlags[i] = PlayState::Flag::kFalse;
+    }
+
+    for (uint i = 0; i < 1000; ++i) {
+        playState.sceneHitCount[i] = 0;
+    }
+
+    _sceneID = _engine->_firstSceneID;
+    _engine->_gameFlow.previousGameState = NancyEngine::kScene;
+
+    // TODO init action records
+    
+    for (uint i = 0; i <= 60; ++i) {
+		_engine->graphics->clearGenericZRenderStruct(i);
+	}
+
+    // Load the primary frame
+    if (!_engine->_res->loadImage("ciftree", _engine->_frames[0].name, _engine->graphics->_primaryFrameSurface)) {
+        error("Failed to load %s", _engine->_frames[0].name.c_str());
+    }
+
+    char *name = new char[10];
+
+    // Load the Object 0 image
+    if (!_engine->_res->loadImage("ciftree", _engine->_objects[0].name, _engine->graphics->_object0Surface)) {
+        error("Failed to load %s", _engine->_objects[0].name.c_str());
+    }
+
+    // Load inventory icons
+    Common::SeekableReadStream *inv = _engine->getBootChunkStream("INV");
+    inv->seek(0x1C8);
+    inv->read(name, 10); // should be TOOL
+    if (!_engine->_res->loadImage("ciftree", Common::String(name), _engine->graphics->_inventoryBoxIconsSurface)) {
+        error("Failed to load inventory icons (TOOL)");
+    }
+
+    // Load the cursors
+    inv->read(name, 10); // should be TOOLCUR1
+    if (!_engine->_res->loadImage("ciftree", Common::String(name), _engine->graphics->_inventoryCursorsSurface)) {
+        error("Failed to load cursors (TOOLCUR1)");
+    }
+
+    delete[] name;
+
+    // TODO init various rects for the ZRender structs
+
+    // Init the ZRender structs themselves:
+    // TODO most of these are wrong and/or incomplete
+    #define READ_SOURCE_RECT(x) chunk->seek(x); source = new Common::Rect(chunk->readUint32LE(), chunk->readUint32LE(), chunk->readUint32LE(), chunk->readUint32LE());
+    #define READ_DEST_POINT(x) chunk->seek(x); dest = new Common::Point(chunk->readUint32LE(), chunk->readUint32LE());
+
+    Common::SeekableReadStream *chunk;
+    Common::Rect *source;
+    Common::Point *dest;
+
+    // CUR: current
+    // RES: redraws the background of a moved element so it doesnt get doubled
+    // VP: viewport
+
+    chunk = _engine->getBootChunkStream("MENU");
+    READ_SOURCE_RECT(16)
+    READ_DEST_POINT(16)
+    _engine->graphics->initGenericZRenderStruct(0, "FRAME", 1, true, ZRenderStruct::BltType::kNone, &_engine->graphics->_primaryFrameSurface,
+                                                new Common::Functor0Mem<void, GraphicsManager>(_engine->graphics, GraphicsManager::renderFrame),
+                                                source, dest);
+    _engine->graphics->initGenericZRenderStruct(2, "CUR IMAGE CURSOR", 11, false, ZRenderStruct::BltType::kTrans,
+                                                &_engine->graphics->_object0Surface, nullptr, new Common::Rect(), new Common::Point());
+    _engine->graphics->initGenericZRenderStruct(3, "RES IMAGE CURSOR - FRAME", 2, false, ZRenderStruct::BltType::kNoTrans,
+                                                &_engine->graphics->_primaryFrameSurface, nullptr, new Common::Rect(), new Common::Point());
+    _engine->graphics->initGenericZRenderStruct(4, "RES IMAGE CURSOR - VP", 3, false, ZRenderStruct::BltType::kNoTrans,
+                                                &_engine->graphics->_background, nullptr, new Common::Rect(), new Common::Point());
+    // Skip DIAGNOSTICS and VERSION
+
+    chunk = _engine->getBootChunkStream("TBOX");
+    READ_SOURCE_RECT(0)
+    _engine->graphics->initGenericZRenderStruct(9, "CUR TB BAT SLIDER", 9, false, ZRenderStruct::BltType::kTrans,
+                                                &_engine->graphics->_object0Surface, nullptr, source, new Common::Point());           
+
+    _engine->graphics->initGenericZRenderStruct(10, "RES TB BAT SLIDER", 3, false, ZRenderStruct::BltType::kNoTrans, &_engine->graphics->_primaryFrameSurface,
+                                               new Common::Functor0Mem<void, GraphicsManager>(_engine->graphics, GraphicsManager::renderResTBBatSlider),
+                                               new Common::Rect(), new Common::Point());
+
+    chunk = _engine->getBootChunkStream("BSUM");
+    READ_DEST_POINT(356)
+    _engine->graphics->initGenericZRenderStruct(8, "FRAME TB SURF", 6, false, ZRenderStruct::BltType::kNoTrans,
+                                                &_engine->graphics->_frameTextBox, nullptr, source, dest);
+
+    READ_SOURCE_RECT(356)
+    READ_DEST_POINT(356)
+    _engine->graphics->initGenericZRenderStruct(11, "TB FRAME RES", 2, false, ZRenderStruct::BltType::kNoTrans,
+                                                &_engine->graphics->_primaryFrameSurface, nullptr, source, dest);  
+
+    READ_SOURCE_RECT(388)
+    READ_DEST_POINT(420)
+    _engine->graphics->initGenericZRenderStruct(12, "MENU BUT DN", 5, false, ZRenderStruct::BltType::kTrans,
+                                                &_engine->graphics->_object0Surface, nullptr, source, dest);
+    READ_SOURCE_RECT(404)
+    READ_DEST_POINT(436)
+    _engine->graphics->initGenericZRenderStruct(13, "HELP BUT DN", 5, false, ZRenderStruct::BltType::kTrans,
+                                                &_engine->graphics->_object0Surface, nullptr, source, dest);
+    
+    chunk = _engine->getBootChunkStream("INV");
+    READ_SOURCE_RECT(0)
+    _engine->graphics->initGenericZRenderStruct(14, "CUR INV SLIDER", 9, false, ZRenderStruct::BltType::kTrans,
+                                                &_engine->graphics->_object0Surface, nullptr, source, new Common::Point());
+
+    _engine->graphics->initGenericZRenderStruct(15, "RES INV SLIDER", 3, false, ZRenderStruct::BltType::kNoTrans,
+                                                &_engine->graphics->_primaryFrameSurface, nullptr, new Common::Rect(), new Common::Point());
+
+    _engine->graphics->initGenericZRenderStruct(16, "FRAME INV BOX", 6, false, ZRenderStruct::BltType::kNoTrans, nullptr,
+                                                new Common::Functor0Mem<void, GraphicsManager>(_engine->graphics, GraphicsManager::renderFrameInvBox),
+                                                nullptr, nullptr);
+
+    _engine->graphics->initGenericZRenderStruct(5, "INV BITMAP", 9, false, ZRenderStruct::BltType::kNoTrans,
+                                                nullptr, nullptr, new Common::Rect(), new Common::Point());
+
+    _engine->graphics->initGenericZRenderStruct(17, "PRIMARY VIDEO", 8, false, ZRenderStruct::BltType::kNoTrans, nullptr,
+                                                new Common::Functor0Mem<void, GraphicsManager>(_engine->graphics, GraphicsManager::renderPrimaryVideo),
+                                                new Common::Rect(), new Common::Point());
+
+    _engine->graphics->initGenericZRenderStruct(18, "SEC VIDEO 0", 8, false, ZRenderStruct::BltType::kTrans, nullptr,
+                                                new Common::Functor0Mem<void, GraphicsManager>(_engine->graphics, GraphicsManager::renderSecVideo0),
+                                                new Common::Rect(), new Common::Point());
+
+    _engine->graphics->initGenericZRenderStruct(19, "SEC VIDEO 1", 8, false, ZRenderStruct::BltType::kTrans, nullptr,
+                                                new Common::Functor0Mem<void, GraphicsManager>(_engine->graphics, GraphicsManager::renderSecVideo1),
+                                                new Common::Rect(), new Common::Point());
+    
+    _engine->graphics->initGenericZRenderStruct(20, "SEC MOVIE", 8, false, ZRenderStruct::BltType::kNoTrans, nullptr,
+                                                new Common::Functor0Mem<void, GraphicsManager>(_engine->graphics, GraphicsManager::renderSecMovie),
+                                                new Common::Rect(), new Common::Point());
+    
+    _engine->graphics->initGenericZRenderStruct(21, "ORDERING PUZZLE", 7, false, ZRenderStruct::BltType::kNoTrans, nullptr,
+                                                new Common::Functor0Mem<void, GraphicsManager>(_engine->graphics, GraphicsManager::renderOrderingPuzzle),
+                                                nullptr);
+    
+    _engine->graphics->initGenericZRenderStruct(22, "ROTATING LOCK PUZZLE", 7, false, ZRenderStruct::BltType::kNoTrans, nullptr,
+                                                new Common::Functor0Mem<void, GraphicsManager>(_engine->graphics, GraphicsManager::renderRotatingLockPuzzle),
+                                                nullptr, nullptr);
+
+    _engine->graphics->initGenericZRenderStruct(23, "LEVER PUZZLE", 7, false, ZRenderStruct::BltType::kNoTrans, nullptr,
+                                                new Common::Functor0Mem<void, GraphicsManager>(_engine->graphics, GraphicsManager::renderLeverPuzzle),
+                                                nullptr, nullptr);
+
+    _engine->graphics->initGenericZRenderStruct(24, "TELEPHONE", 7, false, ZRenderStruct::BltType::kNoTrans, nullptr,
+                                                new Common::Functor0Mem<void, GraphicsManager>(_engine->graphics, GraphicsManager::renderTelephone),
+                                                nullptr, nullptr);
+    
+    _engine->graphics->initGenericZRenderStruct(25, "SLIDER PUZZLE", 7, false, ZRenderStruct::BltType::kNoTrans, nullptr,
+                                                new Common::Functor0Mem<void, GraphicsManager>(_engine->graphics, GraphicsManager::renderSliderPuzzle),
+                                                nullptr, nullptr);
+    
+    _engine->graphics->initGenericZRenderStruct(26, "PASSWORD PUZZLE", 7, false, ZRenderStruct::BltType::kNoTrans, nullptr,
+                                                new Common::Functor0Mem<void, GraphicsManager>(_engine->graphics, GraphicsManager::renderPasswordPuzzle),
+                                                nullptr, nullptr);
+    #undef READ_SOURCE_RECT
+    #undef READ_DEST_POINT
+
+    _state = kLoad;
+}
+
+void SceneManager::load() {
+
+    // Scene IDs are prefixed with S inside the cif tree; e.g 100 -> S100                                                                                    
+    Common::String sceneName = Common::String::format("S%u", _sceneID);
+    IFF sceneIFF(_engine, sceneName);
+	if (!sceneIFF.load()) {
+		error("Faled to load IFF %s", sceneName.c_str());
+	}
+
+    Common::SeekableReadStream *sceneSummaryChunk = sceneIFF.getChunkStream("SSUM");
+    if (!sceneSummaryChunk) {
+        error("Invalid IFF Chunk SSUM");
+    }
+
+    currentScene = SceneSummary(sceneSummaryChunk);
+
+    // The check to see if we need to switch the CD is performed here
+
+    // Not sure what these do yet
+    Common::SeekableReadStream *bsum = _engine->getBootChunkStream("BSUM");
+    bsum->seek(0x1F1);
+    byte unknown = bsum->readByte();
+    if (unknown) {
+        currentScene.unknown78 = bsum->readUint16LE();
+        currentScene.unknown7A = bsum->readUint16LE();
+    }
+
+    // Search for Action Records, maximum for a scene is 30
+    Common::SeekableReadStream *actionRecordChunk = nullptr;
+
+    while (actionRecordChunk = sceneIFF.getChunkStream("ACT", _engine->logic->_records.size()), actionRecordChunk != nullptr)
+    {
+        if (_engine->logic->_records.size() >= 30) {
+            error("Invalid number of Action Records");
+        }
+
+        _engine->logic->addNewActionRecord(*actionRecordChunk);
+    }
+    _engine->graphics->loadBackgroundVideo(currentScene.videoFile);
+    if (_engine->graphics->getBackgroundFrameCount() == 1) {
+        currentScene.sceneHasMovement = 0;
+    }
+
+    View &viewportDesc = _engine->graphics->viewportDesc;
+
+    if (!hasLoadedFromSavefile) {
+        playState.currentMaxVerticalScroll = playState.queuedMaxVerticalScroll;
+        playState.currentViewFrame = playState.queuedViewFrame;
+
+        if (currentScene.videoFormat == 1) {
+            // TODO not sure this ever gets hit
+        } else if (currentScene.videoFormat == 2) {
+            // always start from the bottom
+            playState.verticalScroll = playState.currentMaxVerticalScroll;
+        } else {
+            error("Unrecognized Scene summary chunk video file format");
+        }
+
+        // Some checks against rFrame
+
+        if (currentScene.videoFormat == 1) {
+            // TODO not sure this ever gets hit
+        } else if (currentScene.videoFormat == 2) {
+            if (_engine->graphics->getBackgroundHeight() == viewportDesc.f2Bottom) {
+                currentScene.sceneHasMovement = false;
+            }
+        }
+    }
+
+    delete sceneSummaryChunk;
+
+    Common::Point *dest = new Common::Point(viewportDesc.destLeft, viewportDesc.destTop);
+    Common::Rect *source = new Common::Rect(viewportDesc.srcLeft, viewportDesc.srcTop, viewportDesc.srcRight, viewportDesc.srcBottom);
+   _engine->graphics->initGenericZRenderStruct(2, "VIEWPORT AVF", 6, true, ZRenderStruct::BltType::kNoTrans,
+                                                &_engine->graphics->_background, nullptr, source, dest);
+
+    _state = kRun; // TODO temp, is actually StartSound
+}
+
+void SceneManager::run() {
+    if (isComingFromMenu) {
+        // TODO
+    }
+    isComingFromMenu = false;
+
+    if (orderingPuzzleIsActive)
+        _engine->graphics->getGenericZRenderStruct(21)->isActive = true;
+    if (rotatingLockPuzzleIsActive)
+        _engine->graphics->getGenericZRenderStruct(22)->isActive = true;
+    if (leverPuzzleIsActive)
+        _engine->graphics->getGenericZRenderStruct(23)->isActive = true;
+    if (sliderPuzzleIsActive)
+        _engine->graphics->getGenericZRenderStruct(25)->isActive = true;
+    if (passwordPuzzleIsActive)
+        _engine->graphics->getGenericZRenderStruct(26)->isActive = true;
+    if (telephoneIsActive)
+        _engine->graphics->getGenericZRenderStruct(24)->isActive = true;
+
+    if (helpMenuRequested) {
+        _stashedTickCount = _engine->getTotalPlayTime();
+        _engine->_gameFlow.previousGameState = NancyEngine::GameState::kScene;
+        _engine->_gameFlow.minGameState = NancyEngine::GameState::kHelp;
+        // _engine->helpMenu->state = HelpMenu::State::kInit;
+        helpMenuRequested = false;
+        // _engine->sound->stopSceneSpecificSounds();
+        return;
+    }
+
+    if (mainMenuRequested) {
+        _stashedTickCount = _engine->getTotalPlayTime();
+        _engine->_gameFlow.previousGameState = NancyEngine::GameState::kScene;
+        _engine->_gameFlow.minGameState = NancyEngine::GameState::kMainMenu;
+        // _engine->mainMenu->state = MainMenu::State::kInit;
+        mainMenuRequested = false;
+        // _engine->sound->stopSceneSpecificSounds();
+        return;
+    }
+
+    if (saveLoadRequested) {
+        _stashedTickCount = _engine->getTotalPlayTime();
+        _engine->_gameFlow.previousGameState = NancyEngine::GameState::kScene;
+        _engine->_gameFlow.minGameState = NancyEngine::GameState::kLoadSave;
+        // _engine->loadSaveMenu->state = LoadSaveMenu::State::kInit;
+        saveLoadRequested = false;
+        // _engine->sound->stopSceneSpecificSounds();
+        return;
+    }
+
+    // Unknown if, have never triggered it
+
+    if (setupMenuRequested) {
+        _stashedTickCount = _engine->getTotalPlayTime();
+        _engine->_gameFlow.previousGameState = NancyEngine::GameState::kScene;
+        _engine->_gameFlow.minGameState = NancyEngine::GameState::kSetup;
+        // _engine->setupMenu->state = setupMenu::State::kInit;
+        setupMenuRequested = false;
+        // _engine->sound->stopSceneSpecificSounds();
+        return;
+    }
+
+    if (creditsSequenceRequested) {
+        _stashedTickCount = _engine->getTotalPlayTime();
+        _engine->_gameFlow.previousGameState = NancyEngine::GameState::kScene;
+        _engine->_gameFlow.minGameState = NancyEngine::GameState::kHelp;
+        // _engine->credits->state = CreditsSequence::State::kInit;
+        creditsSequenceRequested = false;
+        // _engine->sound->stopSceneSpecificSounds();
+        return;
+    }
+
+    if (mapScreenRequested) {
+        _stashedTickCount = _engine->getTotalPlayTime();
+        _engine->_gameFlow.previousGameState = NancyEngine::GameState::kScene;
+        _engine->_gameFlow.minGameState = NancyEngine::GameState::kHelp;
+        // _engine->map->state = Map::State::kInit;
+        mapScreenRequested = false;
+        // _engine->sound->stopSceneSpecificSounds();
+        return;
+    }
+
+    // Cheat menu, will not implement
+
+    // Do some work if we're coming from a different game state
+    if (_engine->_gameFlow.previousGameState != NancyEngine::GameState::kScene) {
+        uint32 t = _engine->getTotalPlayTime();
+        if (hasLoadedFromSavefile) {
+            if (t > _stashedTickCount) {
+                t -= _stashedTickCount;
+                playState.totalTime -= t;
+                playState.sceneTime -= t;
+                if (playState.timerIsActive)
+                    playState.timerTime -= t;
+            }
+        }
+            
+        _engine->graphics->getGenericZRenderStruct(12)->isActive = false; // MENU BTN DN
+        _engine->graphics->getGenericZRenderStruct(13)->isActive = false; // HELP BTN DN
+        hovered = -1;
+        // TODO a bunch of function calls
+        _engine->_gameFlow.previousGameState = NancyEngine::GameState::kScene;
+        return;
+    }
+
+    uint32 t = _engine->getTotalPlayTime();
+    uint32 diff = 0;
+    if (_tickCount < t) {
+        diff = t - _tickCount;
+        _tickCount = t;
+    }
+    playState.totalTime += diff;
+    if (playState.timerIsActive)
+        playState.timerTime += diff;
+    playState.sceneTime =+ diff;
+    
+    // TODO we're skipping a lot of things like mouse handling and scene movement
+    // so we can get _something_ on screen
+
+    // TODO dont call every frame
+    _engine->graphics->_background.copyRectToSurface(
+        *_engine->graphics->getBackgroundFrame(playState.currentViewFrame),
+        0, 0,
+        Common::Rect(0, playState.verticalScroll, _engine->graphics->getBackgroundWidth() - 1,
+            playState.verticalScroll + _engine->graphics->viewportDesc.srcBottom)); // TODO fix magic number
+    _engine->graphics->renderDisplay(25);
+}
+
+void SceneManager::clearSceneData() {
+    // TODO these shouldn't be here
+    _engine->graphics->_primaryFrameSurface.free();
+    _engine->graphics->_object0Surface.free();
+    _engine->graphics->_inventoryBoxIconsSurface.free();
+    _engine->graphics->_inventoryCursorsSurface.free();
+}
+
+} // End of namespace Nancy
\ No newline at end of file
diff --git a/engines/nancy/scene.h b/engines/nancy/scene.h
new file mode 100644
index 0000000000..d07985dc7a
--- /dev/null
+++ b/engines/nancy/scene.h
@@ -0,0 +1,106 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef NANCY_SCENE_H
+#define NANCY_SCENE_H
+
+#include "engines/nancy/time.h"
+#include "engines/nancy/playstate.h"
+#include "engines/nancy/datatypes.h"
+
+#include "common/scummsys.h"
+#include "common/str.h"
+
+namespace Graphics {
+	class Surface;
+}
+
+namespace Common {
+    class SeekableReadStream;
+}
+
+namespace Nancy {
+
+class NancyEngine;
+
+class SceneManager {
+public:
+    SceneManager(NancyEngine *engine) :
+        _engine {engine},
+        _state {kInit},
+        _sceneID {0} { }
+    ~SceneManager();
+
+    void process();
+
+private:
+    void init();
+    void load();
+    void run();
+
+    void clearSceneData();
+
+    enum State {
+        kInit,
+        kLoad,
+        kStartSound,
+        kRun,
+        kLoadNew
+    };
+
+public:
+    PlayState playState;
+
+private:
+    NancyEngine *_engine;
+    State _state;
+    uint16 _sceneID;
+    SceneSummary currentScene;
+    // TODO these two can be Time
+    uint32 _tickCount;
+    uint32 _stashedTickCount;
+    int16 hovered = -1;
+
+    bool isComingFromMenu = true;
+    bool hasLoadedFromSavefile = false;
+
+    bool orderingPuzzleIsActive = false;
+    bool rotatingLockPuzzleIsActive = false;
+    bool leverPuzzleIsActive = false;
+    bool sliderPuzzleIsActive = false;
+    bool passwordPuzzleIsActive = false;
+    bool telephoneIsActive = false;
+
+    // These could be condensed into an enum
+    bool helpMenuRequested = false;
+    bool mainMenuRequested = false;
+    bool saveLoadRequested = false;
+    // not sure
+    bool setupMenuRequested = false;
+    bool creditsSequenceRequested = false;
+    bool mapScreenRequested = false;
+    
+};
+
+} // End of namespace Nancy
+
+#endif // NANCY_SCENE_H
\ No newline at end of file
diff --git a/engines/nancy/time.h b/engines/nancy/time.h
new file mode 100644
index 0000000000..42dcf14989
--- /dev/null
+++ b/engines/nancy/time.h
@@ -0,0 +1,60 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef NANCY_TIME_H
+#define NANCY_TIME_H
+
+#include "common/scummsys.h"
+
+namespace Nancy {
+
+// A small utility struct to measure/accumulate time
+struct Time {
+public:
+    Time() { _milliseconds = 0; }
+    Time(uint32 &t) { _milliseconds = t; }
+    Time(const Time &t) =default;
+    ~Time() =default;
+    Time &operator=(const Time &t)                  { if (this != &t) _milliseconds = t._milliseconds; return *this; }
+    Time &operator=(const uint32 &t)                { _milliseconds = t; return *this; }
+    Time &operator+=(const Time &t)                 { _milliseconds += t._milliseconds; return *this; }
+    friend Time operator+(Time l, const Time &r)    { l += r; return l; }
+    Time &operator+=(const uint32 &t)               { _milliseconds+=t; return *this; }
+    friend Time operator+(Time l, const uint32 &r)  { l += r; return l; }
+    friend Time operator+(const uint32 &l, Time r)  { r += l; return r; }
+    Time &operator-=(const Time &t)                 { _milliseconds -= t._milliseconds; return *this; }
+    friend Time operator-(Time l, const Time &r)    { l -= r; return l; }
+    Time &operator-=(const uint32 &t)               { _milliseconds-=t; return *this; }
+    friend Time operator-(Time l, const uint32 &r)  { l -= r; return l; }
+    friend Time operator-(const uint32 &l, Time r)  { r -= l; return r; }
+
+    uint16 getSeconds() { return (_milliseconds / 1000) % 60; }
+    uint16 getMinutes() { return (_milliseconds / 60000) % 60; }
+    uint16 getHours()   { return _milliseconds / 3600000; }
+
+private:
+    uint32 _milliseconds;
+};
+
+} // End of namespace Nancy
+
+#endif // NANCY_TIME_H
\ No newline at end of file
diff --git a/engines/nancy/video.cpp b/engines/nancy/video.cpp
index 84295a7adf..b31ab6035c 100644
--- a/engines/nancy/video.cpp
+++ b/engines/nancy/video.cpp
@@ -20,6 +20,9 @@
  *
  */
 
+#include "engines/nancy/video.h"
+#include "engines/nancy/decompress.h"
+
 #include "common/endian.h"
 #include "common/stream.h"
 #include "common/system.h"
@@ -30,8 +33,6 @@
 
 #include "graphics/surface.h"
 
-#include "nancy/video.h"
-#include "nancy/decompress.h"
 
 namespace Nancy {
 
@@ -67,6 +68,10 @@ bool AVFDecoder::loadStream(Common::SeekableReadStream *stream) {
 	return true;
 }
 
+const Graphics::Surface *AVFDecoder::decodeFrame(uint frameNr) {
+	return ((AVFDecoder::AVFVideoTrack *)getTrack(0))->decodeFrame(frameNr);
+}
+
 AVFDecoder::AVFVideoTrack::AVFVideoTrack(Common::SeekableReadStream *stream) {
 	assert(stream);
 	_fileStream = stream;
@@ -181,7 +186,7 @@ const Graphics::Surface *AVFDecoder::AVFVideoTrack::decodeFrame(uint frameNr) {
 		return _surface;
 	}
 
-	byte *decompBuf = 0;
+	byte *decompBuf = nullptr;
 	if (info.type == 0) {
 		// For type 0 we decompress straight to the surface, make sure we don't go out of bounds
 		if (info.size > _frameSize) {
@@ -216,4 +221,4 @@ const Graphics::Surface *AVFDecoder::AVFVideoTrack::decodeNextFrame() {
 	return decodeFrame(++_curFrame);
 }
 
-} // End of namespace Nancy
+} // End of namespace Nancy
\ No newline at end of file
diff --git a/engines/nancy/video.h b/engines/nancy/video.h
index e44d566cd0..937656f4dc 100644
--- a/engines/nancy/video.h
+++ b/engines/nancy/video.h
@@ -28,6 +28,7 @@
 #include "common/rational.h"
 
 namespace Common {
+class ReadStream;
 class SeekableReadStream;
 }
 
@@ -44,6 +45,7 @@ public:
 	virtual ~AVFDecoder();
 
 	bool loadStream(Common::SeekableReadStream *stream);
+	const Graphics::Surface *decodeFrame(uint frameNr);
 
 private:
 	class AVFVideoTrack : public FixedRateVideoTrack {
@@ -56,6 +58,7 @@ private:
 		Graphics::PixelFormat getPixelFormat() const { return _pixelFormat; }
 		int getCurFrame() const { return _curFrame; }
 		int getFrameCount() const { return _frameCount; }
+		const Graphics::Surface *decodeFrame(uint frameNr);
 		const Graphics::Surface *decodeNextFrame();
 
 	protected:
@@ -70,7 +73,6 @@ private:
 			byte type;
 		};
 
-		const Graphics::Surface *decodeFrame(uint frameNr);
 		bool decode(byte *outBuf, uint32 frameSize, Common::ReadStream &inBuf) const;
 
 		Common::SeekableReadStream *_fileStream;
@@ -88,4 +90,4 @@ private:
 
 } // End of namespace Nancy
 
-#endif
+#endif // NANCY_VIDEO_H
\ No newline at end of file


Commit: 4a76623e67d42501e3d7077e22f39eac686cfedc
    https://github.com/scummvm/scummvm/commit/4a76623e67d42501e3d7077e22f39eac686cfedc
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Fix compiler errors

Fix incorrect typedef for render function pointers; correct forward declarations of Graphics::Surface from class to struct.

Changed paths:
    engines/nancy/graphics.h
    engines/nancy/logo.h
    engines/nancy/menu.h
    engines/nancy/scene.cpp
    engines/nancy/scene.h


diff --git a/engines/nancy/graphics.h b/engines/nancy/graphics.h
index 6629b7d545..85d3280789 100644
--- a/engines/nancy/graphics.h
+++ b/engines/nancy/graphics.h
@@ -33,7 +33,7 @@
 
 namespace Nancy {
 
-typedef Common::Functor0<void> RenderFunction;
+typedef Common::Functor0Mem<void, GraphicsManager> RenderFunction;
 
 struct ZRenderStruct {
 public:
diff --git a/engines/nancy/logo.h b/engines/nancy/logo.h
index 899078eba6..a6c849e103 100644
--- a/engines/nancy/logo.h
+++ b/engines/nancy/logo.h
@@ -26,7 +26,7 @@
 #include "common/scummsys.h"
 
 namespace Graphics {
-	class Surface;
+	struct Surface;
 }
 
 namespace Nancy {
diff --git a/engines/nancy/menu.h b/engines/nancy/menu.h
index 16bc8575da..f31681afb6 100644
--- a/engines/nancy/menu.h
+++ b/engines/nancy/menu.h
@@ -24,7 +24,7 @@
 #define NANCY_MENU_H
 
 namespace Graphics {
-class Surface;
+struct Surface;
 }
 
 namespace Nancy {
diff --git a/engines/nancy/scene.cpp b/engines/nancy/scene.cpp
index 24fb71958b..8c0fae3e38 100644
--- a/engines/nancy/scene.cpp
+++ b/engines/nancy/scene.cpp
@@ -124,7 +124,7 @@ void SceneManager::init() {
     READ_SOURCE_RECT(16)
     READ_DEST_POINT(16)
     _engine->graphics->initGenericZRenderStruct(0, "FRAME", 1, true, ZRenderStruct::BltType::kNone, &_engine->graphics->_primaryFrameSurface,
-                                                new Common::Functor0Mem<void, GraphicsManager>(_engine->graphics, GraphicsManager::renderFrame),
+                                                new RenderFunction(_engine->graphics, GraphicsManager::renderFrame),
                                                 source, dest);
     _engine->graphics->initGenericZRenderStruct(2, "CUR IMAGE CURSOR", 11, false, ZRenderStruct::BltType::kTrans,
                                                 &_engine->graphics->_object0Surface, nullptr, new Common::Rect(), new Common::Point());
@@ -140,7 +140,7 @@ void SceneManager::init() {
                                                 &_engine->graphics->_object0Surface, nullptr, source, new Common::Point());           
 
     _engine->graphics->initGenericZRenderStruct(10, "RES TB BAT SLIDER", 3, false, ZRenderStruct::BltType::kNoTrans, &_engine->graphics->_primaryFrameSurface,
-                                               new Common::Functor0Mem<void, GraphicsManager>(_engine->graphics, GraphicsManager::renderResTBBatSlider),
+                                               new RenderFunction(_engine->graphics, GraphicsManager::renderResTBBatSlider),
                                                new Common::Rect(), new Common::Point());
 
     chunk = _engine->getBootChunkStream("BSUM");
@@ -171,50 +171,50 @@ void SceneManager::init() {
                                                 &_engine->graphics->_primaryFrameSurface, nullptr, new Common::Rect(), new Common::Point());
 
     _engine->graphics->initGenericZRenderStruct(16, "FRAME INV BOX", 6, false, ZRenderStruct::BltType::kNoTrans, nullptr,
-                                                new Common::Functor0Mem<void, GraphicsManager>(_engine->graphics, GraphicsManager::renderFrameInvBox),
+                                                new RenderFunction(_engine->graphics, GraphicsManager::renderFrameInvBox),
                                                 nullptr, nullptr);
 
     _engine->graphics->initGenericZRenderStruct(5, "INV BITMAP", 9, false, ZRenderStruct::BltType::kNoTrans,
                                                 nullptr, nullptr, new Common::Rect(), new Common::Point());
 
     _engine->graphics->initGenericZRenderStruct(17, "PRIMARY VIDEO", 8, false, ZRenderStruct::BltType::kNoTrans, nullptr,
-                                                new Common::Functor0Mem<void, GraphicsManager>(_engine->graphics, GraphicsManager::renderPrimaryVideo),
+                                                new RenderFunction(_engine->graphics, GraphicsManager::renderPrimaryVideo),
                                                 new Common::Rect(), new Common::Point());
 
     _engine->graphics->initGenericZRenderStruct(18, "SEC VIDEO 0", 8, false, ZRenderStruct::BltType::kTrans, nullptr,
-                                                new Common::Functor0Mem<void, GraphicsManager>(_engine->graphics, GraphicsManager::renderSecVideo0),
+                                                new RenderFunction(_engine->graphics, GraphicsManager::renderSecVideo0),
                                                 new Common::Rect(), new Common::Point());
 
     _engine->graphics->initGenericZRenderStruct(19, "SEC VIDEO 1", 8, false, ZRenderStruct::BltType::kTrans, nullptr,
-                                                new Common::Functor0Mem<void, GraphicsManager>(_engine->graphics, GraphicsManager::renderSecVideo1),
+                                                new RenderFunction(_engine->graphics, GraphicsManager::renderSecVideo1),
                                                 new Common::Rect(), new Common::Point());
     
     _engine->graphics->initGenericZRenderStruct(20, "SEC MOVIE", 8, false, ZRenderStruct::BltType::kNoTrans, nullptr,
-                                                new Common::Functor0Mem<void, GraphicsManager>(_engine->graphics, GraphicsManager::renderSecMovie),
+                                                new RenderFunction(_engine->graphics, GraphicsManager::renderSecMovie),
                                                 new Common::Rect(), new Common::Point());
     
     _engine->graphics->initGenericZRenderStruct(21, "ORDERING PUZZLE", 7, false, ZRenderStruct::BltType::kNoTrans, nullptr,
-                                                new Common::Functor0Mem<void, GraphicsManager>(_engine->graphics, GraphicsManager::renderOrderingPuzzle),
+                                                new RenderFunction(_engine->graphics, GraphicsManager::renderOrderingPuzzle),
                                                 nullptr);
     
     _engine->graphics->initGenericZRenderStruct(22, "ROTATING LOCK PUZZLE", 7, false, ZRenderStruct::BltType::kNoTrans, nullptr,
-                                                new Common::Functor0Mem<void, GraphicsManager>(_engine->graphics, GraphicsManager::renderRotatingLockPuzzle),
+                                                new RenderFunction(_engine->graphics, GraphicsManager::renderRotatingLockPuzzle),
                                                 nullptr, nullptr);
 
     _engine->graphics->initGenericZRenderStruct(23, "LEVER PUZZLE", 7, false, ZRenderStruct::BltType::kNoTrans, nullptr,
-                                                new Common::Functor0Mem<void, GraphicsManager>(_engine->graphics, GraphicsManager::renderLeverPuzzle),
+                                                new RenderFunction(_engine->graphics, GraphicsManager::renderLeverPuzzle),
                                                 nullptr, nullptr);
 
     _engine->graphics->initGenericZRenderStruct(24, "TELEPHONE", 7, false, ZRenderStruct::BltType::kNoTrans, nullptr,
-                                                new Common::Functor0Mem<void, GraphicsManager>(_engine->graphics, GraphicsManager::renderTelephone),
+                                                new RenderFunction(_engine->graphics, GraphicsManager::renderTelephone),
                                                 nullptr, nullptr);
     
     _engine->graphics->initGenericZRenderStruct(25, "SLIDER PUZZLE", 7, false, ZRenderStruct::BltType::kNoTrans, nullptr,
-                                                new Common::Functor0Mem<void, GraphicsManager>(_engine->graphics, GraphicsManager::renderSliderPuzzle),
+                                                new RenderFunction(_engine->graphics, GraphicsManager::renderSliderPuzzle),
                                                 nullptr, nullptr);
     
     _engine->graphics->initGenericZRenderStruct(26, "PASSWORD PUZZLE", 7, false, ZRenderStruct::BltType::kNoTrans, nullptr,
-                                                new Common::Functor0Mem<void, GraphicsManager>(_engine->graphics, GraphicsManager::renderPasswordPuzzle),
+                                                new RenderFunction(_engine->graphics, GraphicsManager::renderPasswordPuzzle),
                                                 nullptr, nullptr);
     #undef READ_SOURCE_RECT
     #undef READ_DEST_POINT
diff --git a/engines/nancy/scene.h b/engines/nancy/scene.h
index d07985dc7a..c7cc4dab4d 100644
--- a/engines/nancy/scene.h
+++ b/engines/nancy/scene.h
@@ -31,7 +31,7 @@
 #include "common/str.h"
 
 namespace Graphics {
-	class Surface;
+	struct Surface;
 }
 
 namespace Common {


Commit: 085293852098ac40312bf9c4e8a344c365d77228
    https://github.com/scummvm/scummvm/commit/085293852098ac40312bf9c4e8a344c365d77228
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Fix compiler errors

Fix the pointer to member function syntax when adding render functions.

Changed paths:
    engines/nancy/scene.cpp


diff --git a/engines/nancy/scene.cpp b/engines/nancy/scene.cpp
index 8c0fae3e38..040a0e5360 100644
--- a/engines/nancy/scene.cpp
+++ b/engines/nancy/scene.cpp
@@ -124,7 +124,7 @@ void SceneManager::init() {
     READ_SOURCE_RECT(16)
     READ_DEST_POINT(16)
     _engine->graphics->initGenericZRenderStruct(0, "FRAME", 1, true, ZRenderStruct::BltType::kNone, &_engine->graphics->_primaryFrameSurface,
-                                                new RenderFunction(_engine->graphics, GraphicsManager::renderFrame),
+                                                new RenderFunction(_engine->graphics, &GraphicsManager::renderFrame),
                                                 source, dest);
     _engine->graphics->initGenericZRenderStruct(2, "CUR IMAGE CURSOR", 11, false, ZRenderStruct::BltType::kTrans,
                                                 &_engine->graphics->_object0Surface, nullptr, new Common::Rect(), new Common::Point());
@@ -140,7 +140,7 @@ void SceneManager::init() {
                                                 &_engine->graphics->_object0Surface, nullptr, source, new Common::Point());           
 
     _engine->graphics->initGenericZRenderStruct(10, "RES TB BAT SLIDER", 3, false, ZRenderStruct::BltType::kNoTrans, &_engine->graphics->_primaryFrameSurface,
-                                               new RenderFunction(_engine->graphics, GraphicsManager::renderResTBBatSlider),
+                                               new RenderFunction(_engine->graphics, &GraphicsManager::renderResTBBatSlider),
                                                new Common::Rect(), new Common::Point());
 
     chunk = _engine->getBootChunkStream("BSUM");
@@ -171,50 +171,50 @@ void SceneManager::init() {
                                                 &_engine->graphics->_primaryFrameSurface, nullptr, new Common::Rect(), new Common::Point());
 
     _engine->graphics->initGenericZRenderStruct(16, "FRAME INV BOX", 6, false, ZRenderStruct::BltType::kNoTrans, nullptr,
-                                                new RenderFunction(_engine->graphics, GraphicsManager::renderFrameInvBox),
+                                                new RenderFunction(_engine->graphics, &GraphicsManager::renderFrameInvBox),
                                                 nullptr, nullptr);
 
     _engine->graphics->initGenericZRenderStruct(5, "INV BITMAP", 9, false, ZRenderStruct::BltType::kNoTrans,
                                                 nullptr, nullptr, new Common::Rect(), new Common::Point());
 
     _engine->graphics->initGenericZRenderStruct(17, "PRIMARY VIDEO", 8, false, ZRenderStruct::BltType::kNoTrans, nullptr,
-                                                new RenderFunction(_engine->graphics, GraphicsManager::renderPrimaryVideo),
+                                                new RenderFunction(_engine->graphics, &GraphicsManager::renderPrimaryVideo),
                                                 new Common::Rect(), new Common::Point());
 
     _engine->graphics->initGenericZRenderStruct(18, "SEC VIDEO 0", 8, false, ZRenderStruct::BltType::kTrans, nullptr,
-                                                new RenderFunction(_engine->graphics, GraphicsManager::renderSecVideo0),
+                                                new RenderFunction(_engine->graphics, &GraphicsManager::renderSecVideo0),
                                                 new Common::Rect(), new Common::Point());
 
     _engine->graphics->initGenericZRenderStruct(19, "SEC VIDEO 1", 8, false, ZRenderStruct::BltType::kTrans, nullptr,
-                                                new RenderFunction(_engine->graphics, GraphicsManager::renderSecVideo1),
+                                                new RenderFunction(_engine->graphics, &GraphicsManager::renderSecVideo1),
                                                 new Common::Rect(), new Common::Point());
     
     _engine->graphics->initGenericZRenderStruct(20, "SEC MOVIE", 8, false, ZRenderStruct::BltType::kNoTrans, nullptr,
-                                                new RenderFunction(_engine->graphics, GraphicsManager::renderSecMovie),
+                                                new RenderFunction(_engine->graphics, &GraphicsManager::renderSecMovie),
                                                 new Common::Rect(), new Common::Point());
     
     _engine->graphics->initGenericZRenderStruct(21, "ORDERING PUZZLE", 7, false, ZRenderStruct::BltType::kNoTrans, nullptr,
-                                                new RenderFunction(_engine->graphics, GraphicsManager::renderOrderingPuzzle),
+                                                new RenderFunction(_engine->graphics, &GraphicsManager::renderOrderingPuzzle),
                                                 nullptr);
     
     _engine->graphics->initGenericZRenderStruct(22, "ROTATING LOCK PUZZLE", 7, false, ZRenderStruct::BltType::kNoTrans, nullptr,
-                                                new RenderFunction(_engine->graphics, GraphicsManager::renderRotatingLockPuzzle),
+                                                new RenderFunction(_engine->graphics, &GraphicsManager::renderRotatingLockPuzzle),
                                                 nullptr, nullptr);
 
     _engine->graphics->initGenericZRenderStruct(23, "LEVER PUZZLE", 7, false, ZRenderStruct::BltType::kNoTrans, nullptr,
-                                                new RenderFunction(_engine->graphics, GraphicsManager::renderLeverPuzzle),
+                                                new RenderFunction(_engine->graphics, &GraphicsManager::renderLeverPuzzle),
                                                 nullptr, nullptr);
 
     _engine->graphics->initGenericZRenderStruct(24, "TELEPHONE", 7, false, ZRenderStruct::BltType::kNoTrans, nullptr,
-                                                new RenderFunction(_engine->graphics, GraphicsManager::renderTelephone),
+                                                new RenderFunction(_engine->graphics, &GraphicsManager::renderTelephone),
                                                 nullptr, nullptr);
     
     _engine->graphics->initGenericZRenderStruct(25, "SLIDER PUZZLE", 7, false, ZRenderStruct::BltType::kNoTrans, nullptr,
-                                                new RenderFunction(_engine->graphics, GraphicsManager::renderSliderPuzzle),
+                                                new RenderFunction(_engine->graphics, &GraphicsManager::renderSliderPuzzle),
                                                 nullptr, nullptr);
     
     _engine->graphics->initGenericZRenderStruct(26, "PASSWORD PUZZLE", 7, false, ZRenderStruct::BltType::kNoTrans, nullptr,
-                                                new RenderFunction(_engine->graphics, GraphicsManager::renderPasswordPuzzle),
+                                                new RenderFunction(_engine->graphics, &GraphicsManager::renderPasswordPuzzle),
                                                 nullptr, nullptr);
     #undef READ_SOURCE_RECT
     #undef READ_DEST_POINT


Commit: 7e1e276c3c329273bfd12de281706f1c8bc3e70f
    https://github.com/scummvm/scummvm/commit/7e1e276c3c329273bfd12de281706f1c8bc3e70f
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add in-game time

Added a playerTime variable, which contains the in-game time, and expanded the Time struct to support it; also added the timeOfDay variable which can affect scene changing.

Changed paths:
    engines/nancy/nancy.cpp
    engines/nancy/playstate.h
    engines/nancy/scene.cpp
    engines/nancy/scene.h
    engines/nancy/time.h


diff --git a/engines/nancy/nancy.cpp b/engines/nancy/nancy.cpp
index b042337478..1d9b2aee99 100644
--- a/engines/nancy/nancy.cpp
+++ b/engines/nancy/nancy.cpp
@@ -377,6 +377,8 @@ void NancyEngine_v0::readBootSummary(const IFF &boot) {
 	readImageList(boot, "OB", _objects);
 	bsum->seek(0x1D1);
 	_fontSize = bsum->readSint32LE() * 1346;
+	bsum->seek(0x1ED);
+	sceneManager->playerTimeMinuteLength = bsum->readSint16LE();
 }
 
 class NancyEngine_v1 : public NancyEngine_v0 {
diff --git a/engines/nancy/playstate.h b/engines/nancy/playstate.h
index 67ebce2741..4d4d97ef5b 100644
--- a/engines/nancy/playstate.h
+++ b/engines/nancy/playstate.h
@@ -31,6 +31,7 @@ namespace Nancy {
 // TODO split to PlayerState/SceneState
 struct PlayState {
     enum Flag { kFalse = 1, kTrue = 2 };
+    enum TimeOfDay { kDay, kNight, kDuskDawn };
 
     Flag inventory[11];
     Flag eventFlags[672];
@@ -41,6 +42,9 @@ struct PlayState {
     Time sceneTime;
     Time timerTime;
     bool timerIsActive = false;
+    Time playerTime; // Nancy's in-game time of day, adds a minute every 5 seconds
+    Time playerTimeNextMinute; // Stores the next tick count until we add a minute to playerTime
+    TimeOfDay timeOfDay = kDay;
     uint16 currentViewFrame = 0;
     uint16 queuedViewFrame = 0;
     uint16 currentMaxVerticalScroll = 0;
diff --git a/engines/nancy/scene.cpp b/engines/nancy/scene.cpp
index 040a0e5360..c281ca9e4d 100644
--- a/engines/nancy/scene.cpp
+++ b/engines/nancy/scene.cpp
@@ -415,6 +415,21 @@ void SceneManager::run() {
     if (playState.timerIsActive)
         playState.timerTime += diff;
     playState.sceneTime =+ diff;
+
+    // Calculate the in-game time (playerTime)
+    if (t > playState.playerTimeNextMinute) {
+        playState.playerTime += 60000; // Add a minute
+        playState.playerTimeNextMinute = t + playerTimeMinuteLength; // Set when we're going to add the next minute
+    }
+
+    // Set the time of day according to playerTime
+    if (playState.playerTime.getHours_alt() >= 7 && playState.playerTime.getHours_alt() < 18) {
+        playState.timeOfDay = playState.kDay;
+    } else if ((playState.playerTime.getHours_alt() >= 19 || playState.playerTime.getHours_alt() < 6)) {
+        playState.timeOfDay = playState.kNight;
+    } else {
+        playState.timeOfDay = playState.kDuskDawn;
+    }
     
     // TODO we're skipping a lot of things like mouse handling and scene movement
     // so we can get _something_ on screen
diff --git a/engines/nancy/scene.h b/engines/nancy/scene.h
index c7cc4dab4d..ec454327b9 100644
--- a/engines/nancy/scene.h
+++ b/engines/nancy/scene.h
@@ -69,6 +69,7 @@ private:
 
 public:
     PlayState playState;
+    int32 playerTimeMinuteLength;
 
 private:
     NancyEngine *_engine;
diff --git a/engines/nancy/time.h b/engines/nancy/time.h
index 42dcf14989..e9bee710de 100644
--- a/engines/nancy/time.h
+++ b/engines/nancy/time.h
@@ -34,22 +34,45 @@ public:
     Time(uint32 &t) { _milliseconds = t; }
     Time(const Time &t) =default;
     ~Time() =default;
-    Time &operator=(const Time &t)                  { if (this != &t) _milliseconds = t._milliseconds; return *this; }
-    Time &operator=(const uint32 &t)                { _milliseconds = t; return *this; }
-    Time &operator+=(const Time &t)                 { _milliseconds += t._milliseconds; return *this; }
-    friend Time operator+(Time l, const Time &r)    { l += r; return l; }
-    Time &operator+=(const uint32 &t)               { _milliseconds+=t; return *this; }
-    friend Time operator+(Time l, const uint32 &r)  { l += r; return l; }
-    friend Time operator+(const uint32 &l, Time r)  { r += l; return r; }
-    Time &operator-=(const Time &t)                 { _milliseconds -= t._milliseconds; return *this; }
-    friend Time operator-(Time l, const Time &r)    { l -= r; return l; }
-    Time &operator-=(const uint32 &t)               { _milliseconds-=t; return *this; }
-    friend Time operator-(Time l, const uint32 &r)  { l -= r; return l; }
-    friend Time operator-(const uint32 &l, Time r)  { r -= l; return r; }
-
-    uint16 getSeconds() { return (_milliseconds / 1000) % 60; }
-    uint16 getMinutes() { return (_milliseconds / 60000) % 60; }
-    uint16 getHours()   { return _milliseconds / 3600000; }
+    Time &operator=(const Time &t)                          { if (this != &t) _milliseconds = t._milliseconds; return *this; }
+    Time &operator=(const uint32 &t)                        { _milliseconds = t; return *this; }
+    Time &operator+=(const Time &t)                         { _milliseconds += t._milliseconds; return *this; }
+    Time &operator+=(const uint32 &t)                       { _milliseconds+=t; return *this; }
+    Time &operator-=(const Time &t)                         { _milliseconds -= t._milliseconds; return *this; }
+    Time &operator-=(const uint32 &t)                       { _milliseconds-=t; return *this; }
+    
+    friend Time operator+(Time l, const Time &r)            { l += r; return l; }
+    friend Time operator+(Time l, const uint32 &r)          { l += r; return l; }
+    friend Time operator+(const uint32 &l, Time r)          { r += l; return r; }
+    friend Time operator-(Time l, const Time &r)            { l -= r; return l; }
+    friend Time operator-(Time l, const uint32 &r)          { l -= r; return l; }
+    friend Time operator-(const uint32 &l, Time r)          { r -= l; return r; }
+
+    friend bool operator== (const Time &l, const Time &r)   { return l._milliseconds == r._milliseconds; }
+    friend bool operator== (const Time &l, const uint32 &r) { return l._milliseconds == r; }
+    friend bool operator== (const uint32 &l, const Time &r) { return l == r._milliseconds; }
+    friend bool operator!= (const Time &l, const Time &r)   { return l._milliseconds != r._milliseconds; }
+    friend bool operator!= (const Time &l, const uint32 &r) { return l._milliseconds != r; }
+    friend bool operator!= (const uint32 &l, const Time &r) { return l != r._milliseconds; }
+    friend bool operator< (const Time &l, const Time &r)    { return l._milliseconds < r._milliseconds; }
+    friend bool operator< (const Time &l, const uint32 &r)  { return l._milliseconds < r; }
+    friend bool operator< (const uint32 &l, const Time &r)  { return l < r._milliseconds; }
+    friend bool operator> (const Time &l, const Time &r)    { return r < l; }
+    friend bool operator> (const Time &l, const uint32 &r)  { return r < l; }
+    friend bool operator> (const uint32 &l, const Time &r)  { return r < l; }
+    friend bool operator<= (const Time &l, const Time &r)   { return !(l > r); }
+    friend bool operator<= (const Time &l, const uint32 &r) { return !(l > r); }
+    friend bool operator<= (const uint32 &l, const Time &r) { return !(l > r); }
+    friend bool operator>= (const Time &l, const Time &r)   { return !(l < r); }
+    friend bool operator>= (const Time &l, const uint32 &r) { return !(l < r); }
+    friend bool operator>= (const uint32 &l, const Time &r) { return !(l < r); }
+
+    uint16 getSeconds()     { return (_milliseconds / 1000) % 60; }
+    uint16 getMinutes()     { return (_milliseconds / 60000) % 60; }
+    uint16 getHours()       { return _milliseconds / 3600000; }
+    // Used for player time
+    uint16 getHours_alt()   { return (_milliseconds / 3600000) % 24; }
+    uint16 getDays()        { return _milliseconds / 86400000; } // up to 49.7 days
 
 private:
     uint32 _milliseconds;


Commit: 11f89b5b979909d2827a174e34cfa2707075abe2
    https://github.com/scummvm/scummvm/commit/11f89b5b979909d2827a174e34cfa2707075abe2
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add initial input support

Added an Input class with support for every nancy1 key combo (that I know of). Added code for skipping the Logo state with a left click. Proper mouse handling is still TODO.

Changed paths:
  A engines/nancy/input.cpp
  A engines/nancy/input.h
    engines/nancy/logo.cpp
    engines/nancy/metaengine.cpp
    engines/nancy/module.mk
    engines/nancy/nancy.cpp
    engines/nancy/nancy.h


diff --git a/engines/nancy/input.cpp b/engines/nancy/input.cpp
new file mode 100644
index 0000000000..a0bd5171bd
--- /dev/null
+++ b/engines/nancy/input.cpp
@@ -0,0 +1,229 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/nancy/input.h"
+#include "engines/nancy/nancy.h"
+
+#include "common/events.h"
+#include "common/keyboard.h"
+
+#include "backends/keymapper/action.h"
+#include "backends/keymapper/keymap.h"
+#include "backends/keymapper/standard-actions.h"
+
+namespace Nancy {
+    
+void InputManager::processEvents() {
+    using namespace Common;
+
+    Common::Event event;
+
+    // TODO consider adding a keymapper
+    // TODO add debug key combos
+    while (_engine->getEventManager()->pollEvent(event)) {
+        switch (event.type) {
+            case EVENT_CUSTOM_ENGINE_ACTION_START:
+                // TODO add debug shortcuts
+                switch (event.customType) {
+                    case kNancyActionMoveUp:
+                        _inputs |= (byte)kMoveUp;
+                        break;
+                    case kNancyActionMoveDown:
+                        _inputs |= (byte)kMoveDown;
+                        break;
+                    case kNancyActionMoveLeft:
+                        _inputs |= (byte)kMoveLeft;
+                        break;
+                    case kNancyActionMoveRight:
+                        _inputs |= (byte)kMoveRight;
+                        break;
+                    case kNancyActionMoveFast:
+                        _inputs |= (byte)kMoveFastModifier;
+                        break;
+                    case kNancyActionLeftClick:
+                        _inputs |= (byte)kLeftMouseButton;
+                        break;
+                    case kNancyActionRightClick:
+                        _inputs |= (byte)kRightMouseButton;
+                        break;
+                    default:
+                        break;
+                }
+                break;
+            case EVENT_CUSTOM_ENGINE_ACTION_END:
+                switch (event.customType) {
+                    case kNancyActionMoveUp:
+                        _inputs &= ~(byte)kMoveUp;
+                        break;
+                    case kNancyActionMoveDown:
+                        _inputs &= ~(byte)kMoveDown;
+                        break;
+                    case kNancyActionMoveLeft:
+                        _inputs &= ~(byte)kMoveLeft;
+                        break;
+                    case kNancyActionMoveRight:
+                        _inputs &= ~(byte)kMoveRight;
+                        break;
+                    case kNancyActionMoveFast:
+                        _inputs &= ~(byte)kMoveFastModifier;
+                        break;
+                    case kNancyActionLeftClick:
+                        _inputs &= ~(byte)kLeftMouseButton;
+                        break;
+                    case kNancyActionRightClick:
+                        _inputs &= ~(byte)kRightMouseButton;
+                        break;
+                    default:
+                        break;
+                }
+                break;
+            case EVENT_KEYDOWN: 
+                switch (event.kbd.keycode) {
+                    // Launch debug console
+                    case KEYCODE_d:
+                        if (event.kbd.flags & Common::KBD_CTRL) 
+                            _engine->launchConsole = true;
+                        break;
+                    // Quit
+                    case KEYCODE_q:
+                        if (event.kbd.flags & Common::KBD_CTRL) {
+                            _engine->quitGame();
+                            break;
+                        }
+                        break;
+                    default:
+                        break;
+                }
+                break;
+            case EVENT_MOUSEMOVE:
+                // TODO add frameMousePos
+                break;
+            default:
+                break;
+        }
+    }
+}
+
+bool InputManager::getInput(InputManager::InputType type) {
+    return _inputs & (byte)type;
+}
+
+void InputManager::initKeymaps(Common::KeymapArray &keymaps) {
+    using namespace Common;
+	using namespace Nancy;
+	
+	Keymap *mainKeymap = new Keymap(Keymap::kKeymapTypeGame, "nancy-main", "Nancy Drew");
+	Keymap *debugKeymap = new Keymap(Keymap::kKeymapTypeGame, "nancy-debug", "Nancy Drew - Debug/Cheat Shortcuts");
+	Action *act;
+
+	act = new Action(kStandardActionInteract, U32String("Left Click Interact"));
+    act->setLeftClickEvent();
+	act->setCustomEngineActionEvent(kNancyActionLeftClick);
+    act->addDefaultInputMapping("MOUSE_LEFT");
+    act->addDefaultInputMapping("JOY_A");
+	mainKeymap->addAction(act);
+
+    act = new Action("RCLK", U32String("Right Click Interact"));
+    act->setRightClickEvent();
+	act->setCustomEngineActionEvent(kNancyActionRightClick);
+    act->addDefaultInputMapping("MOUSE_RIGHT");
+    act->addDefaultInputMapping("JOY_B");
+	mainKeymap->addAction(act);
+
+    act = new Action(kStandardActionMoveUp, U32String("Move up"));
+	act->setCustomEngineActionEvent(kNancyActionMoveUp);
+    act->addDefaultInputMapping("UP");
+    act->addDefaultInputMapping("JOY_UP");
+	mainKeymap->addAction(act);
+
+    act = new Action(kStandardActionMoveDown, U32String("Move down"));
+	act->setCustomEngineActionEvent(kNancyActionMoveDown);
+    act->addDefaultInputMapping("DOWN");
+    act->addDefaultInputMapping("JOY_DOWN");
+	mainKeymap->addAction(act);
+
+    act = new Action(kStandardActionMoveLeft, U32String("Move left"));
+	act->setCustomEngineActionEvent(kNancyActionMoveLeft);
+    act->addDefaultInputMapping("LEFT");
+    act->addDefaultInputMapping("JOY_LEFT");
+	mainKeymap->addAction(act);
+
+    act = new Action(kStandardActionMoveRight, U32String("Move right"));
+	act->setCustomEngineActionEvent(kNancyActionMoveRight);
+    act->addDefaultInputMapping("RIGHT");
+    act->addDefaultInputMapping("JOY_RIGHT");
+	mainKeymap->addAction(act);
+
+    act = new Action("FASTM", U32String("Fast move modifier"));
+	act->setCustomEngineActionEvent(kNancyActionMoveFast);
+    act->addDefaultInputMapping("LCTRL");
+    act->addDefaultInputMapping("JOY_LEFT_SHOULDER");
+	mainKeymap->addAction(act);
+
+    // Debug shortcuts
+
+    act = new Action("FASTC", U32String("Toggle fast conversation mode"));
+	act->setCustomEngineActionEvent(kNancyActionFastConvoToggle);
+    act->addDefaultInputMapping("C+S+TAB+f");
+	debugKeymap->addAction(act);
+
+    act = new Action("ENDC", U32String("Toggle end conversation mode"));
+	act->setCustomEngineActionEvent(kNancyActionEndConvoToggle);
+    act->addDefaultInputMapping("C+S+TAB+e");
+	debugKeymap->addAction(act);
+
+    act = new Action("MMENU", U32String("Go to main menu"));
+	act->setCustomEngineActionEvent(kNancyActionRequestMainMenu);
+    act->addDefaultInputMapping("C+S+TAB+F2");
+	debugKeymap->addAction(act);
+
+    act = new Action("LDSV", U32String("Go to save/load menu"));
+	act->setCustomEngineActionEvent(kNancyActionRequestSaveLoad);
+    act->addDefaultInputMapping("C+S+TAB+F3");
+	debugKeymap->addAction(act);
+
+    act = new Action("RLDSV", U32String("Reload last save"));
+	act->setCustomEngineActionEvent(kNancyActionReloadSave);
+    act->addDefaultInputMapping("C+S+TAB+F4");
+	debugKeymap->addAction(act);
+
+    act = new Action("SETUP", U32String("Go to setup menu"));
+	act->setCustomEngineActionEvent(kNancyActionRequestSetupMenu);
+    act->addDefaultInputMapping("C+S+TAB+F6");
+	debugKeymap->addAction(act);
+
+    act = new Action("CRED", U32String("Show credits"));
+	act->setCustomEngineActionEvent(kNancyActionRequestCredits);
+    act->addDefaultInputMapping("C+S+TAB+F7");
+	debugKeymap->addAction(act);
+
+    act = new Action("MAP", U32String("Go to map screen"));
+	act->setCustomEngineActionEvent(kNancyActionRequestMap);
+    act->addDefaultInputMapping("C+S+TAB+F8");
+    act->addDefaultInputMapping("C+S+TAB+m");
+	debugKeymap->addAction(act);
+
+	keymaps.push_back(mainKeymap);
+    keymaps.push_back(debugKeymap);
+}
+
+} // End of namespace Nancy
\ No newline at end of file
diff --git a/engines/nancy/input.h b/engines/nancy/input.h
new file mode 100644
index 0000000000..9ee62b2c2e
--- /dev/null
+++ b/engines/nancy/input.h
@@ -0,0 +1,86 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef NANCY_INPUT_H
+#define NANCY_INPUT_H
+
+#include "common/rect.h"
+namespace Common {
+template <class T>
+class Array;
+
+class Keymap;
+typedef class Array<Keymap*> KeymapArray;
+}
+
+namespace Nancy {
+
+class NancyEngine;
+
+class InputManager {
+enum NancyAction {
+    kNancyActionMoveUp,
+    kNancyActionMoveDown,
+    kNancyActionMoveLeft,
+    kNancyActionMoveRight,
+    kNancyActionMoveFast,
+    kNancyActionLeftClick,
+    kNancyActionRightClick,
+    kNancyActionFastConvoToggle,
+    kNancyActionEndConvoToggle,
+    kNancyActionReloadSave,
+    kNancyActionRequestMainMenu,
+    kNancyActionRequestSaveLoad,
+    kNancyActionRequestSetupMenu,
+    kNancyActionRequestCredits,
+    kNancyActionRequestMap
+};
+
+
+public:
+enum InputType : byte {
+    kLeftMouseButton    = 1 << 0,
+    kRightMouseButton   = 1 << 1,
+    kMoveUp             = 1 << 2,
+    kMoveDown           = 1 << 3,
+    kMoveLeft           = 1 << 4,
+    kMoveRight          = 1 << 5,
+    kMoveFastModifier   = 1 << 6
+};
+
+    InputManager(NancyEngine *engine) : _engine(engine), _inputs(0) {}
+    void processEvents();
+
+    bool getInput(InputType type);
+
+    static void initKeymaps(Common::KeymapArray &keymaps);
+
+private:
+    NancyEngine *_engine;
+
+    byte _inputs;
+    Common::Point _mousePos;
+};
+
+} // End of namespace Nancy
+
+#endif // NANCY_INPUT_H
\ No newline at end of file
diff --git a/engines/nancy/logo.cpp b/engines/nancy/logo.cpp
index af8d73788f..183bdab039 100644
--- a/engines/nancy/logo.cpp
+++ b/engines/nancy/logo.cpp
@@ -24,6 +24,7 @@
 #include "engines/nancy/nancy.h"
 #include "engines/nancy/resource.h"
 #include "engines/nancy/audio.h"
+#include "engines/nancy/input.h"
 
 #include "common/error.h"
 #include "common/system.h"
@@ -83,9 +84,7 @@ void LogoSequence::run() {
 		_runState = kWait;
 		break;
 	case kWait:
-		int buttons = g_system->getEventManager()->getButtonState();
-
-		if (_engine->_system->getMillis() - _startTicks >= 7000 || (buttons & Common::EventManager::LBUTTON))
+		if (_engine->_system->getMillis() - _startTicks >= 7000 || (_engine->input->getInput(InputManager::kLeftMouseButton)))
 			_state = kStop;
 	}
 }
diff --git a/engines/nancy/metaengine.cpp b/engines/nancy/metaengine.cpp
index bab7c73ad5..a4c4571349 100644
--- a/engines/nancy/metaengine.cpp
+++ b/engines/nancy/metaengine.cpp
@@ -21,6 +21,7 @@
  */
 
 #include "engines/nancy/nancy.h"
+#include "engines/nancy/input.h"
 
 #include "common/config-manager.h"
 #include "common/savefile.h"
@@ -62,8 +63,16 @@ public:
 	SaveStateList listSaves(const char *target) const override;
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const override;
 	void removeSaveState(const char *target, int slot) const override;
+
+	Common::KeymapArray initKeymaps(const char *target) const override;
 };
 
+Common::KeymapArray NancyMetaEngine::initKeymaps(const char *target) const {
+	Common::KeymapArray keymaps;
+	Nancy::InputManager::initKeymaps(keymaps);
+	return keymaps;
+}
+
 bool NancyMetaEngine::hasFeature(MetaEngineFeature f) const {
 	return
 	    (f == kSupportsListSaves) ||
diff --git a/engines/nancy/module.mk b/engines/nancy/module.mk
index 679ba8af11..5fd15a111b 100644
--- a/engines/nancy/module.mk
+++ b/engines/nancy/module.mk
@@ -9,6 +9,7 @@ MODULE_OBJS = \
   decompress.o \
   graphics.o \
   iff.o \
+  input.o \
   logic.o \
   logo.o \
   metaengine.o \
diff --git a/engines/nancy/nancy.cpp b/engines/nancy/nancy.cpp
index 1d9b2aee99..f851cb6bfa 100644
--- a/engines/nancy/nancy.cpp
+++ b/engines/nancy/nancy.cpp
@@ -28,6 +28,7 @@
 #include "engines/nancy/logo.h"
 #include "engines/nancy/scene.h"
 #include "engines/nancy/graphics.h"
+#include "engines/nancy/input.h"
 
 #include "common/system.h"
 #include "common/random.h"
@@ -75,6 +76,9 @@ NancyEngine::NancyEngine(OSystem *syst, const NancyGameDescription *gd) :
 	logic = new Logic(this);
 	sceneManager = new SceneManager(this);
 	graphics = new GraphicsManager(this);
+	input = new InputManager(this);
+
+	launchConsole = false;
 }
 
 NancyEngine::~NancyEngine() {
@@ -85,6 +89,7 @@ NancyEngine::~NancyEngine() {
 	
 	delete logic;
 	delete sceneManager;
+	delete input;
 }
 
 GUI::Debugger *NancyEngine::getDebugger() {
@@ -136,12 +141,9 @@ Common::Error NancyEngine::run() {
 	// Setup mixer
 	syncSoundSettings();
 
-	Common::EventManager *ev = g_system->getEventManager();
-	bool quit = false;
-
 	_gameFlow.minGameState = kBoot;
 
-	while (!shouldQuit() && !quit) {
+	while (!shouldQuit()) {
 		switch (_gameFlow.minGameState) {
 		case kBoot:
 			bootGameEngine();
@@ -161,45 +163,17 @@ Common::Error NancyEngine::run() {
 			break;
 		}
 
-		Common::Event event;
-		if (ev->pollEvent(event)) {
-			if (event.type == Common::EVENT_KEYDOWN && (event.kbd.flags & Common::KBD_CTRL)) {
-				switch (event.kbd.keycode) {
-				case Common::KEYCODE_q:
-					quit = true;
-					break;
-				case Common::KEYCODE_d:
-					_console->attach();
-				default:
-					break;
-				}
-			}
-		}
+		input->processEvents();
 
+		if (launchConsole) {
+			_console->attach();
+			launchConsole = false;
+		}
 		_console->onFrame();
 
 		_system->updateScreen();
 		_system->delayMillis(16);
 	}
-#if 0
-	// Play music
-	Common::SeekableReadStream *mSnd = SearchMan.createReadStreamForMember(_menuSound.name + ".his");
-	if (mSnd) {
-		Audio::RewindableAudioStream *aStr = makeHISStream(mSnd, DisposeAfterUse::YES);
-		if (aStr) {
-			Audio::AudioStream *aStrLoop = Audio::makeLoopingAudioStream(aStr, 0);
-			Audio::SoundHandle handle;
-			_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &handle, aStrLoop);
-		}
-	}
-
-	// Show logo
-	Graphics::Surface surf;
-	if (_res->loadImage("ciftree", _logos[0].name, surf)) {
-		_system->copyRectToScreen(surf.getPixels(), surf.pitch, 0, 0, surf.w, surf.h);
-		surf.free();
-	}
-#endif
 
 	return Common::kNoError;
 }
diff --git a/engines/nancy/nancy.h b/engines/nancy/nancy.h
index 82f3d19df3..d479d168f0 100644
--- a/engines/nancy/nancy.h
+++ b/engines/nancy/nancy.h
@@ -67,6 +67,7 @@ class LogoSequence;
 class SceneManager;
 class Logic;
 class GraphicsManager;
+class InputManager;
 
 class NancyEngine : public Engine {
 public:
@@ -101,6 +102,8 @@ public:
 	void syncSoundSettings();
 
 	static NancyEngine *create(GameType type, OSystem *syst, const NancyGameDescription *gd);
+
+	bool launchConsole;
 	
 	// Chunks found in BOOT get extracted and cached at startup, this function lets other classes access them
 	Common::SeekableReadStream *getBootChunkStream(const Common::String &name);
@@ -110,6 +113,7 @@ public:
 	Logic *logic;
 	SceneManager *sceneManager;
 	GraphicsManager *graphics;
+	InputManager *input;
 
 protected:
 	// Engine APIs


Commit: f60ccc38b984ea9e71ef3304ba77c8e886370f0e
    https://github.com/scummvm/scummvm/commit/f60ccc38b984ea9e71ef3304ba77c8e886370f0e
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: runScene() improvemens

Implemented a bit more of runScene(), but a large part of it is still TODO.

Changed paths:
    engines/nancy/datatypes.cpp
    engines/nancy/datatypes.h
    engines/nancy/input.cpp
    engines/nancy/input.h
    engines/nancy/playstate.h
    engines/nancy/scene.cpp
    engines/nancy/scene.h


diff --git a/engines/nancy/datatypes.cpp b/engines/nancy/datatypes.cpp
index 969880e8b7..2e81b6b6ed 100644
--- a/engines/nancy/datatypes.cpp
+++ b/engines/nancy/datatypes.cpp
@@ -46,11 +46,12 @@ SceneSummary::SceneSummary(Common::SeekableReadStream *stream) {
     buf[9] = 0;
     audioFile = Common::String(buf);
 
-    stream->seek(0x74);
+    stream->seek(0x72);
+    verticalScrollDelta = stream->readUint16LE();
     sceneHasRotation = stream->readUint16LE();
     sceneHasMovement = stream->readUint16LE();
-    unknown78 = stream->readUint16LE();
-    unknown7A = stream->readUint16LE();
+    slowMoveTimeDelta = stream->readUint16LE();
+    fastMoveTimeDelta = stream->readUint16LE();
     unknown7C = stream->readByte();
 
     // put the entire chunk into a temp buffer until we figure out the rest of the structure
diff --git a/engines/nancy/datatypes.h b/engines/nancy/datatypes.h
index fc08954fa9..045aa1fa00 100644
--- a/engines/nancy/datatypes.h
+++ b/engines/nancy/datatypes.h
@@ -45,10 +45,11 @@ struct SceneSummary {
     uint16 videoFormat;         // 0x3E, value is 1 or 2
     Common::String audioFile;   // 0x40
     //
+    uint16 verticalScrollDelta; // 0x72
     uint16 sceneHasRotation;    // 0x74, could be an enum?
     uint16 sceneHasMovement;    // 0x76, also an enum??
-    uint16 unknown78;           // 0x78
-    uint16 unknown7A;           // 0x7A
+    uint16 slowMoveTimeDelta;   // 0x78
+    uint16 fastMoveTimeDelta;   // 0x7A
     byte unknown7C;             // 0x7C, enum with 4 values
     //
     byte *chunkData;
diff --git a/engines/nancy/input.cpp b/engines/nancy/input.cpp
index a0bd5171bd..d5fd963176 100644
--- a/engines/nancy/input.cpp
+++ b/engines/nancy/input.cpp
@@ -22,6 +22,7 @@
 
 #include "engines/nancy/input.h"
 #include "engines/nancy/nancy.h"
+#include "engines/nancy/scene.h"
 
 #include "common/events.h"
 #include "common/keyboard.h"
@@ -31,10 +32,34 @@
 #include "backends/keymapper/standard-actions.h"
 
 namespace Nancy {
-    
+
+const int16 InputManager::mapButtonID               = 10000;
+const int16 InputManager::textBoxID                 = 10002;
+const int16 InputManager::textBoxScrollbarID        = 10003;
+const int16 InputManager::helpButtonID              = 10004;
+const int16 InputManager::menuButtonID              = 10005;
+const int16 InputManager::inventoryScrollbarID      = 10006;
+const int16 InputManager::inventoryItemTakeID       = 10007;
+const int16 InputManager::inventoryItemReturnID     = 10008;
+const int16 InputManager::orderingPuzzleID          = 10009;
+const int16 InputManager::orderingPuzzleEndID       = 10010;
+const int16 InputManager::rotatingLockPuzzleUpID    = 10011;
+const int16 InputManager::rotatingLockPuzzleDownID  = 10012;
+const int16 InputManager::rotatingLockPuzzleEndID   = 10013;
+const int16 InputManager::leverPuzzleID             = 10014; // not sure abt the lever ones
+const int16 InputManager::leverPuzzleEndID          = 10015;
+const int16 InputManager::telephoneID               = 10016;
+const int16 InputManager::telephoneEndID            = 10017;
+const int16 InputManager::sliderPuzzleID            = 10018;
+const int16 InputManager::sliderPuzzleEndID         = 10019;
+const int16 InputManager::passwordPuzzleEndID       = 10020;
+
 void InputManager::processEvents() {
     using namespace Common;
 
+    isClickValidLMB = false;
+    isClickValidRMB = false;
+
     Common::Event event;
 
     // TODO consider adding a keymapper
@@ -45,25 +70,29 @@ void InputManager::processEvents() {
                 // TODO add debug shortcuts
                 switch (event.customType) {
                     case kNancyActionMoveUp:
-                        _inputs |= (byte)kMoveUp;
+                        _inputs |= kMoveUp;
+                        _engine->sceneManager->movementDirection |= SceneManager::kUp;
                         break;
                     case kNancyActionMoveDown:
-                        _inputs |= (byte)kMoveDown;
+                        _inputs |= kMoveDown;
+                        _engine->sceneManager->movementDirection |= SceneManager::kDown;
                         break;
                     case kNancyActionMoveLeft:
-                        _inputs |= (byte)kMoveLeft;
+                        _inputs |= kMoveLeft;
+                        _engine->sceneManager->movementDirection |= SceneManager::kLeft;
                         break;
                     case kNancyActionMoveRight:
-                        _inputs |= (byte)kMoveRight;
+                        _inputs |= kMoveRight;
+                        _engine->sceneManager->movementDirection |= SceneManager::kRight;
                         break;
                     case kNancyActionMoveFast:
-                        _inputs |= (byte)kMoveFastModifier;
+                        _inputs |= kMoveFastModifier;
                         break;
                     case kNancyActionLeftClick:
-                        _inputs |= (byte)kLeftMouseButton;
+                        _inputs |= kLeftMouseButton;
                         break;
                     case kNancyActionRightClick:
-                        _inputs |= (byte)kRightMouseButton;
+                        _inputs |= kRightMouseButton;
                         break;
                     default:
                         break;
@@ -72,25 +101,31 @@ void InputManager::processEvents() {
             case EVENT_CUSTOM_ENGINE_ACTION_END:
                 switch (event.customType) {
                     case kNancyActionMoveUp:
-                        _inputs &= ~(byte)kMoveUp;
+                        _inputs &= ~kMoveUp;
                         break;
                     case kNancyActionMoveDown:
-                        _inputs &= ~(byte)kMoveDown;
+                        _inputs &= ~kMoveDown;
                         break;
                     case kNancyActionMoveLeft:
-                        _inputs &= ~(byte)kMoveLeft;
+                        _inputs &= ~kMoveLeft;
                         break;
                     case kNancyActionMoveRight:
-                        _inputs &= ~(byte)kMoveRight;
+                        _inputs &= ~kMoveRight;
                         break;
                     case kNancyActionMoveFast:
-                        _inputs &= ~(byte)kMoveFastModifier;
+                        _inputs &= ~kMoveFastModifier;
                         break;
                     case kNancyActionLeftClick:
-                        _inputs &= ~(byte)kLeftMouseButton;
+                        _inputs &= ~kLeftMouseButton;
+                        if (hoveredElementID != -1) {
+                            isClickValidLMB = true;
+                        }
                         break;
                     case kNancyActionRightClick:
-                        _inputs &= ~(byte)kRightMouseButton;
+                        _inputs &= ~kRightMouseButton;
+                        if (hoveredElementID != -1) {
+                            isClickValidRMB = true;
+                        }
                         break;
                     default:
                         break;
@@ -121,10 +156,19 @@ void InputManager::processEvents() {
                 break;
         }
     }
+
+    // Discard conflicting directions
+    byte dir = _engine->sceneManager->movementDirection;
+    if ((dir & SceneManager::kUp) && (dir & SceneManager::kDown)) {
+        _engine->sceneManager->movementDirection &= !(SceneManager::kUp | SceneManager::kDown);
+    }
+    if ((dir & SceneManager::kLeft) && (dir & SceneManager::kRight)) {
+        _engine->sceneManager->movementDirection &= !(SceneManager::kLeft | SceneManager::kRight);
+    }
 }
 
 bool InputManager::getInput(InputManager::InputType type) {
-    return _inputs & (byte)type;
+    return _inputs & type;
 }
 
 void InputManager::initKeymaps(Common::KeymapArray &keymaps) {
diff --git a/engines/nancy/input.h b/engines/nancy/input.h
index 9ee62b2c2e..ee0468edb3 100644
--- a/engines/nancy/input.h
+++ b/engines/nancy/input.h
@@ -55,7 +55,6 @@ enum NancyAction {
     kNancyActionRequestMap
 };
 
-
 public:
 enum InputType : byte {
     kLeftMouseButton    = 1 << 0,
@@ -67,13 +66,41 @@ enum InputType : byte {
     kMoveFastModifier   = 1 << 6
 };
 
-    InputManager(NancyEngine *engine) : _engine(engine), _inputs(0) {}
+    InputManager(NancyEngine *engine) : _engine(engine), _inputs(0), isClickValidLMB(false), isClickValidRMB(false), hoveredElementID(-1) {}
     void processEvents();
 
     bool getInput(InputType type);
+    byte getInput() { return _inputs; }
 
     static void initKeymaps(Common::KeymapArray &keymaps);
 
+    bool isClickValidLMB;
+    bool isClickValidRMB;
+    int16 hoveredElementID;
+    
+    // TODO consider using a namespace for these
+    static const int16 mapButtonID;
+    static const int16 textBoxID;
+    static const int16 textBoxScrollbarID;
+    static const int16 helpButtonID;
+    static const int16 menuButtonID;
+    static const int16 inventoryScrollbarID;
+    static const int16 inventoryItemTakeID;
+    static const int16 inventoryItemReturnID;
+
+    static const int16 orderingPuzzleID;
+    static const int16 orderingPuzzleEndID;
+    static const int16 rotatingLockPuzzleUpID;
+    static const int16 rotatingLockPuzzleDownID;
+    static const int16 rotatingLockPuzzleEndID;
+    static const int16 leverPuzzleID;
+    static const int16 leverPuzzleEndID;
+    static const int16 telephoneID;
+    static const int16 telephoneEndID;
+    static const int16 sliderPuzzleID;
+    static const int16 sliderPuzzleEndID;
+    static const int16 passwordPuzzleEndID;
+
 private:
     NancyEngine *_engine;
 
diff --git a/engines/nancy/playstate.h b/engines/nancy/playstate.h
index 4d4d97ef5b..faea263714 100644
--- a/engines/nancy/playstate.h
+++ b/engines/nancy/playstate.h
@@ -45,11 +45,13 @@ struct PlayState {
     Time playerTime; // Nancy's in-game time of day, adds a minute every 5 seconds
     Time playerTimeNextMinute; // Stores the next tick count until we add a minute to playerTime
     TimeOfDay timeOfDay = kDay;
-    uint16 currentViewFrame = 0;
-    uint16 queuedViewFrame = 0;
+    int16 currentViewFrame = 0;
+    int16 lastDrawnViewFrame = -1; 
+    int16 queuedViewFrame = 0; // Used when changing scenes
     uint16 currentMaxVerticalScroll = 0;
     uint16 queuedMaxVerticalScroll = 0;
     uint16 verticalScroll = 0; // This replaces rDisplayed
+    uint16 verticalScrollDelta = 0;
 };
 
 } // End of namespace Nancy
diff --git a/engines/nancy/scene.cpp b/engines/nancy/scene.cpp
index c281ca9e4d..c367faada6 100644
--- a/engines/nancy/scene.cpp
+++ b/engines/nancy/scene.cpp
@@ -26,6 +26,7 @@
 #include "engines/nancy/iff.h"
 #include "engines/nancy/logic.h"
 #include "engines/nancy/graphics.h"
+#include "engines/nancy/input.h"
 
 #include "common/memstream.h"
 #include "common/rect.h"
@@ -223,7 +224,6 @@ void SceneManager::init() {
 }
 
 void SceneManager::load() {
-
     // Scene IDs are prefixed with S inside the cif tree; e.g 100 -> S100                                                                                    
     Common::String sceneName = Common::String::format("S%u", _sceneID);
     IFF sceneIFF(_engine, sceneName);
@@ -243,10 +243,10 @@ void SceneManager::load() {
     // Not sure what these do yet
     Common::SeekableReadStream *bsum = _engine->getBootChunkStream("BSUM");
     bsum->seek(0x1F1);
-    byte unknown = bsum->readByte();
-    if (unknown) {
-        currentScene.unknown78 = bsum->readUint16LE();
-        currentScene.unknown7A = bsum->readUint16LE();
+    byte overrideMovementDeltas = bsum->readByte();
+    if (overrideMovementDeltas) {
+        currentScene.slowMoveTimeDelta = bsum->readUint16LE();
+        currentScene.fastMoveTimeDelta = bsum->readUint16LE();
     }
 
     // Search for Action Records, maximum for a scene is 30
@@ -276,6 +276,7 @@ void SceneManager::load() {
         } else if (currentScene.videoFormat == 2) {
             // always start from the bottom
             playState.verticalScroll = playState.currentMaxVerticalScroll;
+            playState.verticalScrollDelta = currentScene.verticalScrollDelta;
         } else {
             error("Unrecognized Scene summary chunk video file format");
         }
@@ -350,7 +351,9 @@ void SceneManager::run() {
         return;
     }
 
-    // Unknown if, have never triggered it
+    if (saveReloadRequested) {
+        // TODO
+    }
 
     if (setupMenuRequested) {
         _stashedTickCount = _engine->getTotalPlayTime();
@@ -384,32 +387,32 @@ void SceneManager::run() {
 
     // Cheat menu, will not implement
 
+    uint32 playTimeThisFrame = _engine->getTotalPlayTime();
+
     // Do some work if we're coming from a different game state
     if (_engine->_gameFlow.previousGameState != NancyEngine::GameState::kScene) {
-        uint32 t = _engine->getTotalPlayTime();
         if (hasLoadedFromSavefile) {
-            if (t > _stashedTickCount) {
-                t -= _stashedTickCount;
-                playState.totalTime -= t;
-                playState.sceneTime -= t;
+            if (playTimeThisFrame > _stashedTickCount) {
+                playTimeThisFrame -= _stashedTickCount;
+                playState.totalTime -= playTimeThisFrame;
+                playState.sceneTime -= playTimeThisFrame;
                 if (playState.timerIsActive)
-                    playState.timerTime -= t;
+                    playState.timerTime -= playTimeThisFrame;
             }
         }
             
         _engine->graphics->getGenericZRenderStruct(12)->isActive = false; // MENU BTN DN
         _engine->graphics->getGenericZRenderStruct(13)->isActive = false; // HELP BTN DN
-        hovered = -1;
+        _engine->input->hoveredElementID = -1;
         // TODO a bunch of function calls
         _engine->_gameFlow.previousGameState = NancyEngine::GameState::kScene;
         return;
     }
 
-    uint32 t = _engine->getTotalPlayTime();
     uint32 diff = 0;
-    if (_tickCount < t) {
-        diff = t - _tickCount;
-        _tickCount = t;
+    if (_tickCount < playTimeThisFrame) {
+        diff = playTimeThisFrame - _tickCount;
+        _tickCount = playTimeThisFrame;
     }
     playState.totalTime += diff;
     if (playState.timerIsActive)
@@ -417,9 +420,9 @@ void SceneManager::run() {
     playState.sceneTime =+ diff;
 
     // Calculate the in-game time (playerTime)
-    if (t > playState.playerTimeNextMinute) {
+    if (playTimeThisFrame > playState.playerTimeNextMinute) {
         playState.playerTime += 60000; // Add a minute
-        playState.playerTimeNextMinute = t + playerTimeMinuteLength; // Set when we're going to add the next minute
+        playState.playerTimeNextMinute = playTimeThisFrame + playerTimeMinuteLength; // Set when we're going to add the next minute
     }
 
     // Set the time of day according to playerTime
@@ -430,16 +433,200 @@ void SceneManager::run() {
     } else {
         playState.timeOfDay = playState.kDuskDawn;
     }
+
+    if (_engine->input->isClickValidLMB) {
+        if (orderingPuzzleIsActive) {
+            // TODO
+        }
+
+        if (sliderPuzzleIsActive) {
+            // TODO
+        }
+
+        if (orderingPuzzleIsActive) {
+            // TODO
+        }
+
+        if (telephoneIsActive) {
+            // TODO
+        }
+
+        if (leverPuzzleIsActive) {
+            // TODO   
+        }
+
+        if (passwordPuzzleIsActive) {
+
+        }
+
+        int16 &hovered = _engine->input->hoveredElementID;
+
+        if (hovered == InputManager::mapButtonID) {
+            // TODO another if
+            mapScreenRequested = true;
+            return;
+        }
+
+        if (hovered == InputManager::textBoxID) {
+            // TODO
+        }
+
+        if (hovered == InputManager::inventoryItemTakeID) {
+            // TODO
+        }
+
+        if (hovered == InputManager::inventoryItemReturnID) {
+            // TODO
+        }
+
+        if (hovered == InputManager::textBoxScrollbarID) {
+            // TODO
+        }
+
+        if (hovered == InputManager::inventoryScrollbarID) {
+            // TODO
+        }
+
+        if (hovered == InputManager::menuButtonID) {
+            // TODO
+        }
+
+        if (hovered == InputManager::helpButtonID) {
+            // TODO
+        }
+
+        if (    hovered == InputManager::orderingPuzzleID ||
+                hovered == InputManager::orderingPuzzleEndID ||
+                hovered == InputManager::rotatingLockPuzzleUpID ||
+                hovered == InputManager::rotatingLockPuzzleDownID ||
+                hovered == InputManager::rotatingLockPuzzleEndID ||
+                hovered == InputManager::leverPuzzleID ||
+                hovered == InputManager::leverPuzzleEndID ||
+                hovered == InputManager::telephoneID ||
+                hovered == InputManager::telephoneEndID ||
+                hovered == InputManager::sliderPuzzleID ||
+                hovered == InputManager::sliderPuzzleEndID ||
+                hovered == InputManager::passwordPuzzleEndID) {
+            // TODO
+        }
+
+    } else if (_engine->input->isClickValidRMB) {
+        if (_engine->input->hoveredElementID == InputManager::textBoxScrollbarID) {
+            // TODO, moves scrollbar one line up
+        } else if (_engine->input->hoveredElementID == InputManager::inventoryScrollbarID) {
+            // TODO, moves scrollbar one line up
+        } else if (_engine->input->hoveredElementID == InputManager::textBoxID) {
+            // TODO
+        }
+    } else {
+        // Perform movement
+        byte inputs = _engine->input->getInput();
+        if  ( ( (
+                    (inputs & InputManager::kLeftMouseButton) != (inputs & InputManager::kRightMouseButton) 
+                ) ||
+                (inputs & (InputManager::kMoveUp | InputManager::kMoveDown | InputManager::kMoveLeft | InputManager::kMoveRight) )
+              ) &&
+                movementDirection != 0 &&
+                playTimeThisFrame > _nextBackgroundMovement
+            ) {
+            switch(movementDirection) {
+                case kLeft:
+                    playState.currentViewFrame += 1;
+                    if (playState.currentViewFrame >= (int16)_engine->graphics->getBackgroundFrameCount()) {
+                        playState.currentViewFrame = 0;
+                    }
+                    break;
+                case kRight:
+                    playState.currentViewFrame -= 1;
+                    if (playState.currentViewFrame < 0) {
+                        playState.currentViewFrame = (int16)_engine->graphics->getBackgroundFrameCount() -1;
+                    }
+                    break;
+                case kUp:
+                    if (playState.verticalScroll != 0) {
+                        int16 newScroll = playState.verticalScroll - playState.verticalScrollDelta;
+                        playState.verticalScroll = MAX(newScroll, (int16)0);
+                    }
+                    break;
+                case kDown:
+                    if (playState.verticalScroll != playState.currentMaxVerticalScroll) {
+                        uint16 newScroll = playState.verticalScroll + playState.verticalScrollDelta;
+                        playState.verticalScroll = MIN(newScroll, playState.currentMaxVerticalScroll);
+                    }
+                    break;
+                case kUp | kLeft:
+                    if (playState.verticalScroll != 0) {
+                        int16 newScroll = playState.verticalScroll - playState.verticalScrollDelta;
+                        playState.verticalScroll = MAX(newScroll, (int16)0);
+                    }
+                    playState.currentViewFrame += 1;
+                    if (playState.currentViewFrame >= (int16)_engine->graphics->getBackgroundFrameCount()) {
+                        playState.currentViewFrame = 0;
+                    }
+                    break;
+                case kUp | kRight:
+                    if (playState.verticalScroll != 0) {
+                        int16 newScroll = playState.verticalScroll - playState.verticalScrollDelta;
+                        playState.verticalScroll = MAX(newScroll, (int16)0);
+                    }
+                    playState.currentViewFrame -= 1;
+                    if (playState.currentViewFrame < 0) {
+                        playState.currentViewFrame = _engine->graphics->getBackgroundFrameCount() - 1;
+                    }
+                    break;
+                case kDown | kLeft:
+                    if (playState.verticalScroll != playState.currentMaxVerticalScroll) {
+                        uint16 newScroll = playState.verticalScroll + playState.verticalScrollDelta;
+                        playState.verticalScroll = MIN(newScroll, playState.currentMaxVerticalScroll);
+                    }
+                    playState.currentViewFrame += 1;
+                    if (playState.currentViewFrame >= (int16)_engine->graphics->getBackgroundFrameCount()) {
+                        playState.currentViewFrame = 0;
+                    }
+                    break;
+                case kDown | kRight:
+                    if (playState.verticalScroll != playState.currentMaxVerticalScroll) {
+                        uint16 newScroll = playState.verticalScroll + playState.verticalScrollDelta;
+                        playState.verticalScroll = MIN(newScroll, playState.currentMaxVerticalScroll);
+                    }
+                    playState.currentViewFrame -= 1;
+                    if (playState.currentViewFrame < 0) {
+                        playState.currentViewFrame = _engine->graphics->getBackgroundFrameCount() -1;
+                    }
+                    break;
+            }
+            if (_engine->input->getInput(InputManager::kMoveFastModifier) ||
+                _engine->input->getInput(InputManager::kRightMouseButton)) {
+                _nextBackgroundMovement = playTimeThisFrame + currentScene.fastMoveTimeDelta;
+
+            } else {
+                _nextBackgroundMovement = playTimeThisFrame + currentScene.slowMoveTimeDelta;
+            }
+        }
+    }
+
+    // Redraw the Background surface if we've moved
+    if (playState.currentViewFrame != playState.lastDrawnViewFrame) {
+        if (currentScene.videoFormat == 1) {
+            // TODO if it ever gets hit
+        } else if (currentScene.videoFormat == 2) {
+            _engine->graphics->_background.copyRectToSurface(
+            *_engine->graphics->getBackgroundFrame(playState.currentViewFrame),
+            0, 0,
+            Common::Rect(0, playState.verticalScroll, _engine->graphics->getBackgroundWidth() - 1,
+                playState.verticalScroll + _engine->graphics->viewportDesc.srcBottom));
+        }
+        // TODO some if related to PlaySoundPanFrameAnchorAndDie
+        playState.lastDrawnViewFrame = playState.currentViewFrame;
+        // TODO function call that sets a val to 1 and 3 others to 0
+    }
+
+    //_engine->logic->processActionRecords();
+
+    // code that skips rendering for the first 12 frames??? why
+
+    // TODO
     
-    // TODO we're skipping a lot of things like mouse handling and scene movement
-    // so we can get _something_ on screen
-
-    // TODO dont call every frame
-    _engine->graphics->_background.copyRectToSurface(
-        *_engine->graphics->getBackgroundFrame(playState.currentViewFrame),
-        0, 0,
-        Common::Rect(0, playState.verticalScroll, _engine->graphics->getBackgroundWidth() - 1,
-            playState.verticalScroll + _engine->graphics->viewportDesc.srcBottom)); // TODO fix magic number
     _engine->graphics->renderDisplay(25);
 }
 
diff --git a/engines/nancy/scene.h b/engines/nancy/scene.h
index ec454327b9..ff96c12f60 100644
--- a/engines/nancy/scene.h
+++ b/engines/nancy/scene.h
@@ -44,10 +44,12 @@ class NancyEngine;
 
 class SceneManager {
 public:
+    enum MovementDirection : byte { kUp = 1, kDown = 2, kLeft = 4, kRight = 8 };
     SceneManager(NancyEngine *engine) :
         _engine {engine},
         _state {kInit},
-        _sceneID {0} { }
+        _sceneID {0},
+        movementDirection{0} { }
     ~SceneManager();
 
     void process();
@@ -70,6 +72,7 @@ private:
 public:
     PlayState playState;
     int32 playerTimeMinuteLength;
+    byte movementDirection;
 
 private:
     NancyEngine *_engine;
@@ -79,7 +82,7 @@ private:
     // TODO these two can be Time
     uint32 _tickCount;
     uint32 _stashedTickCount;
-    int16 hovered = -1;
+    Time _nextBackgroundMovement;
 
     bool isComingFromMenu = true;
     bool hasLoadedFromSavefile = false;
@@ -95,7 +98,7 @@ private:
     bool helpMenuRequested = false;
     bool mainMenuRequested = false;
     bool saveLoadRequested = false;
-    // not sure
+    bool saveReloadRequested = false;
     bool setupMenuRequested = false;
     bool creditsSequenceRequested = false;
     bool mapScreenRequested = false;


Commit: 0a4ed23f51bf2bbbdbaabdf77631bee8ce2abd8b
    https://github.com/scummvm/scummvm/commit/0a4ed23f51bf2bbbdbaabdf77631bee8ce2abd8b
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add action event processing

Implemented (almost) all of processActionRecords(), which is the primary logic processing function of the engine. Moved the playState structure from Scene to NancyEngine.

Changed paths:
    engines/nancy/action/actionrecord.h
    engines/nancy/action/recordtypes.cpp
    engines/nancy/logic.cpp
    engines/nancy/logic.h
    engines/nancy/nancy.h
    engines/nancy/playstate.h
    engines/nancy/scene.cpp
    engines/nancy/scene.h


diff --git a/engines/nancy/action/actionrecord.h b/engines/nancy/action/actionrecord.h
index 7fe2cd0595..fe86e7b8ee 100644
--- a/engines/nancy/action/actionrecord.h
+++ b/engines/nancy/action/actionrecord.h
@@ -23,8 +23,11 @@
 #ifndef NANCY_ACTION_ACTIONRECORD_H
 #define NANCY_ACTION_ACTIONRECORD_H
 
+#include "engines/nancy/time.h"
+
 #include "common/str.h"
 #include "common/stream.h"
+#include "common/rect.h"
 
 namespace Nancy {
 
@@ -34,11 +37,14 @@ enum DependencyType : byte {
     kNone               = 0,
     kInventory          = 1,
     kEventFlag          = 2,
-    // ...
-    kPlayTime           = 4,
+    kLogicCondition     = 3,
+    kTotalTime          = 4,
     kSceneTime          = 5,
+    kPlayerTime         = 6,
+    // ...
     kSceneCount         = 9,
     // ...
+    kTimeOfDay          = 12,
     kTimerNotDone       = 13,
     kTimerDone          = 14,
     kDifficultyLevel    = 15
@@ -63,11 +69,13 @@ public:
         execType(0),
         dependencies(nullptr),
         numDependencies(0),
-        hasNoDependencies(0),
-        hasSatisfiedCondition(false),
+        isActive(0),
+        satisfiedDependencies(nullptr),
+        timers(nullptr),
+        orFlags(nullptr),
         isDone(false),
         state(ExecutionState::Start) {}
-    virtual ~ActionRecord() { delete[] dependencies; delete rawData; }
+    virtual ~ActionRecord() { delete[] dependencies; delete rawData; delete[] satisfiedDependencies; delete[] timers; delete orFlags; }
 
     virtual uint16 readData(Common::SeekableReadStream &stream) =0;
     virtual void execute(NancyEngine *engine) {};
@@ -88,12 +96,14 @@ public:
     // 0x32 data
     DependencyRecord *dependencies; // 0x36
     byte numDependencies;           // 0x3A
-    bool hasNoDependencies;         // 0x3B, not sure abt this one
-    bool hasSatisfiedCondition;     // 0x3C
-    int32 timers[12];               // 0x48, not sure how many there can be
+    bool isActive;                  // 0x3B
+    bool *satisfiedDependencies;    // 0x3C
+    Time *timers;                   // 0x48
+    bool *orFlags;                  // 0x78
     bool isDone;                    // 0x84
+    Common::Rect activeZone;        // 0x89
     ExecutionState state;           // 0x91
-    };
+};
 
 } // End of namespace Nancy
 
diff --git a/engines/nancy/action/recordtypes.cpp b/engines/nancy/action/recordtypes.cpp
index 03fc7de6ab..5db7d5fb1d 100644
--- a/engines/nancy/action/recordtypes.cpp
+++ b/engines/nancy/action/recordtypes.cpp
@@ -255,8 +255,8 @@ uint16 ResetAndStartTimer::readData(Common::SeekableReadStream &stream) {
 }
 
 void ResetAndStartTimer::execute(NancyEngine *engine) {
-    engine->sceneManager->playState.timerIsActive = true;
-    engine->sceneManager->playState.timerTime = 0;
+    engine->playState.timerIsActive = true;
+    engine->playState.timerTime = 0;
     isDone = true;
 }
 
@@ -266,8 +266,8 @@ uint16 StopTimer::readData(Common::SeekableReadStream &stream) {
 }
 
 void StopTimer::execute(NancyEngine *engine) {
-    engine->sceneManager->playState.timerIsActive = false;
-    engine->sceneManager->playState.timerTime = 0;
+    engine->playState.timerIsActive = false;
+    engine->playState.timerTime = 0;
     isDone = true;
 }
 
@@ -290,7 +290,7 @@ uint16 EventFlags::readData(Common::SeekableReadStream &stream) {
 void EventFlags::execute(NancyEngine *engine) {
     for (uint i = 0; i < 10; ++i) {
         if (descs[i].label != -1) {
-            engine->sceneManager->playState.eventFlags[descs[i].label] = descs[i].flag;
+            engine->playState.eventFlags[descs[i].label] = descs[i].flag;
         }
     }
     isDone = true;
@@ -357,9 +357,9 @@ uint16 DifficultyLevel::readData(Common::SeekableReadStream &stream) {
 }
 
 void DifficultyLevel::execute(NancyEngine *engine) {
-    engine->sceneManager->playState.difficulty = difficulty;
+    engine->playState.difficulty = difficulty;
     if (flagLabel != -1) {
-        engine->sceneManager->playState.eventFlags[flagLabel] = (PlayState::Flag)flagCondition;
+        engine->playState.eventFlags[flagLabel] = (PlayState::Flag)flagCondition;
     }
     isDone = true;
 }
diff --git a/engines/nancy/logic.cpp b/engines/nancy/logic.cpp
index 770acfda51..2f8cc71c36 100644
--- a/engines/nancy/logic.cpp
+++ b/engines/nancy/logic.cpp
@@ -21,6 +21,8 @@
  */
 
 #include "engines/nancy/logic.h"
+#include "engines/nancy/nancy.h"
+#include "engines/nancy/playstate.h"
 
 #include "common/memstream.h"
 
@@ -52,7 +54,10 @@ bool Logic::addNewActionRecord(Common::SeekableReadStream &inputData) {
             error("Invalid dependency data size!");
         }
 
-        newRecord->dependencies = new DependencyRecord[newRecord->numDependencies];
+        newRecord->dependencies = new DependencyRecord[newRecord->numDependencies]();
+        newRecord->satisfiedDependencies = new bool[newRecord->numDependencies]();
+        newRecord->timers = new Time[newRecord->numDependencies]();
+        newRecord->orFlags = new bool[newRecord->numDependencies]();
 
         // Initialize the dependencies data
         inputData.seek(/*0x32 + */localChunkSize);
@@ -76,15 +81,142 @@ bool Logic::addNewActionRecord(Common::SeekableReadStream &inputData) {
 
         for (uint16 i = 0; i < newRecord->numDependencies; ++i) {
             if (newRecord->dependencies[i].orFlag == 1) {
-                // newRecord->0x78+i = true
+                newRecord->orFlags[i] = true;
             } else {
-                // 0x78+i = false
+                newRecord->orFlags[i] = false;
             }
         }
+    } else {
+        // Set new record to active if it doesn't depend on anything
+        newRecord->isActive = true;
     }
 
     _records.push_back(newRecord);
     return true;
 }
 
+void Logic::processActionRecords() {
+    for (auto record : _records) {
+        if (record->isDone) {
+            continue;
+        }
+
+        if (!record->isActive) {
+            if (record->numDependencies > 0) {
+                for (uint i = 0; i < record->numDependencies; ++i) {
+                    if (record->satisfiedDependencies[i] != 0) {
+                        DependencyRecord &dep = record->dependencies[i];
+                        switch (record->dependencies[1].type) {
+                            case kNone:
+                                record->satisfiedDependencies[i] = true;
+                            case kInventory:
+                                // TODO
+                                break;
+                            case kEventFlag:
+                                if (_engine->playState.eventFlags[dep.label] == dep.condition)
+                                    // nancy1 has code for some timer array that never gets used
+                                    // and is discarded from nancy2 onward
+                                    record->satisfiedDependencies[i] = true;
+                                break;
+                            case kLogicCondition:
+                                if (_engine->playState.logicConditions[dep.label] == dep.condition) {
+                                    // Wait for specified time before satisfying dependency condition
+                                    Time elapsed = _engine->playState.totalTime - _engine->playState.logicConditionsTimestamps[dep.label];
+                                    if (elapsed >= record->timers[i])
+                                        record->satisfiedDependencies[i] = true;
+                                }
+                                break;
+                            case kTotalTime:
+                                if (_engine->playState.totalTime >= record->timers[i])
+                                    record->satisfiedDependencies[i] = true;
+                                break;
+                            case kSceneTime:
+                                if (_engine->playState.sceneTime >= record->timers[i])
+                                    record->satisfiedDependencies[i] = true;
+                                break;
+                            case kPlayerTime:
+                                if (_engine->playState.playerTime >= record->timers[i])
+                                    record->satisfiedDependencies[i] = true;
+                                break;
+                            /*case 7:
+                                // TODO
+                                break;
+                            case 8:
+                                // TODO
+                                break;*/
+                            case kSceneCount:
+                                // This dependency type keeps its data in the time variables
+                                // Also, I'm pretty sure it never gets used
+                                switch (dep.milliseconds) {
+                                    case 1:
+                                        if (dep.seconds < _engine->playState.sceneHitCount[dep.hours])
+                                            record->satisfiedDependencies[i] = true;
+                                        break;
+                                    case 2:
+                                        if (dep.seconds > _engine->playState.sceneHitCount[dep.hours])
+                                            record->satisfiedDependencies[i] = true;
+                                        break;
+                                    case 3:
+                                        if (dep.seconds == _engine->playState.sceneHitCount[dep.hours])
+                                            record->satisfiedDependencies[i] = true;
+                                        break;
+                                }
+                                break;
+                            /*case 10:
+                                // TODO
+                                break;
+                            case 11:
+                                // TODO
+                                break;*/
+                            case kTimeOfDay:
+                                if (dep.label == (byte)_engine->playState.timeOfDay)
+                                    record->satisfiedDependencies[i] = true;
+                                break;
+                            case kTimerNotDone:
+                                if (_engine->playState.timerTime <= record->timers[i])
+                                    record->satisfiedDependencies[i] = true;
+                                break;
+                            case kTimerDone:
+                                if (_engine->playState.timerTime > record->timers[i])
+                                    record->satisfiedDependencies[i] = true;
+                                break;
+                            case kDifficultyLevel:
+                                if (dep.condition == _engine->playState.difficulty)
+                                    record->satisfiedDependencies[i] = true;
+                                break;
+                            default:
+                                break;
+                        }
+                    }
+                }
+
+                // An orFlag marks that its corresponding dependency and the one after it
+                // mutually satisfy each other; if one is satisfied, so is the other
+                for (int i = 1; i < record->numDependencies; ++i) {
+                    if (record->orFlags[i-1]) {
+                        if (record->satisfiedDependencies[i-1])
+                            record->satisfiedDependencies[i] = true;
+                        if (record->satisfiedDependencies[i])
+                            record->satisfiedDependencies[i-1] = true;
+                    }
+                }
+
+                // Check if all dependencies have been satisfied, and activate the record if they have
+                uint satisfied = 0;
+                for (uint i = 0; i < record->numDependencies; ++i) {
+                    if (record->satisfiedDependencies[i])
+                        ++satisfied;
+                }
+
+                if (satisfied == record->numDependencies)
+                    record->isActive = true;
+            }
+        }
+
+        if (record->isActive) {
+            record->execute(_engine);
+        }
+    }
+}
+
 } // End of namespace Nancy
\ No newline at end of file
diff --git a/engines/nancy/logic.h b/engines/nancy/logic.h
index c7e65ac923..3a739977aa 100644
--- a/engines/nancy/logic.h
+++ b/engines/nancy/logic.h
@@ -43,6 +43,7 @@ public:
     virtual ~Logic() {}
 
     bool addNewActionRecord(Common::SeekableReadStream &inputData);
+    void processActionRecords();
 
 protected:
     virtual ActionRecord *createActionRecord(uint16 type);
diff --git a/engines/nancy/nancy.h b/engines/nancy/nancy.h
index d479d168f0..847758278c 100644
--- a/engines/nancy/nancy.h
+++ b/engines/nancy/nancy.h
@@ -25,6 +25,7 @@
 
 #include "nancy/console.h"
 #include "nancy/detection.h"
+#include "nancy/playstate.h"
 
 #include "engines/engine.h"
 #include "common/file.h"
@@ -114,6 +115,9 @@ public:
 	SceneManager *sceneManager;
 	GraphicsManager *graphics;
 	InputManager *input;
+	
+	// Contains all player data
+    PlayState playState;
 
 protected:
 	// Engine APIs
diff --git a/engines/nancy/playstate.h b/engines/nancy/playstate.h
index faea263714..3d00731812 100644
--- a/engines/nancy/playstate.h
+++ b/engines/nancy/playstate.h
@@ -30,13 +30,18 @@ namespace Nancy {
 // A catch-all struct for storing all player progress and related variables
 // TODO split to PlayerState/SceneState
 struct PlayState {
-    enum Flag { kFalse = 1, kTrue = 2 };
+    enum Flag { kFalse = 1, kTrue = 2 }; // Could be the other way around
     enum TimeOfDay { kDay, kNight, kDuskDawn };
 
+    // These two are used to keep track of what options the player picked in the
+    // current conversation. Since they don't get stored they probably shouldn't
+    // be here
+    Flag logicConditions[30];
+    Time logicConditionsTimestamps[30]; // Stores when the condition got satisfied
+    
     Flag inventory[11];
     Flag eventFlags[672];
-    // Second array with the same size as EventFlags that never gets used?
-    bool sceneHitCount[1000];
+    byte sceneHitCount[1000];
     uint16 difficulty; // 0, 1, 2
     Time totalTime;
     Time sceneTime;
diff --git a/engines/nancy/scene.cpp b/engines/nancy/scene.cpp
index c367faada6..557aca1f57 100644
--- a/engines/nancy/scene.cpp
+++ b/engines/nancy/scene.cpp
@@ -62,11 +62,11 @@ void SceneManager::process() {
 
 void SceneManager::init() {
     for (uint i = 0; i < 672; ++i) {
-        playState.eventFlags[i] = PlayState::Flag::kFalse;
+        _engine->playState.eventFlags[i] = PlayState::Flag::kFalse;
     }
 
     for (uint i = 0; i < 1000; ++i) {
-        playState.sceneHitCount[i] = 0;
+        _engine->playState.sceneHitCount[i] = 0;
     }
 
     _sceneID = _engine->_firstSceneID;
@@ -268,15 +268,15 @@ void SceneManager::load() {
     View &viewportDesc = _engine->graphics->viewportDesc;
 
     if (!hasLoadedFromSavefile) {
-        playState.currentMaxVerticalScroll = playState.queuedMaxVerticalScroll;
-        playState.currentViewFrame = playState.queuedViewFrame;
+        _engine->playState.currentMaxVerticalScroll = _engine->playState.queuedMaxVerticalScroll;
+        _engine->playState.currentViewFrame = _engine->playState.queuedViewFrame;
 
         if (currentScene.videoFormat == 1) {
             // TODO not sure this ever gets hit
         } else if (currentScene.videoFormat == 2) {
             // always start from the bottom
-            playState.verticalScroll = playState.currentMaxVerticalScroll;
-            playState.verticalScrollDelta = currentScene.verticalScrollDelta;
+            _engine->playState.verticalScroll = _engine->playState.currentMaxVerticalScroll;
+            _engine->playState.verticalScrollDelta = currentScene.verticalScrollDelta;
         } else {
             error("Unrecognized Scene summary chunk video file format");
         }
@@ -394,10 +394,10 @@ void SceneManager::run() {
         if (hasLoadedFromSavefile) {
             if (playTimeThisFrame > _stashedTickCount) {
                 playTimeThisFrame -= _stashedTickCount;
-                playState.totalTime -= playTimeThisFrame;
-                playState.sceneTime -= playTimeThisFrame;
-                if (playState.timerIsActive)
-                    playState.timerTime -= playTimeThisFrame;
+                _engine->playState.totalTime -= playTimeThisFrame;
+                _engine->playState.sceneTime -= playTimeThisFrame;
+                if (_engine->playState.timerIsActive)
+                    _engine->playState.timerTime -= playTimeThisFrame;
             }
         }
             
@@ -414,24 +414,24 @@ void SceneManager::run() {
         diff = playTimeThisFrame - _tickCount;
         _tickCount = playTimeThisFrame;
     }
-    playState.totalTime += diff;
-    if (playState.timerIsActive)
-        playState.timerTime += diff;
-    playState.sceneTime =+ diff;
+    _engine->playState.totalTime += diff;
+    if (_engine->playState.timerIsActive)
+        _engine->playState.timerTime += diff;
+    _engine->playState.sceneTime =+ diff;
 
     // Calculate the in-game time (playerTime)
-    if (playTimeThisFrame > playState.playerTimeNextMinute) {
-        playState.playerTime += 60000; // Add a minute
-        playState.playerTimeNextMinute = playTimeThisFrame + playerTimeMinuteLength; // Set when we're going to add the next minute
+    if (playTimeThisFrame > _engine->playState.playerTimeNextMinute) {
+        _engine->playState.playerTime += 60000; // Add a minute
+        _engine->playState.playerTimeNextMinute = playTimeThisFrame + playerTimeMinuteLength; // Set when we're going to add the next minute
     }
 
     // Set the time of day according to playerTime
-    if (playState.playerTime.getHours_alt() >= 7 && playState.playerTime.getHours_alt() < 18) {
-        playState.timeOfDay = playState.kDay;
-    } else if ((playState.playerTime.getHours_alt() >= 19 || playState.playerTime.getHours_alt() < 6)) {
-        playState.timeOfDay = playState.kNight;
+    if (_engine->playState.playerTime.getHours_alt() >= 7 && _engine->playState.playerTime.getHours_alt() < 18) {
+        _engine->playState.timeOfDay = _engine->playState.kDay;
+    } else if ((_engine->playState.playerTime.getHours_alt() >= 19 || _engine->playState.playerTime.getHours_alt() < 6)) {
+        _engine->playState.timeOfDay = _engine->playState.kNight;
     } else {
-        playState.timeOfDay = playState.kDuskDawn;
+        _engine->playState.timeOfDay = _engine->playState.kDuskDawn;
     }
 
     if (_engine->input->isClickValidLMB) {
@@ -531,67 +531,67 @@ void SceneManager::run() {
             ) {
             switch(movementDirection) {
                 case kLeft:
-                    playState.currentViewFrame += 1;
-                    if (playState.currentViewFrame >= (int16)_engine->graphics->getBackgroundFrameCount()) {
-                        playState.currentViewFrame = 0;
+                    _engine->playState.currentViewFrame += 1;
+                    if (_engine->playState.currentViewFrame >= (int16)_engine->graphics->getBackgroundFrameCount()) {
+                        _engine->playState.currentViewFrame = 0;
                     }
                     break;
                 case kRight:
-                    playState.currentViewFrame -= 1;
-                    if (playState.currentViewFrame < 0) {
-                        playState.currentViewFrame = (int16)_engine->graphics->getBackgroundFrameCount() -1;
+                    _engine->playState.currentViewFrame -= 1;
+                    if (_engine->playState.currentViewFrame < 0) {
+                        _engine->playState.currentViewFrame = (int16)_engine->graphics->getBackgroundFrameCount() -1;
                     }
                     break;
                 case kUp:
-                    if (playState.verticalScroll != 0) {
-                        int16 newScroll = playState.verticalScroll - playState.verticalScrollDelta;
-                        playState.verticalScroll = MAX(newScroll, (int16)0);
+                    if (_engine->playState.verticalScroll != 0) {
+                        int16 newScroll = _engine->playState.verticalScroll - _engine->playState.verticalScrollDelta;
+                        _engine->playState.verticalScroll = MAX(newScroll, (int16)0);
                     }
                     break;
                 case kDown:
-                    if (playState.verticalScroll != playState.currentMaxVerticalScroll) {
-                        uint16 newScroll = playState.verticalScroll + playState.verticalScrollDelta;
-                        playState.verticalScroll = MIN(newScroll, playState.currentMaxVerticalScroll);
+                    if (_engine->playState.verticalScroll != _engine->playState.currentMaxVerticalScroll) {
+                        uint16 newScroll = _engine->playState.verticalScroll + _engine->playState.verticalScrollDelta;
+                        _engine->playState.verticalScroll = MIN(newScroll, _engine->playState.currentMaxVerticalScroll);
                     }
                     break;
                 case kUp | kLeft:
-                    if (playState.verticalScroll != 0) {
-                        int16 newScroll = playState.verticalScroll - playState.verticalScrollDelta;
-                        playState.verticalScroll = MAX(newScroll, (int16)0);
+                    if (_engine->playState.verticalScroll != 0) {
+                        int16 newScroll = _engine->playState.verticalScroll - _engine->playState.verticalScrollDelta;
+                        _engine->playState.verticalScroll = MAX(newScroll, (int16)0);
                     }
-                    playState.currentViewFrame += 1;
-                    if (playState.currentViewFrame >= (int16)_engine->graphics->getBackgroundFrameCount()) {
-                        playState.currentViewFrame = 0;
+                    _engine->playState.currentViewFrame += 1;
+                    if (_engine->playState.currentViewFrame >= (int16)_engine->graphics->getBackgroundFrameCount()) {
+                        _engine->playState.currentViewFrame = 0;
                     }
                     break;
                 case kUp | kRight:
-                    if (playState.verticalScroll != 0) {
-                        int16 newScroll = playState.verticalScroll - playState.verticalScrollDelta;
-                        playState.verticalScroll = MAX(newScroll, (int16)0);
+                    if (_engine->playState.verticalScroll != 0) {
+                        int16 newScroll = _engine->playState.verticalScroll - _engine->playState.verticalScrollDelta;
+                        _engine->playState.verticalScroll = MAX(newScroll, (int16)0);
                     }
-                    playState.currentViewFrame -= 1;
-                    if (playState.currentViewFrame < 0) {
-                        playState.currentViewFrame = _engine->graphics->getBackgroundFrameCount() - 1;
+                    _engine->playState.currentViewFrame -= 1;
+                    if (_engine->playState.currentViewFrame < 0) {
+                        _engine->playState.currentViewFrame = _engine->graphics->getBackgroundFrameCount() - 1;
                     }
                     break;
                 case kDown | kLeft:
-                    if (playState.verticalScroll != playState.currentMaxVerticalScroll) {
-                        uint16 newScroll = playState.verticalScroll + playState.verticalScrollDelta;
-                        playState.verticalScroll = MIN(newScroll, playState.currentMaxVerticalScroll);
+                    if (_engine->playState.verticalScroll != _engine->playState.currentMaxVerticalScroll) {
+                        uint16 newScroll = _engine->playState.verticalScroll + _engine->playState.verticalScrollDelta;
+                        _engine->playState.verticalScroll = MIN(newScroll, _engine->playState.currentMaxVerticalScroll);
                     }
-                    playState.currentViewFrame += 1;
-                    if (playState.currentViewFrame >= (int16)_engine->graphics->getBackgroundFrameCount()) {
-                        playState.currentViewFrame = 0;
+                    _engine->playState.currentViewFrame += 1;
+                    if (_engine->playState.currentViewFrame >= (int16)_engine->graphics->getBackgroundFrameCount()) {
+                        _engine->playState.currentViewFrame = 0;
                     }
                     break;
                 case kDown | kRight:
-                    if (playState.verticalScroll != playState.currentMaxVerticalScroll) {
-                        uint16 newScroll = playState.verticalScroll + playState.verticalScrollDelta;
-                        playState.verticalScroll = MIN(newScroll, playState.currentMaxVerticalScroll);
+                    if (_engine->playState.verticalScroll != _engine->playState.currentMaxVerticalScroll) {
+                        uint16 newScroll = _engine->playState.verticalScroll + _engine->playState.verticalScrollDelta;
+                        _engine->playState.verticalScroll = MIN(newScroll, _engine->playState.currentMaxVerticalScroll);
                     }
-                    playState.currentViewFrame -= 1;
-                    if (playState.currentViewFrame < 0) {
-                        playState.currentViewFrame = _engine->graphics->getBackgroundFrameCount() -1;
+                    _engine->playState.currentViewFrame -= 1;
+                    if (_engine->playState.currentViewFrame < 0) {
+                        _engine->playState.currentViewFrame = _engine->graphics->getBackgroundFrameCount() -1;
                     }
                     break;
             }
@@ -606,22 +606,22 @@ void SceneManager::run() {
     }
 
     // Redraw the Background surface if we've moved
-    if (playState.currentViewFrame != playState.lastDrawnViewFrame) {
+    if (_engine->playState.currentViewFrame != _engine->playState.lastDrawnViewFrame) {
         if (currentScene.videoFormat == 1) {
             // TODO if it ever gets hit
         } else if (currentScene.videoFormat == 2) {
             _engine->graphics->_background.copyRectToSurface(
-            *_engine->graphics->getBackgroundFrame(playState.currentViewFrame),
+            *_engine->graphics->getBackgroundFrame(_engine->playState.currentViewFrame),
             0, 0,
-            Common::Rect(0, playState.verticalScroll, _engine->graphics->getBackgroundWidth() - 1,
-                playState.verticalScroll + _engine->graphics->viewportDesc.srcBottom));
+            Common::Rect(0, _engine->playState.verticalScroll, _engine->graphics->getBackgroundWidth() - 1,
+                _engine->playState.verticalScroll + _engine->graphics->viewportDesc.srcBottom));
         }
         // TODO some if related to PlaySoundPanFrameAnchorAndDie
-        playState.lastDrawnViewFrame = playState.currentViewFrame;
+        _engine->playState.lastDrawnViewFrame = _engine->playState.currentViewFrame;
         // TODO function call that sets a val to 1 and 3 others to 0
     }
 
-    //_engine->logic->processActionRecords();
+    _engine->logic->processActionRecords();
 
     // code that skips rendering for the first 12 frames??? why
 
diff --git a/engines/nancy/scene.h b/engines/nancy/scene.h
index ff96c12f60..e752b30c62 100644
--- a/engines/nancy/scene.h
+++ b/engines/nancy/scene.h
@@ -70,7 +70,6 @@ private:
     };
 
 public:
-    PlayState playState;
     int32 playerTimeMinuteLength;
     byte movementDirection;
 


Commit: c1a6f38271eaec13541dd21706bb348be145c68a
    https://github.com/scummvm/scummvm/commit/c1a6f38271eaec13541dd21706bb348be145c68a
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add Cursors structure

Implement the struct stored in the CURS chunk. Contains the coordinates of every cursor bitmap and an X and Y coordinate used when playing a primary video sequence.

Changed paths:
    engines/nancy/datatypes.cpp
    engines/nancy/datatypes.h
    engines/nancy/input.h
    engines/nancy/nancy.cpp


diff --git a/engines/nancy/datatypes.cpp b/engines/nancy/datatypes.cpp
index 2e81b6b6ed..82ff205313 100644
--- a/engines/nancy/datatypes.cpp
+++ b/engines/nancy/datatypes.cpp
@@ -62,6 +62,7 @@ SceneSummary::SceneSummary(Common::SeekableReadStream *stream) {
     delete[] buf;
 }
 
+// Takes a VIEW chunk as input
 View::View(Common::SeekableReadStream *stream) {
     stream->seek(0);
     destLeft = stream->readUint32LE();
@@ -82,4 +83,18 @@ View::View(Common::SeekableReadStream *stream) {
     f2Bottom = stream->readUint32LE();
 }
 
+// Takes a CURS chunk as input
+Cursors::Cursors(Common::SeekableReadStream *stream) {
+    stream->seek(0);
+    for (uint i = 0; i < 85; ++i) {
+        Common::Rect &rect = rects[i];
+        rect.left = stream->readUint32LE();
+        rect.top = stream->readUint32LE();
+        rect.right = stream->readUint32LE();
+        rect.bottom = stream->readUint32LE();
+    }
+    primaryVideoCursorX = stream->readUint16LE();
+    primaryVideoCursorY = stream->readUint16LE();
+}
+
 } // End of namespace Nancy
\ No newline at end of file
diff --git a/engines/nancy/datatypes.h b/engines/nancy/datatypes.h
index 045aa1fa00..cfc0203e13 100644
--- a/engines/nancy/datatypes.h
+++ b/engines/nancy/datatypes.h
@@ -24,6 +24,7 @@
 #define NANCY_DATATYPES_H
 
 #include "common/str.h"
+#include "common/rect.h"
 
 namespace Common {
 class SeekableReadStream;
@@ -81,6 +82,16 @@ struct View {
     uint32 f2Bottom;        // 0x3C
 };
 
+// Holds the coordinates for the bitmaps of all cursors
+struct Cursors {
+    Cursors() =default;
+    Cursors(Common::SeekableReadStream *stream);
+    Common::Rect rects[85];
+    // The cursor gets set to this location at some point during PrimaryVideoSequence 
+    uint16 primaryVideoCursorX;
+    uint16 primaryVideoCursorY;
+};
+
 } // End of namespace Nancy
 
 #endif // NANCY_DATATYPES_H
\ No newline at end of file
diff --git a/engines/nancy/input.h b/engines/nancy/input.h
index ee0468edb3..7adea0c877 100644
--- a/engines/nancy/input.h
+++ b/engines/nancy/input.h
@@ -23,7 +23,10 @@
 #ifndef NANCY_INPUT_H
 #define NANCY_INPUT_H
 
+#include "engines/nancy/datatypes.h"
+
 #include "common/rect.h"
+
 namespace Common {
 template <class T>
 class Array;
@@ -77,6 +80,7 @@ enum InputType : byte {
     bool isClickValidLMB;
     bool isClickValidRMB;
     int16 hoveredElementID;
+    Cursors cursorsData;
     
     // TODO consider using a namespace for these
     static const int16 mapButtonID;
diff --git a/engines/nancy/nancy.cpp b/engines/nancy/nancy.cpp
index f851cb6bfa..398aea95e6 100644
--- a/engines/nancy/nancy.cpp
+++ b/engines/nancy/nancy.cpp
@@ -202,6 +202,8 @@ void NancyEngine::bootGameEngine() {
 		addBootChunk(n, boot->getChunkStream(n));
 	}
 
+	input->cursorsData = Cursors(getBootChunkStream("CURS"));
+
 	// The FR, LG and OB chunks get added here	
 
 	Common::SeekableReadStream *font = getBootChunkStream("FONT");


Commit: b79d9f1a322addbc0ac56d8f73582e6ce6d6cb62
    https://github.com/scummvm/scummvm/commit/b79d9f1a322addbc0ac56d8f73582e6ce6d6cb62
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Rendering code refactor

Several small changes to the rendering code that reduce code complexity, most notably the removal of the game's dirty rectangle optimizations.

Changed paths:
    engines/nancy/graphics.cpp
    engines/nancy/graphics.h
    engines/nancy/nancy.cpp
    engines/nancy/scene.cpp


diff --git a/engines/nancy/graphics.cpp b/engines/nancy/graphics.cpp
index b4973b5cc0..f342d4f077 100644
--- a/engines/nancy/graphics.cpp
+++ b/engines/nancy/graphics.cpp
@@ -67,81 +67,171 @@ GraphicsManager::~GraphicsManager() {
     _screen.free();
 
     for (auto st : _ZRender) {
-        delete st.sourceRect;
-        delete st.destPoint;
-        delete st.renderFunction;
+        delete st._value.renderFunction;
     }
 }
 
-void GraphicsManager::clearGenericZRenderStruct(uint id) {
-    ZRenderStruct *st = getGenericZRenderStruct(id);
-    st->isActive = false;
-    st->isInitialized = false;
+void GraphicsManager::clearZRenderStruct(Common::String name) {
+    _ZRender.erase(name);
 }
 
-ZRenderStruct *GraphicsManager::getGenericZRenderStruct(uint id) {
-    if (id > 60)
-        error("Bad ZRender ID!");
+void GraphicsManager::clearZRenderStructs() {
+    _ZRender.clear();
+}
+
+ZRenderStruct &GraphicsManager::getZRenderStruct(Common::String name) {
+    // Creates a new struct if one didn't exist before
+    return _ZRender[name];
+}
+
+void GraphicsManager::initZRenderStruct(char const *name,
+                                        uint32 z,
+                                        bool isActive,
+                                        ZRenderStruct::BltType bltType,
+                                        Graphics::Surface *surface,
+                                        RenderFunction *func,
+                                        Common::Rect *sourceRect,
+                                        Common::Rect *destRect ) {
+    clearZRenderStruct(name);
+    ZRenderStruct &st = getZRenderStruct(name);
+    st.name = name;
+    st.z = z;
+    st.isActive = isActive;
+    st.isInitialized = true;
+    st.bltType = bltType;
+    st.sourceSurface = surface;
+    if (sourceRect)
+        st.sourceRect = *sourceRect;
+    else st.sourceRect = Common::Rect();
+    if (destRect)
+        st.destRect = *destRect;
+    else st.destRect = Common::Rect();
+    st.renderFunction = func;
+}
+
+// TODO nancy1 only, move to subclass whenever we support multiple games
+// TODO most of these are wrong and/or incomplete
+// The original engine uses dirty rectangles for optimization and marks
+// their location with zrender structs whose names start with RES.
+// I'm using a more naive implementation where everything is redrawn every frame
+// for code simplicity, but that can be changed in the future if needed
+void GraphicsManager::initSceneZRenderStructs() {
+    Common::Rect *source = new Common::Rect();
+    Common::Rect *dest = new Common::Rect();
+    Common::SeekableReadStream *chunk = nullptr;
     
-    return &_ZRender[id];
-}
-
-void GraphicsManager::initGenericZRenderStruct(uint id, char const *name, uint32 z, bool isActive, ZRenderStruct::BltType bltType, Graphics::Surface *surface, RenderFunction *func, Common::Rect *sourceRect, Common::Point *destPoint) {
-    clearGenericZRenderStruct(id);
-    ZRenderStruct *st = getGenericZRenderStruct(id);
-    delete st->sourceRect;
-    delete st->destPoint;
-    delete st->renderFunction;
-    st->name = name;
-    st->z = z;
-    st->isActive = isActive;
-    st->isInitialized = true;
-    st->bltType = bltType;
-    st->sourceSurface = surface;
-    st->sourceRect = sourceRect;
-    st->destPoint = destPoint;
-    st->renderFunction = func;
-}
-
-void GraphicsManager::renderDisplay(uint last) {
-    int *mask = new int[last+2];
-    for (uint i = 0; i <= last; ++i) {
-        mask[i] = i;
-    }
-    // set final member to -1
-    mask[last+1] = -1;
-    renderDisplay(mask);
-    delete[] mask;
+    #define READ_RECT(where, x) chunk->seek(x); \
+                                where->left = chunk->readUint32LE(); \
+                                where->top = chunk->readUint32LE(); \
+                                where->right = chunk->readUint32LE(); \
+                                where->bottom = chunk->readUint32LE();
+
+    chunk = _engine->getBootChunkStream("MENU");
+    READ_RECT(source, 16)
+    initZRenderStruct(  "FRAME", 1, true, ZRenderStruct::BltType::kNone, &_primaryFrameSurface,
+                        new RenderFunction(this, &GraphicsManager::renderFrame), source, source);
+    initZRenderStruct(  "CUR IMAGE CURSOR", 11, false, ZRenderStruct::BltType::kTrans, &_object0Surface);
+
+    chunk = _engine->getBootChunkStream("TBOX");
+    READ_RECT(source, 0)
+    initZRenderStruct(  "CUR TB BAT SLIDER", 9, false, ZRenderStruct::BltType::kTrans,
+                        &_object0Surface, nullptr, source, nullptr);
+
+    chunk = _engine->getBootChunkStream("BSUM");
+    READ_RECT(dest, 356)
+    initZRenderStruct(  "FRAME TB SURF", 6, false, ZRenderStruct::BltType::kNoTrans,
+                        &_frameTextBox, nullptr, nullptr, dest);
+
+    READ_RECT(source, 388)
+    READ_RECT(dest, 420)
+    initZRenderStruct(  "MENU BUT DN", 5, false, ZRenderStruct::BltType::kTrans,
+                        &_object0Surface, nullptr, source, dest);
+
+    READ_RECT(source, 404)
+    READ_RECT(dest, 436)
+    initZRenderStruct(  "HELP BUT DN", 5, false, ZRenderStruct::BltType::kTrans,
+                        &_object0Surface, nullptr, source, dest);
+
+    chunk = _engine->getBootChunkStream("INV");
+    READ_RECT(source, 0)
+    initZRenderStruct(  "CUR INV SLIDER", 9, false, ZRenderStruct::BltType::kTrans,
+                         &_object0Surface, nullptr, source, nullptr);
+
+    initZRenderStruct(  "FRAME INV BOX", 6, false, ZRenderStruct::BltType::kNoTrans, nullptr,
+                        new RenderFunction(this, &GraphicsManager::renderFrameInvBox));
+    
+    initZRenderStruct(  "INV BITMAP", 9, false, ZRenderStruct::BltType::kNoTrans);
+    initZRenderStruct(  "PRIMARY VIDEO", 8, false, ZRenderStruct::BltType::kNoTrans, nullptr,
+                        new RenderFunction(this, &GraphicsManager::renderPrimaryVideo));
+    initZRenderStruct(  "SEC VIDEO 0", 8, false, ZRenderStruct::BltType::kTrans, nullptr,
+                        new RenderFunction(this, &GraphicsManager::renderSecVideo0));
+    initZRenderStruct(  "SEC VIDEO 1", 8, false, ZRenderStruct::BltType::kTrans, nullptr,
+                        new RenderFunction(this, &GraphicsManager::renderSecVideo1));
+    initZRenderStruct(  "SEC MOVIE", 8, false, ZRenderStruct::BltType::kNoTrans, nullptr,
+                        new RenderFunction(this, &GraphicsManager::renderSecMovie));
+    initZRenderStruct(  "ORDERING PUZZLE", 7, false, ZRenderStruct::BltType::kNoTrans, nullptr,
+                        new RenderFunction(this, &GraphicsManager::renderOrderingPuzzle));
+    initZRenderStruct(  "ROTATING LOCK PUZZLE", 7, false, ZRenderStruct::BltType::kNoTrans, nullptr,
+                        new RenderFunction(this, &GraphicsManager::renderRotatingLockPuzzle));
+    initZRenderStruct(  "LEVER PUZZLE", 7, false, ZRenderStruct::BltType::kNoTrans, nullptr,
+                        new RenderFunction(this, &GraphicsManager::renderLeverPuzzle));
+    initZRenderStruct(  "TELEPHONE", 7, false, ZRenderStruct::BltType::kNoTrans, nullptr,
+                        new RenderFunction(this, &GraphicsManager::renderTelephone));
+    initZRenderStruct(  "SLIDER PUZZLE", 7, false, ZRenderStruct::BltType::kNoTrans, nullptr,
+                        new RenderFunction(this, &GraphicsManager::renderSliderPuzzle));
+    initZRenderStruct(  "PASSWORD PUZZLE", 7, false, ZRenderStruct::BltType::kNoTrans, nullptr,
+                        new RenderFunction(this, &GraphicsManager::renderPasswordPuzzle));
+
+    // Moved here from SceneManager::load(), should be ok
+    *source = Common::Rect(viewportDesc.srcLeft, viewportDesc.srcTop, viewportDesc.srcRight, viewportDesc.srcBottom);
+    *dest = Common::Rect(viewportDesc.destLeft, viewportDesc.destTop, viewportDesc.destRight, viewportDesc.destBottom);
+    initZRenderStruct(  "VIEWPORT AVF", 6, true, ZRenderStruct::BltType::kNoTrans,
+                        &_background, nullptr, source, dest);
+    #undef READ_RECT
+
+    delete source;
+    delete dest;
 }
 
-void GraphicsManager::renderDisplay(int *idMask) {
-    char hasBeenRendered[60]{};
-    if (!idMask)
-        error("Bad ZRender ID Mask!");
+void GraphicsManager::renderDisplay() {
+    // Construct a list containing every struct and pass it along
+    Common::Array<Common::String> array;
+    for (auto i : _ZRender) {
+        array.push_back(i._key);
+    }
+
+    renderDisplay(array);
+}
 
+void GraphicsManager::renderDisplay(Common::Array<Common::String> ids) {
     for (uint currentZ = _startingZ; currentZ < 12; ++currentZ) {
-        for (uint i = 0; idMask[i] != -1; ++i) {
-            int maskCur = idMask[i];
-            ZRenderStruct &current = _ZRender[maskCur];
-            if (!hasBeenRendered[i] && current.isActive && current.isInitialized && current.z == currentZ) {
+        for (uint i = 0; i < ids.size(); ++i) {
+            ZRenderStruct &current = getZRenderStruct(ids[i]);
+            if (current.isActive && current.isInitialized && current.z == currentZ) {
                 if (current.renderFunction && current.renderFunction->isValid()) {
                     current.renderFunction->operator()();
                 }
                 else {
                     switch (current.bltType) {
                         // making some assumptions here
-                        case ZRenderStruct::BltType::kNoTrans:
-                            _screen.blitFrom(*current.sourceSurface, *current.sourceRect, *current.destPoint);
+                        case ZRenderStruct::BltType::kNoTrans: {
+                            Common::Point dest(current.destRect.left, current.destRect.top);
+                            _screen.blitFrom(*current.sourceSurface, current.sourceRect, dest);
                             break;
+                        }
                         case ZRenderStruct::BltType::kTrans: {
-                            
-                            _screen.transBlitFrom(*current.sourceSurface, *current.sourceRect, *current.destPoint, transColor);
-                            break; }
+                            Common::Point dest(current.destRect.left, current.destRect.top);
+                            _screen.transBlitFrom(*current.sourceSurface, current.sourceRect, dest, transColor);
+                            break;
+                        }
                         default:
                             error("Bad ZRender Blt type!");
                     }
                 }
-                hasBeenRendered[i] = 1;
+
+                // Current struct has been rendered, remove from list
+                ids.remove_at(i);
+                break;
             }
         }
     }
@@ -176,9 +266,9 @@ uint32 GraphicsManager::getBackgroundHeight() {
 }
 
 void GraphicsManager::renderFrame() {
-    Graphics::Surface n = *_ZRender[0].sourceSurface->convertTo(pixelFormat);
-    _screen.blitFrom(n, *_ZRender[0].sourceRect, *_ZRender[0].destPoint);
-    n.free();
+    ZRenderStruct &zr = getZRenderStruct("FRAME");
+    Common::Point dest(zr.destRect.left, zr.destRect.top);
+    _screen.blitFrom(*zr.sourceSurface, zr.sourceRect, dest);
 
     // not sure why we do this
     _numTimesRenderedFrame += 1;
@@ -190,12 +280,6 @@ void GraphicsManager::renderFrame() {
     }
 }
 
-void GraphicsManager::renderResTBBatSlider() {
-    // not sure why this is its own function
-    ZRenderStruct &st = _ZRender[10];
-    _screen.blitFrom(*st.sourceSurface, *st.sourceRect, *st.destPoint);
-}
-
 void GraphicsManager::renderFrameInvBox() {
     // TODO
 }
diff --git a/engines/nancy/graphics.h b/engines/nancy/graphics.h
index 85d3280789..00fd1338c8 100644
--- a/engines/nancy/graphics.h
+++ b/engines/nancy/graphics.h
@@ -42,8 +42,8 @@ public:
     uint32 z = 0;
     RenderFunction *renderFunction = nullptr;
     Graphics::Surface *sourceSurface = nullptr;
-    Common::Rect *sourceRect = nullptr;
-    Common::Point *destPoint = nullptr;
+    Common::Rect sourceRect;
+    Common::Rect destRect;
     bool isActive = false;
     bool isInitialized = false;
     BltType bltType = kNone;
@@ -53,16 +53,26 @@ public:
 class GraphicsManager {
 public:
     GraphicsManager(NancyEngine *engine);
-    ~GraphicsManager();
+    virtual ~GraphicsManager();
 
     void init();
 
-    void clearGenericZRenderStruct(uint id);
-    ZRenderStruct *getGenericZRenderStruct(uint id);
-    void initGenericZRenderStruct(uint id, char const *name, uint32 z, bool isActive, ZRenderStruct::BltType bltType, Graphics::Surface *surface = nullptr, RenderFunction *func = nullptr, Common::Rect *sourceRect = nullptr, Common::Point *destPoint = nullptr);
+    void clearZRenderStruct(Common::String name);
+    void clearZRenderStructs();
+    ZRenderStruct &getZRenderStruct(Common::String name);
+    void initZRenderStruct( char const *name,
+                            uint32 z,
+                            bool isActive,
+                            ZRenderStruct::BltType bltType,
+                            Graphics::Surface *surface = nullptr,
+                            RenderFunction *func = nullptr,
+                            Common::Rect *sourceRect = nullptr,
+                            Common::Rect *destRect = nullptr );
 
-    void renderDisplay(uint last);
-    void renderDisplay(int *idMask);
+    virtual void initSceneZRenderStructs();
+
+    void renderDisplay();
+    void renderDisplay(Common::Array<Common::String> ids);
 
     void loadBackgroundVideo(const Common::String &filename);
     const Graphics::Surface *getBackgroundFrame(uint16 frameId);
@@ -84,7 +94,7 @@ public:
 
 private:
     NancyEngine *_engine;
-    ZRenderStruct _ZRender[60];
+    Common::HashMap<Common::String, ZRenderStruct> _ZRender;
     Graphics::Screen _screen;
     AVFDecoder _videoDecoder;
 
@@ -94,7 +104,6 @@ private:
 public:
     // custom render functions
     void renderFrame();
-    void renderResTBBatSlider();
     void renderFrameInvBox();
     void renderPrimaryVideo();
     void renderSecVideo0();
diff --git a/engines/nancy/nancy.cpp b/engines/nancy/nancy.cpp
index 398aea95e6..27a127ca78 100644
--- a/engines/nancy/nancy.cpp
+++ b/engines/nancy/nancy.cpp
@@ -214,9 +214,7 @@ void NancyEngine::bootGameEngine() {
 	// TODO another loop that does the same thing but with CURS
 	// TODO reset some vars
 
-	for (uint i = 0; i <= 60; ++i) {
-		graphics->clearGenericZRenderStruct(i);
-	}
+	graphics->clearZRenderStructs();
 
 	// TODO reset some more vars
 
diff --git a/engines/nancy/scene.cpp b/engines/nancy/scene.cpp
index 557aca1f57..3494d3d2e3 100644
--- a/engines/nancy/scene.cpp
+++ b/engines/nancy/scene.cpp
@@ -74,9 +74,7 @@ void SceneManager::init() {
 
     // TODO init action records
     
-    for (uint i = 0; i <= 60; ++i) {
-		_engine->graphics->clearGenericZRenderStruct(i);
-	}
+    _engine->graphics->clearZRenderStructs();
 
     // Load the primary frame
     if (!_engine->_res->loadImage("ciftree", _engine->_frames[0].name, _engine->graphics->_primaryFrameSurface)) {
@@ -106,119 +104,7 @@ void SceneManager::init() {
 
     delete[] name;
 
-    // TODO init various rects for the ZRender structs
-
-    // Init the ZRender structs themselves:
-    // TODO most of these are wrong and/or incomplete
-    #define READ_SOURCE_RECT(x) chunk->seek(x); source = new Common::Rect(chunk->readUint32LE(), chunk->readUint32LE(), chunk->readUint32LE(), chunk->readUint32LE());
-    #define READ_DEST_POINT(x) chunk->seek(x); dest = new Common::Point(chunk->readUint32LE(), chunk->readUint32LE());
-
-    Common::SeekableReadStream *chunk;
-    Common::Rect *source;
-    Common::Point *dest;
-
-    // CUR: current
-    // RES: redraws the background of a moved element so it doesnt get doubled
-    // VP: viewport
-
-    chunk = _engine->getBootChunkStream("MENU");
-    READ_SOURCE_RECT(16)
-    READ_DEST_POINT(16)
-    _engine->graphics->initGenericZRenderStruct(0, "FRAME", 1, true, ZRenderStruct::BltType::kNone, &_engine->graphics->_primaryFrameSurface,
-                                                new RenderFunction(_engine->graphics, &GraphicsManager::renderFrame),
-                                                source, dest);
-    _engine->graphics->initGenericZRenderStruct(2, "CUR IMAGE CURSOR", 11, false, ZRenderStruct::BltType::kTrans,
-                                                &_engine->graphics->_object0Surface, nullptr, new Common::Rect(), new Common::Point());
-    _engine->graphics->initGenericZRenderStruct(3, "RES IMAGE CURSOR - FRAME", 2, false, ZRenderStruct::BltType::kNoTrans,
-                                                &_engine->graphics->_primaryFrameSurface, nullptr, new Common::Rect(), new Common::Point());
-    _engine->graphics->initGenericZRenderStruct(4, "RES IMAGE CURSOR - VP", 3, false, ZRenderStruct::BltType::kNoTrans,
-                                                &_engine->graphics->_background, nullptr, new Common::Rect(), new Common::Point());
-    // Skip DIAGNOSTICS and VERSION
-
-    chunk = _engine->getBootChunkStream("TBOX");
-    READ_SOURCE_RECT(0)
-    _engine->graphics->initGenericZRenderStruct(9, "CUR TB BAT SLIDER", 9, false, ZRenderStruct::BltType::kTrans,
-                                                &_engine->graphics->_object0Surface, nullptr, source, new Common::Point());           
-
-    _engine->graphics->initGenericZRenderStruct(10, "RES TB BAT SLIDER", 3, false, ZRenderStruct::BltType::kNoTrans, &_engine->graphics->_primaryFrameSurface,
-                                               new RenderFunction(_engine->graphics, &GraphicsManager::renderResTBBatSlider),
-                                               new Common::Rect(), new Common::Point());
-
-    chunk = _engine->getBootChunkStream("BSUM");
-    READ_DEST_POINT(356)
-    _engine->graphics->initGenericZRenderStruct(8, "FRAME TB SURF", 6, false, ZRenderStruct::BltType::kNoTrans,
-                                                &_engine->graphics->_frameTextBox, nullptr, source, dest);
-
-    READ_SOURCE_RECT(356)
-    READ_DEST_POINT(356)
-    _engine->graphics->initGenericZRenderStruct(11, "TB FRAME RES", 2, false, ZRenderStruct::BltType::kNoTrans,
-                                                &_engine->graphics->_primaryFrameSurface, nullptr, source, dest);  
-
-    READ_SOURCE_RECT(388)
-    READ_DEST_POINT(420)
-    _engine->graphics->initGenericZRenderStruct(12, "MENU BUT DN", 5, false, ZRenderStruct::BltType::kTrans,
-                                                &_engine->graphics->_object0Surface, nullptr, source, dest);
-    READ_SOURCE_RECT(404)
-    READ_DEST_POINT(436)
-    _engine->graphics->initGenericZRenderStruct(13, "HELP BUT DN", 5, false, ZRenderStruct::BltType::kTrans,
-                                                &_engine->graphics->_object0Surface, nullptr, source, dest);
-    
-    chunk = _engine->getBootChunkStream("INV");
-    READ_SOURCE_RECT(0)
-    _engine->graphics->initGenericZRenderStruct(14, "CUR INV SLIDER", 9, false, ZRenderStruct::BltType::kTrans,
-                                                &_engine->graphics->_object0Surface, nullptr, source, new Common::Point());
-
-    _engine->graphics->initGenericZRenderStruct(15, "RES INV SLIDER", 3, false, ZRenderStruct::BltType::kNoTrans,
-                                                &_engine->graphics->_primaryFrameSurface, nullptr, new Common::Rect(), new Common::Point());
-
-    _engine->graphics->initGenericZRenderStruct(16, "FRAME INV BOX", 6, false, ZRenderStruct::BltType::kNoTrans, nullptr,
-                                                new RenderFunction(_engine->graphics, &GraphicsManager::renderFrameInvBox),
-                                                nullptr, nullptr);
-
-    _engine->graphics->initGenericZRenderStruct(5, "INV BITMAP", 9, false, ZRenderStruct::BltType::kNoTrans,
-                                                nullptr, nullptr, new Common::Rect(), new Common::Point());
-
-    _engine->graphics->initGenericZRenderStruct(17, "PRIMARY VIDEO", 8, false, ZRenderStruct::BltType::kNoTrans, nullptr,
-                                                new RenderFunction(_engine->graphics, &GraphicsManager::renderPrimaryVideo),
-                                                new Common::Rect(), new Common::Point());
-
-    _engine->graphics->initGenericZRenderStruct(18, "SEC VIDEO 0", 8, false, ZRenderStruct::BltType::kTrans, nullptr,
-                                                new RenderFunction(_engine->graphics, &GraphicsManager::renderSecVideo0),
-                                                new Common::Rect(), new Common::Point());
-
-    _engine->graphics->initGenericZRenderStruct(19, "SEC VIDEO 1", 8, false, ZRenderStruct::BltType::kTrans, nullptr,
-                                                new RenderFunction(_engine->graphics, &GraphicsManager::renderSecVideo1),
-                                                new Common::Rect(), new Common::Point());
-    
-    _engine->graphics->initGenericZRenderStruct(20, "SEC MOVIE", 8, false, ZRenderStruct::BltType::kNoTrans, nullptr,
-                                                new RenderFunction(_engine->graphics, &GraphicsManager::renderSecMovie),
-                                                new Common::Rect(), new Common::Point());
-    
-    _engine->graphics->initGenericZRenderStruct(21, "ORDERING PUZZLE", 7, false, ZRenderStruct::BltType::kNoTrans, nullptr,
-                                                new RenderFunction(_engine->graphics, &GraphicsManager::renderOrderingPuzzle),
-                                                nullptr);
-    
-    _engine->graphics->initGenericZRenderStruct(22, "ROTATING LOCK PUZZLE", 7, false, ZRenderStruct::BltType::kNoTrans, nullptr,
-                                                new RenderFunction(_engine->graphics, &GraphicsManager::renderRotatingLockPuzzle),
-                                                nullptr, nullptr);
-
-    _engine->graphics->initGenericZRenderStruct(23, "LEVER PUZZLE", 7, false, ZRenderStruct::BltType::kNoTrans, nullptr,
-                                                new RenderFunction(_engine->graphics, &GraphicsManager::renderLeverPuzzle),
-                                                nullptr, nullptr);
-
-    _engine->graphics->initGenericZRenderStruct(24, "TELEPHONE", 7, false, ZRenderStruct::BltType::kNoTrans, nullptr,
-                                                new RenderFunction(_engine->graphics, &GraphicsManager::renderTelephone),
-                                                nullptr, nullptr);
-    
-    _engine->graphics->initGenericZRenderStruct(25, "SLIDER PUZZLE", 7, false, ZRenderStruct::BltType::kNoTrans, nullptr,
-                                                new RenderFunction(_engine->graphics, &GraphicsManager::renderSliderPuzzle),
-                                                nullptr, nullptr);
-    
-    _engine->graphics->initGenericZRenderStruct(26, "PASSWORD PUZZLE", 7, false, ZRenderStruct::BltType::kNoTrans, nullptr,
-                                                new RenderFunction(_engine->graphics, &GraphicsManager::renderPasswordPuzzle),
-                                                nullptr, nullptr);
-    #undef READ_SOURCE_RECT
-    #undef READ_DEST_POINT
+    _engine->graphics->initSceneZRenderStructs();
 
     _state = kLoad;
 }
@@ -294,11 +180,6 @@ void SceneManager::load() {
 
     delete sceneSummaryChunk;
 
-    Common::Point *dest = new Common::Point(viewportDesc.destLeft, viewportDesc.destTop);
-    Common::Rect *source = new Common::Rect(viewportDesc.srcLeft, viewportDesc.srcTop, viewportDesc.srcRight, viewportDesc.srcBottom);
-   _engine->graphics->initGenericZRenderStruct(2, "VIEWPORT AVF", 6, true, ZRenderStruct::BltType::kNoTrans,
-                                                &_engine->graphics->_background, nullptr, source, dest);
-
     _state = kRun; // TODO temp, is actually StartSound
 }
 
@@ -309,17 +190,17 @@ void SceneManager::run() {
     isComingFromMenu = false;
 
     if (orderingPuzzleIsActive)
-        _engine->graphics->getGenericZRenderStruct(21)->isActive = true;
+        _engine->graphics->getZRenderStruct("ORDERING PUZZLE").isActive = true;
     if (rotatingLockPuzzleIsActive)
-        _engine->graphics->getGenericZRenderStruct(22)->isActive = true;
+        _engine->graphics->getZRenderStruct("ROTATING LOCK PUZZLE").isActive = true;
     if (leverPuzzleIsActive)
-        _engine->graphics->getGenericZRenderStruct(23)->isActive = true;
+        _engine->graphics->getZRenderStruct("LEVER PUZZLE").isActive = true;
     if (sliderPuzzleIsActive)
-        _engine->graphics->getGenericZRenderStruct(25)->isActive = true;
+        _engine->graphics->getZRenderStruct("SLIDER PUZZLE").isActive = true;
     if (passwordPuzzleIsActive)
-        _engine->graphics->getGenericZRenderStruct(26)->isActive = true;
+        _engine->graphics->getZRenderStruct("PASSWORD PUZZLE").isActive = true;
     if (telephoneIsActive)
-        _engine->graphics->getGenericZRenderStruct(24)->isActive = true;
+        _engine->graphics->getZRenderStruct("TELEPHONE").isActive = true;
 
     if (helpMenuRequested) {
         _stashedTickCount = _engine->getTotalPlayTime();
@@ -401,8 +282,8 @@ void SceneManager::run() {
             }
         }
             
-        _engine->graphics->getGenericZRenderStruct(12)->isActive = false; // MENU BTN DN
-        _engine->graphics->getGenericZRenderStruct(13)->isActive = false; // HELP BTN DN
+        _engine->graphics->getZRenderStruct("MENU BTN DN").isActive = false;
+        _engine->graphics->getZRenderStruct("HELP BTN DN").isActive = false;
         _engine->input->hoveredElementID = -1;
         // TODO a bunch of function calls
         _engine->_gameFlow.previousGameState = NancyEngine::GameState::kScene;
@@ -626,8 +507,7 @@ void SceneManager::run() {
     // code that skips rendering for the first 12 frames??? why
 
     // TODO
-    
-    _engine->graphics->renderDisplay(25);
+    _engine->graphics->renderDisplay();
 }
 
 void SceneManager::clearSceneData() {


Commit: c18e0b171dccb615a19350f114eb729eff0a9507
    https://github.com/scummvm/scummvm/commit/c18e0b171dccb615a19350f114eb729eff0a9507
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add initial mouse support

Implemented a simple mouse with the ability to change the cursor bitmap. Also changed the View struct to use Rects instead of separate properties for every coordinate.

Changed paths:
    engines/nancy/datatypes.cpp
    engines/nancy/datatypes.h
    engines/nancy/graphics.cpp
    engines/nancy/input.cpp
    engines/nancy/input.h
    engines/nancy/scene.cpp
    engines/nancy/scene.h


diff --git a/engines/nancy/datatypes.cpp b/engines/nancy/datatypes.cpp
index 82ff205313..c2bd73cc79 100644
--- a/engines/nancy/datatypes.cpp
+++ b/engines/nancy/datatypes.cpp
@@ -48,8 +48,8 @@ SceneSummary::SceneSummary(Common::SeekableReadStream *stream) {
 
     stream->seek(0x72);
     verticalScrollDelta = stream->readUint16LE();
-    sceneHasRotation = stream->readUint16LE();
-    sceneHasMovement = stream->readUint16LE();
+    horizontalEdgeSize = stream->readUint16LE();
+    verticalEdgeSize = stream->readUint16LE();
     slowMoveTimeDelta = stream->readUint16LE();
     fastMoveTimeDelta = stream->readUint16LE();
     unknown7C = stream->readByte();
@@ -65,22 +65,22 @@ SceneSummary::SceneSummary(Common::SeekableReadStream *stream) {
 // Takes a VIEW chunk as input
 View::View(Common::SeekableReadStream *stream) {
     stream->seek(0);
-    destLeft = stream->readUint32LE();
-    destTop = stream->readUint32LE();
-    destRight = stream->readUint32LE();
-    destBottom = stream->readUint32LE();
-    srcLeft = stream->readUint32LE();
-    srcTop = stream->readUint32LE();
-    srcRight = stream->readUint32LE();
-    srcBottom = stream->readUint32LE();
-    f1Left = stream->readUint32LE();
-    f1Top = stream->readUint32LE();
-    f1Right = stream->readUint32LE();
-    f1Bottom = stream->readUint32LE();
-    f2Left = stream->readUint32LE();
-    f2Top = stream->readUint32LE();
-    f2Right = stream->readUint32LE();
-    f2Bottom = stream->readUint32LE();
+    destination.left = stream->readUint32LE();
+    destination.top = stream->readUint32LE();
+    destination.right = stream->readUint32LE();
+    destination.bottom = stream->readUint32LE();
+    source.left = stream->readUint32LE();
+    source.top = stream->readUint32LE();
+    source.right = stream->readUint32LE();
+    source.bottom = stream->readUint32LE();
+    f1Dest.left = stream->readUint32LE();
+    f1Dest.top = stream->readUint32LE();
+    f1Dest.right = stream->readUint32LE();
+    f1Dest.bottom = stream->readUint32LE();
+    f2Dest.left = stream->readUint32LE();
+    f2Dest.top = stream->readUint32LE();
+    f2Dest.right = stream->readUint32LE();
+    f2Dest.bottom = stream->readUint32LE();
 }
 
 // Takes a CURS chunk as input
diff --git a/engines/nancy/datatypes.h b/engines/nancy/datatypes.h
index cfc0203e13..f6655c7457 100644
--- a/engines/nancy/datatypes.h
+++ b/engines/nancy/datatypes.h
@@ -47,8 +47,8 @@ struct SceneSummary {
     Common::String audioFile;   // 0x40
     //
     uint16 verticalScrollDelta; // 0x72
-    uint16 sceneHasRotation;    // 0x74, could be an enum?
-    uint16 sceneHasMovement;    // 0x76, also an enum??
+    uint16 horizontalEdgeSize;  // 0x74
+    uint16 verticalEdgeSize;    // 0x76
     uint16 slowMoveTimeDelta;   // 0x78
     uint16 fastMoveTimeDelta;   // 0x7A
     byte unknown7C;             // 0x7C, enum with 4 values
@@ -61,25 +61,13 @@ struct View {
     View() =default;
     View(Common::SeekableReadStream *stream);
     // The bounds of the destination rectangle on screen
-    uint32 destLeft;        // 0x00
-    uint32 destTop;         // 0x04
-    uint32 destRight;       // 0x08
-    uint32 destBottom;      // 0x0C
+    Common::Rect destination;
     // The bounds of the source rectangle (Background -> screen)
-    uint32 srcLeft;         // 0x10
-    uint32 srcTop;          // 0x14
-    uint32 srcRight;        // 0x18
-    uint32 srcBottom;       // 0x1C
+    Common::Rect source;
     // VideoFileFormat 1 rectangle bounds (video -> Background)
-    uint32 f1Left;          // 0x20
-    uint32 f1Top;           // 0x24
-    uint32 f1Right;         // 0x28
-    uint32 f1Bottom;        // 0x2C
+    Common::Rect f1Dest;
     // VideoFileFormat 2 rectangle bounds (video -> Background)
-    uint32 f2Left;          // 0x30
-    uint32 f2Top;           // 0x34
-    uint32 f2Right;         // 0x38
-    uint32 f2Bottom;        // 0x3C
+    Common::Rect f2Dest;
 };
 
 // Holds the coordinates for the bitmaps of all cursors
diff --git a/engines/nancy/graphics.cpp b/engines/nancy/graphics.cpp
index f342d4f077..5ec78ea270 100644
--- a/engines/nancy/graphics.cpp
+++ b/engines/nancy/graphics.cpp
@@ -46,8 +46,8 @@ GraphicsManager::GraphicsManager(NancyEngine *engine) :
 void GraphicsManager::init() {  
     Common::SeekableReadStream *chunk = _engine->getBootChunkStream("VIEW");
     viewportDesc = View(chunk);
-    uint32 width = viewportDesc.destRight - viewportDesc.destLeft;
-    uint32 height = viewportDesc.destBottom - viewportDesc.destTop;
+    uint32 width = viewportDesc.destination.right - viewportDesc.destination.left;
+    uint32 height = viewportDesc.destination.bottom - viewportDesc.destination.top;
     _background.create(width, height, pixelFormat);
 
     // TODO make a TBOX struct
@@ -128,9 +128,10 @@ void GraphicsManager::initSceneZRenderStructs() {
 
     chunk = _engine->getBootChunkStream("MENU");
     READ_RECT(source, 16)
-    initZRenderStruct(  "FRAME", 1, true, ZRenderStruct::BltType::kNone, &_primaryFrameSurface,
-                        new RenderFunction(this, &GraphicsManager::renderFrame), source, source);
-    initZRenderStruct(  "CUR IMAGE CURSOR", 11, false, ZRenderStruct::BltType::kTrans, &_object0Surface);
+    // Skip the custom rendering function since we're not doing dirty rectangles
+    initZRenderStruct(  "FRAME", 1, true, ZRenderStruct::BltType::kNoTrans, &_primaryFrameSurface,
+                        nullptr, source, source);
+    initZRenderStruct(  "CUR IMAGE CURSOR", 11, true, ZRenderStruct::BltType::kTrans, &_object0Surface);
 
     chunk = _engine->getBootChunkStream("TBOX");
     READ_RECT(source, 0)
@@ -183,10 +184,8 @@ void GraphicsManager::initSceneZRenderStructs() {
                         new RenderFunction(this, &GraphicsManager::renderPasswordPuzzle));
 
     // Moved here from SceneManager::load(), should be ok
-    *source = Common::Rect(viewportDesc.srcLeft, viewportDesc.srcTop, viewportDesc.srcRight, viewportDesc.srcBottom);
-    *dest = Common::Rect(viewportDesc.destLeft, viewportDesc.destTop, viewportDesc.destRight, viewportDesc.destBottom);
     initZRenderStruct(  "VIEWPORT AVF", 6, true, ZRenderStruct::BltType::kNoTrans,
-                        &_background, nullptr, source, dest);
+                        &_background, nullptr, &viewportDesc.source, &viewportDesc.destination);
     #undef READ_RECT
 
     delete source;
diff --git a/engines/nancy/input.cpp b/engines/nancy/input.cpp
index d5fd963176..d830ee45fe 100644
--- a/engines/nancy/input.cpp
+++ b/engines/nancy/input.cpp
@@ -23,6 +23,7 @@
 #include "engines/nancy/input.h"
 #include "engines/nancy/nancy.h"
 #include "engines/nancy/scene.h"
+#include "engines/nancy/graphics.h"
 
 #include "common/events.h"
 #include "common/keyboard.h"
@@ -171,6 +172,40 @@ bool InputManager::getInput(InputManager::InputType type) {
     return _inputs & type;
 }
 
+Common::Point InputManager::getMousePosition() {
+    return _engine->getEventManager()->getMousePos();
+}
+
+void InputManager::setMousePosition(const Common::Point &newPos) {
+    // Hopefully having two mouse move events in the queue won't break my handling code
+    Common::Event newEvent;
+    newEvent.mouse = newPos;
+    newEvent.type = Common::EventType::EVENT_MOUSEMOVE;
+    _engine->getEventManager()->pushEvent(newEvent);
+}
+
+// Passing -1 means keeping the previous value
+void InputManager::setPointerBitmap(int16 id, int16 style, int16 isHoldingItem) {
+    ZRenderStruct &zr = _engine->graphics->getZRenderStruct("CUR IMAGE CURSOR");
+    
+    if (id != -1)
+        pointerId = id;
+    
+    if (style != -1)
+        pointerStyle = style;
+
+    if (isHoldingItem != -1)
+        itemHeld = (bool)isHoldingItem;
+
+    if (itemHeld) {
+        zr.sourceSurface = &_engine->graphics->_inventoryCursorsSurface;
+    } else {
+        zr.sourceSurface = &_engine->graphics->_object0Surface;
+    }
+
+    zr.sourceRect = cursorsData.rects[pointerId*3 + pointerStyle];
+}
+
 void InputManager::initKeymaps(Common::KeymapArray &keymaps) {
     using namespace Common;
 	using namespace Nancy;
diff --git a/engines/nancy/input.h b/engines/nancy/input.h
index 7adea0c877..a60d093982 100644
--- a/engines/nancy/input.h
+++ b/engines/nancy/input.h
@@ -75,6 +75,10 @@ enum InputType : byte {
     bool getInput(InputType type);
     byte getInput() { return _inputs; }
 
+    Common::Point getMousePosition();
+    void setMousePosition(const Common::Point &newPos);
+    void setPointerBitmap(int16 id, int16 style, int16 itemHeld = 0);
+
     static void initKeymaps(Common::KeymapArray &keymaps);
 
     bool isClickValidLMB;
@@ -108,6 +112,10 @@ enum InputType : byte {
 private:
     NancyEngine *_engine;
 
+    int16 pointerId = 0;
+    int16 pointerStyle = 0;
+    bool itemHeld = false;
+
     byte _inputs;
     Common::Point _mousePos;
 };
diff --git a/engines/nancy/scene.cpp b/engines/nancy/scene.cpp
index 3494d3d2e3..236251727f 100644
--- a/engines/nancy/scene.cpp
+++ b/engines/nancy/scene.cpp
@@ -148,7 +148,7 @@ void SceneManager::load() {
     }
     _engine->graphics->loadBackgroundVideo(currentScene.videoFile);
     if (_engine->graphics->getBackgroundFrameCount() == 1) {
-        currentScene.sceneHasMovement = 0;
+        currentScene.horizontalEdgeSize = 0;
     }
 
     View &viewportDesc = _engine->graphics->viewportDesc;
@@ -172,13 +172,13 @@ void SceneManager::load() {
         if (currentScene.videoFormat == 1) {
             // TODO not sure this ever gets hit
         } else if (currentScene.videoFormat == 2) {
-            if (_engine->graphics->getBackgroundHeight() == viewportDesc.f2Bottom) {
-                currentScene.sceneHasMovement = false;
+            if (_engine->graphics->getBackgroundHeight() == (uint32)viewportDesc.f2Dest.bottom + 1) {
+                currentScene.verticalEdgeSize = 0;
             }
         }
     }
 
-    delete sceneSummaryChunk;
+    _engine->input->setPointerBitmap(0, 0, false);
 
     _state = kRun; // TODO temp, is actually StartSound
 }
@@ -495,7 +495,7 @@ void SceneManager::run() {
             *_engine->graphics->getBackgroundFrame(_engine->playState.currentViewFrame),
             0, 0,
             Common::Rect(0, _engine->playState.verticalScroll, _engine->graphics->getBackgroundWidth() - 1,
-                _engine->playState.verticalScroll + _engine->graphics->viewportDesc.srcBottom));
+                _engine->playState.verticalScroll + _engine->graphics->viewportDesc.source.bottom));
         }
         // TODO some if related to PlaySoundPanFrameAnchorAndDie
         _engine->playState.lastDrawnViewFrame = _engine->playState.currentViewFrame;
@@ -506,10 +506,52 @@ void SceneManager::run() {
 
     // code that skips rendering for the first 12 frames??? why
 
+    handleMouse();
+
     // TODO
     _engine->graphics->renderDisplay();
 }
 
+void SceneManager::handleMouse() {
+    ZRenderStruct &zr = _engine->graphics->getZRenderStruct("CUR IMAGE CURSOR");
+    Common::Point mousePos = _engine->input->getMousePosition();
+    zr.destRect.left = zr.destRect.right = mousePos.x;
+    zr.destRect.top = zr.destRect.bottom = mousePos.y;
+    movementDirection = 0;
+
+    View &view = _engine->graphics->viewportDesc;
+
+    // TODO hotpoints for scene movement are not correct, figure out how they're actually calculated
+    Common::Point viewportMouse = mousePos + Common::Point(10, 10);
+    
+    // Check if the mouse is within the viewport
+    if (view.destination.contains(viewportMouse)){
+        _engine->input->setPointerBitmap(0, 0, 0);
+        // We can scroll left and right
+        if (currentScene.horizontalEdgeSize > 0) {
+            if (viewportMouse.x < view.destination.left + currentScene.horizontalEdgeSize) {
+                movementDirection |= kLeft;
+            } else if (viewportMouse.x > view.destination.right - currentScene.horizontalEdgeSize) {
+                movementDirection |= kRight;
+            }
+        }
+        if (currentScene.verticalEdgeSize > 0) {
+            if (viewportMouse.y < view.destination.top + currentScene.verticalEdgeSize) {
+                movementDirection |= kUp;
+            } else if (viewportMouse.y > view.destination.bottom - currentScene.verticalEdgeSize) {
+                movementDirection |= kDown;
+            }
+        }
+        if (movementDirection != 0) {
+            _engine->input->setPointerBitmap(0, 2, 0);
+        }
+    } else {
+        // pointers are out of order from the one on Object0
+        _engine->input->setPointerBitmap(1, 1, 0);
+    }
+        
+}
+
 void SceneManager::clearSceneData() {
     // TODO these shouldn't be here
     _engine->graphics->_primaryFrameSurface.free();
diff --git a/engines/nancy/scene.h b/engines/nancy/scene.h
index e752b30c62..9226e790ab 100644
--- a/engines/nancy/scene.h
+++ b/engines/nancy/scene.h
@@ -59,6 +59,7 @@ private:
     void load();
     void run();
 
+    void handleMouse();
     void clearSceneData();
 
     enum State {


Commit: 95c7d5031fb7cf744037411f8e39ba28d33f712f
    https://github.com/scummvm/scummvm/commit/95c7d5031fb7cf744037411f8e39ba28d33f712f
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Implement EventFlagsMultiHS action record

Implemented the EventFlagsMultiHS action record, which gets triggered by clickable hotspots on screen.

Changed paths:
    engines/nancy/action/actionrecord.h
    engines/nancy/action/recordtypes.cpp
    engines/nancy/action/recordtypes.h


diff --git a/engines/nancy/action/actionrecord.h b/engines/nancy/action/actionrecord.h
index fe86e7b8ee..3d90c528da 100644
--- a/engines/nancy/action/actionrecord.h
+++ b/engines/nancy/action/actionrecord.h
@@ -63,7 +63,7 @@ struct DependencyRecord {
 
 class ActionRecord {
 public:
-    enum ExecutionState { Start }; // TODO has 4 states
+    enum ExecutionState { kBegin, kRun, kEnd };
     ActionRecord() :
         type(0),
         execType(0),
@@ -74,7 +74,8 @@ public:
         timers(nullptr),
         orFlags(nullptr),
         isDone(false),
-        state(ExecutionState::Start) {}
+        hasHotspot(false),
+        state(ExecutionState::kBegin) {}
     virtual ~ActionRecord() { delete[] dependencies; delete rawData; delete[] satisfiedDependencies; delete[] timers; delete orFlags; }
 
     virtual uint16 readData(Common::SeekableReadStream &stream) =0;
@@ -101,7 +102,8 @@ public:
     Time *timers;                   // 0x48
     bool *orFlags;                  // 0x78
     bool isDone;                    // 0x84
-    Common::Rect activeZone;        // 0x89
+    bool hasHotspot;                // 0x85
+    Common::Rect hotspot;           // 0x89
     ExecutionState state;           // 0x91
 };
 
diff --git a/engines/nancy/action/recordtypes.cpp b/engines/nancy/action/recordtypes.cpp
index 5db7d5fb1d..3c82506da3 100644
--- a/engines/nancy/action/recordtypes.cpp
+++ b/engines/nancy/action/recordtypes.cpp
@@ -25,6 +25,7 @@
 #include "engines/nancy/scene.h"
 #include "engines/nancy/logic.h"
 #include "engines/nancy/nancy.h"
+#include "engines/nancy/graphics.h"
 
 namespace Nancy {
 
@@ -271,14 +272,6 @@ void StopTimer::execute(NancyEngine *engine) {
     isDone = true;
 }
 
-uint16 EventFlagsMultiHS::readData(Common::SeekableReadStream &stream) {
-    stream.seek(0x28, SEEK_CUR);
-    uint16 size = stream.readUint16LE() * 0x12 + 0x2A;
-    stream.seek(-0x2A, SEEK_CUR);
-
-    return readRaw(stream, size); // TODO
-}
-
 uint16 EventFlags::readData(Common::SeekableReadStream &stream) {
     for (uint i = 0; i < 10; ++i) {
         descs[i].label = stream.readSint16LE();
@@ -296,6 +289,47 @@ void EventFlags::execute(NancyEngine *engine) {
     isDone = true;
 }
 
+uint16 EventFlagsMultiHS::readData(Common::SeekableReadStream &stream) {
+    uint16 returnSize = EventFlags::readData(stream);
+    uint16 numHotspots = stream.readUint16LE();
+    for (uint16 i = 0; i < numHotspots; ++i) {
+        hotspots.push_back(HotspotDesc());
+        HotspotDesc &newDesc = hotspots[i];
+        newDesc.frameID = stream.readUint16LE();
+        newDesc.coords.left = stream.readUint32LE();
+        newDesc.coords.top = stream.readUint32LE();
+        newDesc.coords.right = stream.readUint32LE();
+        newDesc.coords.bottom = stream.readUint32LE();
+    }
+    returnSize += numHotspots * 0x12 + 0x2;
+
+    return returnSize;
+}
+
+void EventFlagsMultiHS::execute(NancyEngine *engine) {
+    switch (state) {
+        case kBegin:
+            // turn main rendering on
+            state = kRun;
+            // fall through
+        case kRun:
+            hasHotspot = false;
+            for (uint i = 0; i < hotspots.size(); ++i) {
+                if (hotspots[i].frameID == engine->playState.currentViewFrame) {
+                    hasHotspot = true;
+                    hotspot = hotspots[i].coords;
+                }
+            }
+            break;
+        case kEnd:
+            hasHotspot = false;
+            EventFlags::execute(engine);
+            break;
+        default:
+            break;
+    }
+}
+
 uint16 OrderingPuzzle::readData(Common::SeekableReadStream &stream) {
     return readRaw(stream, 0x26D); // TODO
 }
diff --git a/engines/nancy/action/recordtypes.h b/engines/nancy/action/recordtypes.h
index b5d9a45b24..86a05e86df 100644
--- a/engines/nancy/action/recordtypes.h
+++ b/engines/nancy/action/recordtypes.h
@@ -218,11 +218,6 @@ public:
     virtual void execute(NancyEngine *engine) override;
 };
 
-class EventFlagsMultiHS : public ActionRecord {
-public:
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
-};
-
 class EventFlags : public ActionRecord {
 public:
     struct FlagDesc {
@@ -235,6 +230,18 @@ public:
     FlagDesc descs[10];
 };
 
+class EventFlagsMultiHS : public EventFlags {
+public:
+    struct HotspotDesc {
+        uint16 frameID = 0;
+        Common::Rect coords;
+    };
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void execute(NancyEngine *engine) override;
+
+    Common::Array<HotspotDesc> hotspots;
+};
+
 class OrderingPuzzle : public ActionRecord {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;


Commit: abb1583252f599ffb72c78db079891ac0d4e38d9
    https://github.com/scummvm/scummvm/commit/abb1583252f599ffb72c78db079891ac0d4e38d9
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add hotspot detection

Added detection of hovered hotspots (clickable areas on screen). Added detection of mouse release events in preparation for the handling of clicking on said hotspots.

Changed paths:
    engines/nancy/input.cpp
    engines/nancy/input.h
    engines/nancy/logic.h
    engines/nancy/logo.cpp
    engines/nancy/scene.cpp


diff --git a/engines/nancy/input.cpp b/engines/nancy/input.cpp
index d830ee45fe..a777337f2c 100644
--- a/engines/nancy/input.cpp
+++ b/engines/nancy/input.cpp
@@ -60,6 +60,7 @@ void InputManager::processEvents() {
 
     isClickValidLMB = false;
     isClickValidRMB = false;
+    _inputs &= ~(kLeftMouseButtonUp | kRightMouseButtonUp);
 
     Common::Event event;
 
@@ -90,10 +91,10 @@ void InputManager::processEvents() {
                         _inputs |= kMoveFastModifier;
                         break;
                     case kNancyActionLeftClick:
-                        _inputs |= kLeftMouseButton;
+                        _inputs |= kLeftMouseButtonDown;
                         break;
                     case kNancyActionRightClick:
-                        _inputs |= kRightMouseButton;
+                        _inputs |= kRightMouseButtonDown;
                         break;
                     default:
                         break;
@@ -117,13 +118,15 @@ void InputManager::processEvents() {
                         _inputs &= ~kMoveFastModifier;
                         break;
                     case kNancyActionLeftClick:
-                        _inputs &= ~kLeftMouseButton;
+                        _inputs &= ~kLeftMouseButtonDown;
+                        _inputs |= kLeftMouseButtonUp;
                         if (hoveredElementID != -1) {
                             isClickValidLMB = true;
                         }
                         break;
                     case kNancyActionRightClick:
-                        _inputs &= ~kRightMouseButton;
+                        _inputs &= ~kRightMouseButtonDown;
+                        _inputs |= kRightMouseButtonUp;
                         if (hoveredElementID != -1) {
                             isClickValidRMB = true;
                         }
diff --git a/engines/nancy/input.h b/engines/nancy/input.h
index a60d093982..cb18683139 100644
--- a/engines/nancy/input.h
+++ b/engines/nancy/input.h
@@ -59,14 +59,16 @@ enum NancyAction {
 };
 
 public:
-enum InputType : byte {
-    kLeftMouseButton    = 1 << 0,
-    kRightMouseButton   = 1 << 1,
-    kMoveUp             = 1 << 2,
-    kMoveDown           = 1 << 3,
-    kMoveLeft           = 1 << 4,
-    kMoveRight          = 1 << 5,
-    kMoveFastModifier   = 1 << 6
+enum InputType : uint16 {
+    kLeftMouseButtonDown    = 1 << 0,
+    kRightMouseButtonDown   = 1 << 1,
+    kLeftMouseButtonUp    = 1 << 2,
+    kRightMouseButtonUp   = 1 << 3,
+    kMoveUp             = 1 << 4,
+    kMoveDown           = 1 << 5,
+    kMoveLeft           = 1 << 6,
+    kMoveRight          = 1 << 7,
+    kMoveFastModifier   = 1 << 8
 };
 
     InputManager(NancyEngine *engine) : _engine(engine), _inputs(0), isClickValidLMB(false), isClickValidRMB(false), hoveredElementID(-1) {}
@@ -116,7 +118,7 @@ private:
     int16 pointerStyle = 0;
     bool itemHeld = false;
 
-    byte _inputs;
+    uint16 _inputs;
     Common::Point _mousePos;
 };
 
diff --git a/engines/nancy/logic.h b/engines/nancy/logic.h
index 3a739977aa..c9ab77da37 100644
--- a/engines/nancy/logic.h
+++ b/engines/nancy/logic.h
@@ -44,6 +44,7 @@ public:
 
     bool addNewActionRecord(Common::SeekableReadStream &inputData);
     void processActionRecords();
+    Common::Array<ActionRecord *> &getActionRecords() { return _records; }
 
 protected:
     virtual ActionRecord *createActionRecord(uint16 type);
diff --git a/engines/nancy/logo.cpp b/engines/nancy/logo.cpp
index 183bdab039..90f9954357 100644
--- a/engines/nancy/logo.cpp
+++ b/engines/nancy/logo.cpp
@@ -84,7 +84,7 @@ void LogoSequence::run() {
 		_runState = kWait;
 		break;
 	case kWait:
-		if (_engine->_system->getMillis() - _startTicks >= 7000 || (_engine->input->getInput(InputManager::kLeftMouseButton)))
+		if (_engine->_system->getMillis() - _startTicks >= 7000 || (_engine->input->getInput(InputManager::kLeftMouseButtonDown)))
 			_state = kStop;
 	}
 }
diff --git a/engines/nancy/scene.cpp b/engines/nancy/scene.cpp
index 236251727f..3693d20053 100644
--- a/engines/nancy/scene.cpp
+++ b/engines/nancy/scene.cpp
@@ -403,7 +403,7 @@ void SceneManager::run() {
         // Perform movement
         byte inputs = _engine->input->getInput();
         if  ( ( (
-                    (inputs & InputManager::kLeftMouseButton) != (inputs & InputManager::kRightMouseButton) 
+                    (inputs & InputManager::kLeftMouseButtonDown) != (inputs & InputManager::kRightMouseButtonDown) 
                 ) ||
                 (inputs & (InputManager::kMoveUp | InputManager::kMoveDown | InputManager::kMoveLeft | InputManager::kMoveRight) )
               ) &&
@@ -477,7 +477,7 @@ void SceneManager::run() {
                     break;
             }
             if (_engine->input->getInput(InputManager::kMoveFastModifier) ||
-                _engine->input->getInput(InputManager::kRightMouseButton)) {
+                _engine->input->getInput(InputManager::kRightMouseButtonDown)) {
                 _nextBackgroundMovement = playTimeThisFrame + currentScene.fastMoveTimeDelta;
 
             } else {
@@ -518,6 +518,7 @@ void SceneManager::handleMouse() {
     zr.destRect.left = zr.destRect.right = mousePos.x;
     zr.destRect.top = zr.destRect.bottom = mousePos.y;
     movementDirection = 0;
+    _engine->input->hoveredElementID = -1;
 
     View &view = _engine->graphics->viewportDesc;
 
@@ -542,12 +543,34 @@ void SceneManager::handleMouse() {
                 movementDirection |= kDown;
             }
         }
+
         if (movementDirection != 0) {
             _engine->input->setPointerBitmap(0, 2, 0);
+        } else {
+            // Go through all action records and find hotspots
+            Common::Array<ActionRecord *> &records = _engine->logic->getActionRecords();
+            for (uint i = 0; i < records.size(); ++i) {
+                ActionRecord *r = records[i];
+                if (r->isActive && r->hasHotspot) {
+                    // Adjust the hotspot coordinates relative to the viewport
+                    Common::Rect hotspot = r->hotspot;
+                    hotspot.left += view.destination.left;
+                    hotspot.top += view.destination.top;
+                    hotspot.right += view.destination.left;
+                    hotspot.bottom += view.destination.top;
+
+                    if (hotspot.contains(viewportMouse)) {
+                        _engine->input->setPointerBitmap(-1, 1, 0);
+                        _engine->input->hoveredElementID = i;
+                    }
+                }
+            }
         }
     } else {
+        
         // pointers are out of order from the one on Object0
         _engine->input->setPointerBitmap(1, 1, 0);
+        
     }
         
 }


Commit: 8397e352127f90b2df0064f7a0c245fec47cadbd
    https://github.com/scummvm/scummvm/commit/8397e352127f90b2df0064f7a0c245fec47cadbd
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Display UI sliders

Added code to show the sliders for the text and inventory boxes and made them hoverable. Interacting with them is still TODO.

Changed paths:
    engines/nancy/graphics.cpp
    engines/nancy/scene.cpp


diff --git a/engines/nancy/graphics.cpp b/engines/nancy/graphics.cpp
index 5ec78ea270..8269d5bf67 100644
--- a/engines/nancy/graphics.cpp
+++ b/engines/nancy/graphics.cpp
@@ -135,7 +135,7 @@ void GraphicsManager::initSceneZRenderStructs() {
 
     chunk = _engine->getBootChunkStream("TBOX");
     READ_RECT(source, 0)
-    initZRenderStruct(  "CUR TB BAT SLIDER", 9, false, ZRenderStruct::BltType::kTrans,
+    initZRenderStruct(  "CUR TB BAT SLIDER", 9, true, ZRenderStruct::BltType::kTrans,
                         &_object0Surface, nullptr, source, nullptr);
 
     chunk = _engine->getBootChunkStream("BSUM");
@@ -155,7 +155,7 @@ void GraphicsManager::initSceneZRenderStructs() {
 
     chunk = _engine->getBootChunkStream("INV");
     READ_RECT(source, 0)
-    initZRenderStruct(  "CUR INV SLIDER", 9, false, ZRenderStruct::BltType::kTrans,
+    initZRenderStruct(  "CUR INV SLIDER", 9, true, ZRenderStruct::BltType::kTrans,
                          &_object0Surface, nullptr, source, nullptr);
 
     initZRenderStruct(  "FRAME INV BOX", 6, false, ZRenderStruct::BltType::kNoTrans, nullptr,
@@ -230,7 +230,7 @@ void GraphicsManager::renderDisplay(Common::Array<Common::String> ids) {
 
                 // Current struct has been rendered, remove from list
                 ids.remove_at(i);
-                break;
+                --i;
             }
         }
     }
diff --git a/engines/nancy/scene.cpp b/engines/nancy/scene.cpp
index 3693d20053..df041bd189 100644
--- a/engines/nancy/scene.cpp
+++ b/engines/nancy/scene.cpp
@@ -106,6 +106,26 @@ void SceneManager::init() {
 
     _engine->graphics->initSceneZRenderStructs();
 
+    // Set the scroll bar destinations
+    Common::SeekableReadStream *tbox = _engine->getBootChunkStream("TBOX");
+    tbox->seek(0x30);
+    ZRenderStruct &tbzr = _engine->graphics->getZRenderStruct("CUR TB BAT SLIDER");
+    uint16 width = tbzr.sourceRect.right - tbzr.sourceRect.left;
+    uint16 height = tbzr.sourceRect.bottom - tbzr.sourceRect.top;
+    tbzr.destRect.left =  tbox->readUint16LE() - (width / 2); // coords in file are for center position
+    tbzr.destRect.top =  tbox->readUint16LE();
+    tbzr.destRect.right = tbzr.destRect.left + width;
+    tbzr.destRect.bottom = tbzr.destRect.top + height;
+
+    inv->seek(0x10);
+    ZRenderStruct &invzr = _engine->graphics->getZRenderStruct("CUR INV SLIDER");
+    width = invzr.sourceRect.right - invzr.sourceRect.left;
+    height = invzr.sourceRect.bottom - invzr.sourceRect.top;
+    invzr.destRect.left = inv->readUint16LE() - (width / 2); // coords in file are for center position
+    invzr.destRect.top = inv->readUint16LE();
+    invzr.destRect.right = invzr.destRect.left + width;
+    invzr.destRect.bottom = invzr.destRect.top + height;
+
     _state = kLoad;
 }
 
@@ -567,10 +587,19 @@ void SceneManager::handleMouse() {
             }
         }
     } else {
-        
-        // pointers are out of order from the one on Object0
-        _engine->input->setPointerBitmap(1, 1, 0);
-        
+        // Check if we're hovering above a UI element
+        ZRenderStruct *uizr = &_engine->graphics->getZRenderStruct("CUR TB BAT SLIDER");
+        if (uizr->destRect.contains(mousePos)) {
+            _engine->input->hoveredElementID = InputManager::textBoxScrollbarID;
+            // call handler function
+            _engine->input->setPointerBitmap(1, 2, -1);
+        } else if (uizr = &_engine->graphics->getZRenderStruct("CUR INV SLIDER"), uizr->destRect.contains(mousePos)) {
+            _engine->input->hoveredElementID = InputManager::inventoryScrollbarID;
+            // call handler function
+            _engine->input->setPointerBitmap(1, 2, -1);
+        } else {
+            _engine->input->setPointerBitmap(1, 1, 0);
+        }
     }
         
 }


Commit: cd0301af92c51123df3034d23cd6a05bb0b4bf57
    https://github.com/scummvm/scummvm/commit/cd0301af92c51123df3034d23cd6a05bb0b4bf57
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Implement scene switching

Added just enough code to allow the player to pick the difficulty and go to the intro scene.

Changed paths:
    engines/nancy/action/recordtypes.cpp
    engines/nancy/action/recordtypes.h
    engines/nancy/logic.cpp
    engines/nancy/logic.h
    engines/nancy/scene.cpp
    engines/nancy/scene.h


diff --git a/engines/nancy/action/recordtypes.cpp b/engines/nancy/action/recordtypes.cpp
index 3c82506da3..3705b804a7 100644
--- a/engines/nancy/action/recordtypes.cpp
+++ b/engines/nancy/action/recordtypes.cpp
@@ -42,7 +42,19 @@ uint16 HotMultiframeSceneChange::readData(Common::SeekableReadStream &stream) {
 }
 
 uint16 SceneChange::readData(Common::SeekableReadStream &stream) {
-    return readRaw(stream, 0x8); // TODO
+    sceneID = stream.readUint16LE();
+    frameID = stream.readUint16LE();
+    verticalOffset = stream.readUint16LE();
+    doNotStartSound = (bool)(stream.readUint16LE());
+    return 8; // TODO
+}
+
+void SceneChange::execute(NancyEngine *engine) {
+    engine->sceneManager->_sceneID = sceneID;
+    engine->playState.queuedViewFrame = frameID;
+    engine->playState.queuedMaxVerticalScroll = verticalOffset;
+    engine->sceneManager->doNotStartSound = doNotStartSound;
+    engine->sceneManager->_state = SceneManager::kLoadNew;
 }
 
 uint16 HotMultiframeMultisceneChange::readData(Common::SeekableReadStream &stream) {
diff --git a/engines/nancy/action/recordtypes.h b/engines/nancy/action/recordtypes.h
index 86a05e86df..dacaf9c11f 100644
--- a/engines/nancy/action/recordtypes.h
+++ b/engines/nancy/action/recordtypes.h
@@ -46,6 +46,12 @@ public:
 class SceneChange : public ActionRecord {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void execute(NancyEngine *engine) override;
+
+    uint16 sceneID = 0;
+    uint16 frameID = 0;
+    uint16 verticalOffset = 0;
+    bool doNotStartSound = false;
 };
 
 class HotMultiframeMultisceneChange : public ActionRecord {
diff --git a/engines/nancy/logic.cpp b/engines/nancy/logic.cpp
index 2f8cc71c36..6b0a9c6ee8 100644
--- a/engines/nancy/logic.cpp
+++ b/engines/nancy/logic.cpp
@@ -104,9 +104,9 @@ void Logic::processActionRecords() {
         if (!record->isActive) {
             if (record->numDependencies > 0) {
                 for (uint i = 0; i < record->numDependencies; ++i) {
-                    if (record->satisfiedDependencies[i] != 0) {
+                    if (record->satisfiedDependencies[i] == 0) {
                         DependencyRecord &dep = record->dependencies[i];
-                        switch (record->dependencies[1].type) {
+                        switch (dep.type) {
                             case kNone:
                                 record->satisfiedDependencies[i] = true;
                             case kInventory:
@@ -219,4 +219,8 @@ void Logic::processActionRecords() {
     }
 }
 
+void Logic::clearActionRecords() {
+    _records.clear();
+}
+
 } // End of namespace Nancy
\ No newline at end of file
diff --git a/engines/nancy/logic.h b/engines/nancy/logic.h
index c9ab77da37..045748409a 100644
--- a/engines/nancy/logic.h
+++ b/engines/nancy/logic.h
@@ -45,6 +45,8 @@ public:
     bool addNewActionRecord(Common::SeekableReadStream &inputData);
     void processActionRecords();
     Common::Array<ActionRecord *> &getActionRecords() { return _records; }
+    ActionRecord * getActionRecord(uint id) { if (id < _records.size()) return _records[id]; else return nullptr;}
+    void clearActionRecords();
 
 protected:
     virtual ActionRecord *createActionRecord(uint16 type);
diff --git a/engines/nancy/scene.cpp b/engines/nancy/scene.cpp
index df041bd189..62f011af75 100644
--- a/engines/nancy/scene.cpp
+++ b/engines/nancy/scene.cpp
@@ -146,7 +146,6 @@ void SceneManager::load() {
 
     // The check to see if we need to switch the CD is performed here
 
-    // Not sure what these do yet
     Common::SeekableReadStream *bsum = _engine->getBootChunkStream("BSUM");
     bsum->seek(0x1F1);
     byte overrideMovementDeltas = bsum->readByte();
@@ -156,6 +155,7 @@ void SceneManager::load() {
     }
 
     // Search for Action Records, maximum for a scene is 30
+    _engine->logic->clearActionRecords();
     Common::SeekableReadStream *actionRecordChunk = nullptr;
 
     while (actionRecordChunk = sceneIFF.getChunkStream("ACT", _engine->logic->_records.size()), actionRecordChunk != nullptr)
@@ -198,6 +198,8 @@ void SceneManager::load() {
         }
     }
 
+    // Redraw the viewport
+    _engine->playState.lastDrawnViewFrame = -1;
     _engine->input->setPointerBitmap(0, 0, false);
 
     _state = kRun; // TODO temp, is actually StartSound
@@ -411,6 +413,14 @@ void SceneManager::run() {
             // TODO
         }
 
+        // ID must be an action record's
+        ActionRecord *rec = _engine->logic->getActionRecord(hovered);
+        if (rec->isActive /*&& another condition !- 0*/) {
+            // TODO item holding logic
+            rec->state = ActionRecord::ExecutionState::kEnd;
+        }
+
+
     } else if (_engine->input->isClickValidRMB) {
         if (_engine->input->hoveredElementID == InputManager::textBoxScrollbarID) {
             // TODO, moves scrollbar one line up
diff --git a/engines/nancy/scene.h b/engines/nancy/scene.h
index 9226e790ab..62b663dcfd 100644
--- a/engines/nancy/scene.h
+++ b/engines/nancy/scene.h
@@ -62,6 +62,7 @@ private:
     void handleMouse();
     void clearSceneData();
 
+public:
     enum State {
         kInit,
         kLoad,
@@ -69,15 +70,15 @@ private:
         kRun,
         kLoadNew
     };
-
-public:
+    
     int32 playerTimeMinuteLength;
     byte movementDirection;
+    State _state;
+    uint16 _sceneID;
+    bool doNotStartSound = false;
 
 private:
     NancyEngine *_engine;
-    State _state;
-    uint16 _sceneID;
     SceneSummary currentScene;
     // TODO these two can be Time
     uint32 _tickCount;


Commit: 4ab6de6c23e3eaac050124167d0f4bde5c4aba72
    https://github.com/scummvm/scummvm/commit/4ab6de6c23e3eaac050124167d0f4bde5c4aba72
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Implement skipping of intro

Implemented the Hot1FrSceneChange action record, which lets the player skip the intro and enter the first room of the game.

Changed paths:
    engines/nancy/action/recordtypes.cpp
    engines/nancy/action/recordtypes.h


diff --git a/engines/nancy/action/recordtypes.cpp b/engines/nancy/action/recordtypes.cpp
index 3705b804a7..86909fdd09 100644
--- a/engines/nancy/action/recordtypes.cpp
+++ b/engines/nancy/action/recordtypes.cpp
@@ -29,10 +29,13 @@
 
 namespace Nancy {
 
-uint16 Hot1FrSceneChange::readData(Common::SeekableReadStream &stream) {
-    return readRaw(stream, 0x1A); // TODO
+void HotspotDesc::readData(Common::SeekableReadStream &stream) {
+    frameID = stream.readUint16LE();
+    coords.left = stream.readUint32LE();
+    coords.top = stream.readUint32LE();
+    coords.right = stream.readUint32LE();
+    coords.bottom = stream.readUint32LE();
 }
-
 uint16 HotMultiframeSceneChange::readData(Common::SeekableReadStream &stream) {
     stream.seek(8, SEEK_CUR);
     int16 size = stream.readSint16LE() * 0x12 + 0xA;
@@ -57,6 +60,30 @@ void SceneChange::execute(NancyEngine *engine) {
     engine->sceneManager->_state = SceneManager::kLoadNew;
 }
 
+uint16 Hot1FrSceneChange::readData(Common::SeekableReadStream &stream) {
+    SceneChange::readData(stream);
+    hotspotDesc.readData(stream);
+    return 0x1A;
+}
+
+void Hot1FrSceneChange::execute(NancyEngine *engine) {
+    switch (state) {
+        case kBegin:
+            hotspot = hotspotDesc.coords;
+            // fall through
+        case kRun:
+            if (hotspotDesc.frameID == engine->playState.currentViewFrame) {
+                hasHotspot = true;
+            } else {
+                hasHotspot = false;
+            }
+            break;
+        case kEnd:
+            SceneChange::execute(engine);
+            break;
+    }
+}
+
 uint16 HotMultiframeMultisceneChange::readData(Common::SeekableReadStream &stream) {
     stream.seek(0x14, SEEK_CUR);
     uint size = stream.readUint16LE() * 0x12 + 0x16;
@@ -307,11 +334,7 @@ uint16 EventFlagsMultiHS::readData(Common::SeekableReadStream &stream) {
     for (uint16 i = 0; i < numHotspots; ++i) {
         hotspots.push_back(HotspotDesc());
         HotspotDesc &newDesc = hotspots[i];
-        newDesc.frameID = stream.readUint16LE();
-        newDesc.coords.left = stream.readUint32LE();
-        newDesc.coords.top = stream.readUint32LE();
-        newDesc.coords.right = stream.readUint32LE();
-        newDesc.coords.bottom = stream.readUint32LE();
+        newDesc.readData(stream);
     }
     returnSize += numHotspots * 0x12 + 0x2;
 
diff --git a/engines/nancy/action/recordtypes.h b/engines/nancy/action/recordtypes.h
index dacaf9c11f..09736ebbd6 100644
--- a/engines/nancy/action/recordtypes.h
+++ b/engines/nancy/action/recordtypes.h
@@ -33,9 +33,12 @@ namespace Nancy {
 
 class NancyEngine;
 
-class Hot1FrSceneChange : public ActionRecord {
-public:
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+// Describes a hotspot
+struct HotspotDesc {
+    uint16 frameID = 0;
+    Common::Rect coords;
+
+    void readData(Common::SeekableReadStream &stream);
 };
 
 class HotMultiframeSceneChange : public ActionRecord {
@@ -54,6 +57,14 @@ public:
     bool doNotStartSound = false;
 };
 
+class Hot1FrSceneChange : public SceneChange {
+public:
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void execute(NancyEngine *engine) override;
+
+    HotspotDesc hotspotDesc;
+};
+
 class HotMultiframeMultisceneChange : public ActionRecord {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
@@ -238,10 +249,6 @@ public:
 
 class EventFlagsMultiHS : public EventFlags {
 public:
-    struct HotspotDesc {
-        uint16 frameID = 0;
-        Common::Rect coords;
-    };
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
     virtual void execute(NancyEngine *engine) override;
 


Commit: 2fb0dabcbbf07084322942aab6eb8291287a2de0
    https://github.com/scummvm/scummvm/commit/2fb0dabcbbf07084322942aab6eb8291287a2de0
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Fix vertical scroll

Small fix to properly display vertical scrolling in the viewport.

Changed paths:
    engines/nancy/playstate.h
    engines/nancy/scene.cpp


diff --git a/engines/nancy/playstate.h b/engines/nancy/playstate.h
index 3d00731812..c1d2581173 100644
--- a/engines/nancy/playstate.h
+++ b/engines/nancy/playstate.h
@@ -56,6 +56,7 @@ struct PlayState {
     uint16 currentMaxVerticalScroll = 0;
     uint16 queuedMaxVerticalScroll = 0;
     uint16 verticalScroll = 0; // This replaces rDisplayed
+    uint16 lastVerticalScroll = 0;
     uint16 verticalScrollDelta = 0;
 };
 
diff --git a/engines/nancy/scene.cpp b/engines/nancy/scene.cpp
index 62f011af75..7c11f02e1f 100644
--- a/engines/nancy/scene.cpp
+++ b/engines/nancy/scene.cpp
@@ -517,7 +517,8 @@ void SceneManager::run() {
     }
 
     // Redraw the Background surface if we've moved
-    if (_engine->playState.currentViewFrame != _engine->playState.lastDrawnViewFrame) {
+    if (_engine->playState.currentViewFrame != _engine->playState.lastDrawnViewFrame ||
+        _engine->playState.verticalScroll != _engine->playState.lastVerticalScroll) {
         if (currentScene.videoFormat == 1) {
             // TODO if it ever gets hit
         } else if (currentScene.videoFormat == 2) {
@@ -539,6 +540,7 @@ void SceneManager::run() {
     handleMouse();
 
     // TODO
+    _engine->playState.lastVerticalScroll = _engine->playState.verticalScroll;
     _engine->graphics->renderDisplay();
 }
 


Commit: e12f6b6753bee78181ba0914f8c7bef51406a29e
    https://github.com/scummvm/scummvm/commit/e12f6b6753bee78181ba0914f8c7bef51406a29e
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add more scene change types

Implemented every scene change type except HotMultiframeMultisceneChange, allowing the player to walk around the starting area.

Changed paths:
    engines/nancy/action/arfactory_v1.cpp
    engines/nancy/action/recordtypes.cpp
    engines/nancy/action/recordtypes.h
    engines/nancy/scene.cpp


diff --git a/engines/nancy/action/arfactory_v1.cpp b/engines/nancy/action/arfactory_v1.cpp
index 47e291286a..c4423e9712 100644
--- a/engines/nancy/action/arfactory_v1.cpp
+++ b/engines/nancy/action/arfactory_v1.cpp
@@ -39,7 +39,7 @@ ActionRecord *Logic::createActionRecord(uint16 type) {
         case 0x03:
             return new HotMultiframeMultisceneChange();
         case 0x04:
-            return new Hot1frExitSceneChange();
+            return new Hot1FrSceneChange();
         case 0x0C:
             return new StartFrameNextScene();
         case 0x14:
diff --git a/engines/nancy/action/recordtypes.cpp b/engines/nancy/action/recordtypes.cpp
index 86909fdd09..d2551d28a0 100644
--- a/engines/nancy/action/recordtypes.cpp
+++ b/engines/nancy/action/recordtypes.cpp
@@ -36,13 +36,6 @@ void HotspotDesc::readData(Common::SeekableReadStream &stream) {
     coords.right = stream.readUint32LE();
     coords.bottom = stream.readUint32LE();
 }
-uint16 HotMultiframeSceneChange::readData(Common::SeekableReadStream &stream) {
-    stream.seek(8, SEEK_CUR);
-    int16 size = stream.readSint16LE() * 0x12 + 0xA;
-    stream.seek(-10, SEEK_CUR);
-
-    return readRaw(stream, size); // TODO
-}
 
 uint16 SceneChange::readData(Common::SeekableReadStream &stream) {
     sceneID = stream.readUint16LE();
@@ -58,6 +51,40 @@ void SceneChange::execute(NancyEngine *engine) {
     engine->playState.queuedMaxVerticalScroll = verticalOffset;
     engine->sceneManager->doNotStartSound = doNotStartSound;
     engine->sceneManager->_state = SceneManager::kLoadNew;
+    isDone = true;
+}
+
+uint16 HotMultiframeSceneChange::readData(Common::SeekableReadStream &stream) {
+    uint16 ret = SceneChange::readData(stream);
+    uint16 numHotspots = stream.readUint16LE();
+
+    for (uint i = 0; i < numHotspots; ++i) {
+        hotspots.push_back(HotspotDesc());
+        HotspotDesc &newDesc = hotspots[i];
+        newDesc.readData(stream);
+    }
+
+    return ret + (numHotspots * 0x12) + 2;
+}
+
+void HotMultiframeSceneChange::execute(NancyEngine *engine) {
+    switch (state) {
+        case kBegin:
+            // turn main rendering on
+            // fall through
+        case kRun:
+            hasHotspot = false;
+            for (uint i = 0; i < hotspots.size(); ++i) {
+                if (hotspots[i].frameID == engine->playState.currentViewFrame) {
+                    hasHotspot = true;
+                    hotspot = hotspots[i].coords;
+                }
+            }
+            break;
+        case kEnd:
+            SceneChange::execute(engine);
+            break;
+    }
 }
 
 uint16 Hot1FrSceneChange::readData(Common::SeekableReadStream &stream) {
@@ -92,10 +119,6 @@ uint16 HotMultiframeMultisceneChange::readData(Common::SeekableReadStream &strea
     return readRaw(stream, size); // TODO
 }
 
-uint16 Hot1frExitSceneChange::readData(Common::SeekableReadStream &stream) {
-    return readRaw(stream, 0x1A); // TODO, could be same struct as Hot1FrSceneChange
-}
-
 uint16 StartFrameNextScene::readData(Common::SeekableReadStream &stream) {
     return readRaw(stream, 0x4); // TODO
 }
diff --git a/engines/nancy/action/recordtypes.h b/engines/nancy/action/recordtypes.h
index 09736ebbd6..d4c3f12e84 100644
--- a/engines/nancy/action/recordtypes.h
+++ b/engines/nancy/action/recordtypes.h
@@ -41,11 +41,6 @@ struct HotspotDesc {
     void readData(Common::SeekableReadStream &stream);
 };
 
-class HotMultiframeSceneChange : public ActionRecord {
-public:
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
-};
-
 class SceneChange : public ActionRecord {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
@@ -57,20 +52,24 @@ public:
     bool doNotStartSound = false;
 };
 
-class Hot1FrSceneChange : public SceneChange {
+class HotMultiframeSceneChange : public SceneChange {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
     virtual void execute(NancyEngine *engine) override;
 
-    HotspotDesc hotspotDesc;
+    Common::Array<HotspotDesc> hotspots;
 };
 
-class HotMultiframeMultisceneChange : public ActionRecord {
+// The exact same logic as Hot1FrExitSceneChange
+class Hot1FrSceneChange : public SceneChange {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void execute(NancyEngine *engine) override;
+
+    HotspotDesc hotspotDesc;
 };
 
-class Hot1frExitSceneChange : public ActionRecord {
+class HotMultiframeMultisceneChange : public ActionRecord {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
 };
diff --git a/engines/nancy/scene.cpp b/engines/nancy/scene.cpp
index 7c11f02e1f..ff1387cf3b 100644
--- a/engines/nancy/scene.cpp
+++ b/engines/nancy/scene.cpp
@@ -585,14 +585,21 @@ void SceneManager::handleMouse() {
                 ActionRecord *r = records[i];
                 if (r->isActive && r->hasHotspot) {
                     // Adjust the hotspot coordinates relative to the viewport
+                    // taking into account the vertical scroll as well
                     Common::Rect hotspot = r->hotspot;
                     hotspot.left += view.destination.left;
-                    hotspot.top += view.destination.top;
+                    hotspot.top += view.destination.top - _engine->playState.verticalScroll;
                     hotspot.right += view.destination.left;
-                    hotspot.bottom += view.destination.top;
+                    hotspot.bottom += view.destination.top - _engine->playState.verticalScroll;
 
                     if (hotspot.contains(viewportMouse)) {
-                        _engine->input->setPointerBitmap(-1, 1, 0);
+                        if (r->type == 0xE) {
+                            // Set the pointer to the U-shaped arrow if
+                            // the type is Hot1FrExitSceneChange
+                            _engine->input->setPointerBitmap(1, 0, 0);
+                        } else {
+                            _engine->input->setPointerBitmap(-1, 1, 0);
+                        }
                         _engine->input->hoveredElementID = i;
                     }
                 }


Commit: f4d8368bd45f090ebb6f7555e77fa16cd09d2720
    https://github.com/scummvm/scummvm/commit/f4d8368bd45f090ebb6f7555e77fa16cd09d2720
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add initial sound support

Added a sound manager and hooked the Logo and Scene classes to it; also implemented the PlayDigiSoundAndDie action record. As a result the first part of the intro and some of the sounds in the first area can now play properly.

Changed paths:
    engines/nancy/action/recordtypes.cpp
    engines/nancy/action/recordtypes.h
    engines/nancy/audio.cpp
    engines/nancy/audio.h
    engines/nancy/datatypes.cpp
    engines/nancy/datatypes.h
    engines/nancy/logo.cpp
    engines/nancy/nancy.cpp
    engines/nancy/nancy.h
    engines/nancy/scene.cpp


diff --git a/engines/nancy/action/recordtypes.cpp b/engines/nancy/action/recordtypes.cpp
index d2551d28a0..bf4ede3b4d 100644
--- a/engines/nancy/action/recordtypes.cpp
+++ b/engines/nancy/action/recordtypes.cpp
@@ -26,6 +26,9 @@
 #include "engines/nancy/logic.h"
 #include "engines/nancy/nancy.h"
 #include "engines/nancy/graphics.h"
+#include "engines/nancy/audio.h"
+
+#include "common/str.h"
 
 namespace Nancy {
 
@@ -71,6 +74,7 @@ void HotMultiframeSceneChange::execute(NancyEngine *engine) {
     switch (state) {
         case kBegin:
             // turn main rendering on
+            state = kRun;
             // fall through
         case kRun:
             hasHotspot = false;
@@ -97,6 +101,7 @@ void Hot1FrSceneChange::execute(NancyEngine *engine) {
     switch (state) {
         case kBegin:
             hotspot = hotspotDesc.coords;
+            state = kRun;
             // fall through
         case kRun:
             if (hotspotDesc.frameID == engine->playState.currentViewFrame) {
@@ -469,7 +474,34 @@ uint16 ShowInventoryItem::readData(Common::SeekableReadStream &stream) {
 }
 
 uint16 PlayDigiSoundAndDie::readData(Common::SeekableReadStream &stream) {
-    return readRaw(stream, 0x2B); // TODO
+    char str[10];
+    stream.read(str, 10);
+    filename = Common::String(str);
+    id = stream.readSint16LE();
+    stream.skip(4);
+    numLoops = stream.readUint16LE();
+    stream.skip(4);
+    volume = stream.readUint16LE();
+    stream.skip(6);
+    SceneChange::readData(stream);
+    return 0x2B;
+}
+
+void PlayDigiSoundAndDie::execute(NancyEngine *engine){
+    switch (state) {
+        case kBegin:
+            engine->sound->loadSound(filename, id, numLoops, volume);
+            state = kRun;
+            break;
+        case kRun:
+            if (!engine->sound->isSoundPlaying(id)) {
+                state = kEnd;
+            }
+            break;
+        case kEnd:
+            SceneChange::execute(engine);
+            break;
+    }
 }
 
 uint16 PlaySoundPanFrameAnchorAndDie::readData(Common::SeekableReadStream &stream) {
diff --git a/engines/nancy/action/recordtypes.h b/engines/nancy/action/recordtypes.h
index d4c3f12e84..b5af7eb436 100644
--- a/engines/nancy/action/recordtypes.h
+++ b/engines/nancy/action/recordtypes.h
@@ -337,10 +337,18 @@ public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
 };
 
-class PlayDigiSoundAndDie : public ActionRecord {
+class PlayDigiSoundAndDie : public SceneChange {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void execute(NancyEngine *engine) override;
     // TODO subclass into Play and Stop (?)
+
+    Common::String filename;
+    int16 id = -1; // 0xA
+    uint16 numLoops = 0; // 0x10
+    uint16 volume = 0; // 0x16, between 0 and 60
+    // ...
+    // SceneChange elements at 0x1E
 };
 
 class PlaySoundPanFrameAnchorAndDie : public ActionRecord {
diff --git a/engines/nancy/audio.cpp b/engines/nancy/audio.cpp
index 498125de26..6c36bf0881 100644
--- a/engines/nancy/audio.cpp
+++ b/engines/nancy/audio.cpp
@@ -20,6 +20,10 @@
  *
  */
 
+#include "engines/nancy/audio.h"
+#include "engines/nancy/nancy.h"
+
+#include "common/system.h"
 #include "common/debug.h"
 #include "common/textconsole.h"
 #include "common/stream.h"
@@ -172,4 +176,52 @@ Audio::SeekableAudioStream *makeHISStream(Common::SeekableReadStream *stream, Di
 		return Audio::makeVorbisStream(subStream, DisposeAfterUse::YES);
 }
 
+SoundManager::SoundManager(NancyEngine *engine) :
+		_engine(engine) {
+	_mixer = _engine->_system->getMixer();
+}
+
+// Combine load and play until i find a reason not to
+void SoundManager::loadSound(Common::String &name, int16 id, uint16 numLoops, uint16 volume) {
+	if (_mixer->isSoundHandleActive(handles[id])) {
+		_mixer->stopHandle(handles[id]);
+	}
+	Common::SeekableReadStream *mSnd = SearchMan.createReadStreamForMember(name + ".his");
+	if (mSnd) {
+		Audio::RewindableAudioStream *aStr = makeHISStream(mSnd, DisposeAfterUse::YES);
+		if (aStr) {
+			Audio::AudioStream *aStrLoop = Audio::makeLoopingAudioStream(aStr, numLoops);
+			_engine->_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &handles[id], aStrLoop, -1, volume * 255 / 60);
+			names[id] = name;
+		}
+	}
+}
+
+void SoundManager::stopSound(int16 id) {
+	if (isSoundPlaying(id)) {
+		_mixer->stopHandle(handles[id]);
+	}
+	names[id] = Common::String();
+}
+
+bool SoundManager::isSoundPlaying(int16 id) {
+	if (id >= 0 && id < 20) {
+		return _mixer->isSoundHandleActive(handles[id]);
+	}
+	return false;
+}
+
+// Returns whether the exception was skipped
+bool SoundManager::stopAllSounds(Common::String *except) {
+	bool ret = false;
+	for (uint i = 0; i < 20; ++i) {
+		if (except == nullptr || names[i] != *except) {
+			stopSound(i);
+		} else {
+			ret = true;
+		}
+	}
+	return ret;
+}
+
 } // End of namespace Nancy
\ No newline at end of file
diff --git a/engines/nancy/audio.h b/engines/nancy/audio.h
index fcbfbb71d5..0bc396b234 100644
--- a/engines/nancy/audio.h
+++ b/engines/nancy/audio.h
@@ -23,6 +23,11 @@
 #ifndef NANCY_AUDIO_H
 #define NANCY_AUDIO_H
 
+#include "common/types.h"
+#include "common/str.h"
+
+#include "audio/mixer.h"
+
 namespace Common {
 class SeekableReadStream;
 }
@@ -31,14 +36,31 @@ namespace Audio {
 class SeekableAudioStream;
 }
 
-namespace DisposeAfterUse {
-enum Flag;
-}
-
 namespace Nancy {
 
+class NancyEngine;
+
 Audio::SeekableAudioStream *makeHISStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse);
 
+class SoundManager {
+public:
+    enum SoundLoopType { kLoop = 0, kOneShot = 1 };
+    SoundManager(NancyEngine *engine);
+    ~SoundManager() =default;
+
+    void loadSound(Common::String &name, int16 id, uint16 numLoops = 0, uint16 volume = 60);
+    void stopSound(int16 id);
+    bool isSoundPlaying(int16 id);
+    bool stopAllSounds(Common::String *except = nullptr);
+
+private:
+    NancyEngine *_engine;
+    Audio::Mixer *_mixer;
+
+    Audio::SoundHandle handles[20];
+    Common::String names[20];
+};
+
 } // End of namespace Nancy
 
 #endif // NANCY_AUDIO_H
\ No newline at end of file
diff --git a/engines/nancy/datatypes.cpp b/engines/nancy/datatypes.cpp
index c2bd73cc79..07021d2889 100644
--- a/engines/nancy/datatypes.cpp
+++ b/engines/nancy/datatypes.cpp
@@ -42,9 +42,12 @@ SceneSummary::SceneSummary(Common::SeekableReadStream *stream) {
     stream->seek(3, SEEK_CUR);
     videoFormat = stream->readUint16LE();
 
-    stream->read(buf, 9);
+    stream->read(buf, 10);
     buf[9] = 0;
     audioFile = Common::String(buf);
+    audioID = stream->readSint16LE();
+    stream->skip(0xE);
+    audioVolume = stream->readUint16LE();
 
     stream->seek(0x72);
     verticalScrollDelta = stream->readUint16LE();
diff --git a/engines/nancy/datatypes.h b/engines/nancy/datatypes.h
index f6655c7457..e5dc8260b1 100644
--- a/engines/nancy/datatypes.h
+++ b/engines/nancy/datatypes.h
@@ -45,6 +45,8 @@ struct SceneSummary {
     //
     uint16 videoFormat;         // 0x3E, value is 1 or 2
     Common::String audioFile;   // 0x40
+    int16 audioID;              // 0x4A
+    uint16 audioVolume;         // 0x5A
     //
     uint16 verticalScrollDelta; // 0x72
     uint16 horizontalEdgeSize;  // 0x74
diff --git a/engines/nancy/logo.cpp b/engines/nancy/logo.cpp
index 90f9954357..a9d18778a6 100644
--- a/engines/nancy/logo.cpp
+++ b/engines/nancy/logo.cpp
@@ -63,15 +63,12 @@ void LogoSequence::init() {
 }
 
 void LogoSequence::startSound() {
-	Common::SeekableReadStream *mSnd = SearchMan.createReadStreamForMember(_engine->_menuSound.name + ".his");
-	if (mSnd) {
-		Audio::RewindableAudioStream *aStr = makeHISStream(mSnd, DisposeAfterUse::YES);
-		if (aStr) {
-			Audio::AudioStream *aStrLoop = Audio::makeLoopingAudioStream(aStr, 0);
-			Audio::SoundHandle handle;
-			_engine->_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &handle, aStrLoop);
-		}
-	}
+	Common::SeekableReadStream *msnd = _engine->getBootChunkStream("MSND");
+	char name[10];
+	msnd->seek(0);
+	msnd->read(name, 10);
+	Common::String sname(name);
+	_engine->sound->loadSound(sname, 0);
 
 	_startTicks = _engine->_system->getMillis();
 	_state = kRun;
diff --git a/engines/nancy/nancy.cpp b/engines/nancy/nancy.cpp
index 27a127ca78..19c9151d5b 100644
--- a/engines/nancy/nancy.cpp
+++ b/engines/nancy/nancy.cpp
@@ -29,6 +29,7 @@
 #include "engines/nancy/scene.h"
 #include "engines/nancy/graphics.h"
 #include "engines/nancy/input.h"
+#include "engines/nancy/audio.h"
 
 #include "common/system.h"
 #include "common/random.h"
@@ -77,6 +78,7 @@ NancyEngine::NancyEngine(OSystem *syst, const NancyGameDescription *gd) :
 	sceneManager = new SceneManager(this);
 	graphics = new GraphicsManager(this);
 	input = new InputManager(this);
+	sound = new SoundManager(this);
 
 	launchConsole = false;
 }
@@ -90,6 +92,7 @@ NancyEngine::~NancyEngine() {
 	delete logic;
 	delete sceneManager;
 	delete input;
+	delete sound;
 }
 
 GUI::Debugger *NancyEngine::getDebugger() {
@@ -184,7 +187,6 @@ void NancyEngine::bootGameEngine() {
 	if (!boot->load())
 		error("Failed to load boot script");
 	preloadCals(*boot);
-	readSound(*boot, "MSND", _menuSound);
 
 	addBootChunk("BSUM", boot->getChunkStream("BSUM"));
 	readBootSummary(*boot);
@@ -323,15 +325,6 @@ void NancyEngine::readImageList(const IFF &boot, const Common::String &prefix, I
 	}
 }
 
-void NancyEngine::readSound(const IFF &boot, const Common::String &name, NancyEngine::Sound &sound) {
-	Common::SeekableReadStream *stream = boot.getChunkStream(name);
-
-	if (!stream)
-		error("Failed to read BOOT %s", name.c_str());
-
-	sound.name = readFilename(stream);
-}
-
 class NancyEngine_v0 : public NancyEngine {
 public:
 	NancyEngine_v0(OSystem *syst, const NancyGameDescription *gd) : NancyEngine(syst, gd) { }
diff --git a/engines/nancy/nancy.h b/engines/nancy/nancy.h
index 847758278c..addd523e99 100644
--- a/engines/nancy/nancy.h
+++ b/engines/nancy/nancy.h
@@ -69,6 +69,7 @@ class SceneManager;
 class Logic;
 class GraphicsManager;
 class InputManager;
+class SoundManager;
 
 class NancyEngine : public Engine {
 public:
@@ -115,6 +116,7 @@ public:
 	SceneManager *sceneManager;
 	GraphicsManager *graphics;
 	InputManager *input;
+	SoundManager *sound;
 	
 	// Contains all player data
     PlayState playState;
@@ -138,10 +140,6 @@ protected:
 		uint16 height;
 	};
 
-	struct Sound {
-		Common::String name;
-	};
-
 	enum GameState {
 		kBoot,
 		kPartnerLogo, // v2 only
@@ -173,12 +171,10 @@ protected:
 	ImageList _objects;
 	uint16 _firstSceneID;
 	int32 _fontSize;
-	Sound _menuSound;
 	GameFlow _gameFlow;
 
 	void preloadCals(const IFF &boot);
 	void readImageList(const IFF &boot, const Common::String &prefix, ImageList &list);
-	void readSound(const IFF &boot, const Common::String &name, Sound &sound);
 	Common::String readFilename(Common::ReadStream *stream) const;
 
 	virtual uint getFilenameLen() const = 0;
diff --git a/engines/nancy/scene.cpp b/engines/nancy/scene.cpp
index ff1387cf3b..19b9d7f978 100644
--- a/engines/nancy/scene.cpp
+++ b/engines/nancy/scene.cpp
@@ -27,6 +27,7 @@
 #include "engines/nancy/logic.h"
 #include "engines/nancy/graphics.h"
 #include "engines/nancy/input.h"
+#include "engines/nancy/audio.h"
 
 #include "common/memstream.h"
 #include "common/rect.h"
@@ -49,8 +50,13 @@ void SceneManager::process() {
         load();
         break;
     case kStartSound:
-        // TODO
-        break;
+        // Stop all sounds, unless the new scene's background music matches the last one's;
+        // in that case, stop everything except the background music
+        if (!_engine->sound->stopAllSounds(&currentScene.audioFile)) {
+            _engine->sound->loadSound(currentScene.audioFile, currentScene.audioID, 0, currentScene.audioVolume);
+        }
+        _state = kRun;
+        // fall through
     case kRun:
         run();
         break;
@@ -202,7 +208,7 @@ void SceneManager::load() {
     _engine->playState.lastDrawnViewFrame = -1;
     _engine->input->setPointerBitmap(0, 0, false);
 
-    _state = kRun; // TODO temp, is actually StartSound
+    _state = kStartSound; // TODO temp, is actually StartSound
 }
 
 void SceneManager::run() {


Commit: 7d04efad37197fd6e621bd4ab4836fc1950ec971
    https://github.com/scummvm/scummvm/commit/7d04efad37197fd6e621bd4ab4836fc1950ec971
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Fix event flags issues

Corrected the number of event flags and added code to reset some of them on a scene change. This fixes a game crash and some repeating animations.

Changed paths:
    engines/nancy/playstate.h
    engines/nancy/scene.cpp


diff --git a/engines/nancy/playstate.h b/engines/nancy/playstate.h
index c1d2581173..7565a1c1cf 100644
--- a/engines/nancy/playstate.h
+++ b/engines/nancy/playstate.h
@@ -40,7 +40,7 @@ struct PlayState {
     Time logicConditionsTimestamps[30]; // Stores when the condition got satisfied
     
     Flag inventory[11];
-    Flag eventFlags[672];
+    Flag eventFlags[168];
     byte sceneHitCount[1000];
     uint16 difficulty; // 0, 1, 2
     Time totalTime;
diff --git a/engines/nancy/scene.cpp b/engines/nancy/scene.cpp
index 19b9d7f978..a6c972ecb5 100644
--- a/engines/nancy/scene.cpp
+++ b/engines/nancy/scene.cpp
@@ -39,6 +39,10 @@ namespace Nancy {
 
 SceneManager::~SceneManager() {
     clearSceneData();
+    _engine->graphics->_primaryFrameSurface.free();
+    _engine->graphics->_object0Surface.free();
+    _engine->graphics->_inventoryBoxIconsSurface.free();
+    _engine->graphics->_inventoryCursorsSurface.free();
 }
 
 void SceneManager::process() {
@@ -67,7 +71,7 @@ void SceneManager::process() {
 }
 
 void SceneManager::init() {
-    for (uint i = 0; i < 672; ++i) {
+    for (uint i = 0; i < 168; ++i) {
         _engine->playState.eventFlags[i] = PlayState::Flag::kFalse;
     }
 
@@ -136,6 +140,8 @@ void SceneManager::init() {
 }
 
 void SceneManager::load() {
+    clearSceneData();
+
     // Scene IDs are prefixed with S inside the cif tree; e.g 100 -> S100                                                                                    
     Common::String sceneName = Common::String::format("S%u", _sceneID);
     IFF sceneIFF(_engine, sceneName);
@@ -161,7 +167,6 @@ void SceneManager::load() {
     }
 
     // Search for Action Records, maximum for a scene is 30
-    _engine->logic->clearActionRecords();
     Common::SeekableReadStream *actionRecordChunk = nullptr;
 
     while (actionRecordChunk = sceneIFF.getChunkStream("ACT", _engine->logic->_records.size()), actionRecordChunk != nullptr)
@@ -630,11 +635,18 @@ void SceneManager::handleMouse() {
 }
 
 void SceneManager::clearSceneData() {
-    // TODO these shouldn't be here
-    _engine->graphics->_primaryFrameSurface.free();
-    _engine->graphics->_object0Surface.free();
-    _engine->graphics->_inventoryBoxIconsSurface.free();
-    _engine->graphics->_inventoryCursorsSurface.free();
+    // only clear select flags
+    for (uint i = 44; i < 54; ++i) {
+        _engine->playState.eventFlags[i] = PlayState::kFalse;
+    }
+    for (uint i = 63; i < 74; ++i) {
+        _engine->playState.eventFlags[i] = PlayState::kFalse;
+    }
+    for (uint i = 75; i < 85; ++i) {
+        _engine->playState.eventFlags[i] = PlayState::kFalse;
+    }
+
+    _engine->logic->clearActionRecords();
 }
 
 } // End of namespace Nancy
\ No newline at end of file


Commit: b9dc249d5323e73b5d57fe031cf3eada04223279
    https://github.com/scummvm/scummvm/commit/b9dc249d5323e73b5d57fe031cf3eada04223279
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add debug commands

Added debug functions to display the current scene and to teleport to a new one. Also removed old command declarations that were never defined.

Changed paths:
    engines/nancy/console.cpp
    engines/nancy/console.h


diff --git a/engines/nancy/console.cpp b/engines/nancy/console.cpp
index 96aab34fbd..0033c0a98a 100644
--- a/engines/nancy/console.cpp
+++ b/engines/nancy/console.cpp
@@ -26,6 +26,7 @@
 #include "engines/nancy/video.h"
 #include "engines/nancy/audio.h"
 #include "engines/nancy/iff.h"
+#include "engines/nancy/scene.h"
 
 #include "common/system.h"
 #include "common/events.h"
@@ -47,6 +48,8 @@ NancyConsole::NancyConsole(NancyEngine *vm) : GUI::Debugger(), _vm(vm) {
 	registerCmd("show_image", WRAP_METHOD(NancyConsole, Cmd_showImage));
 	registerCmd("play_video", WRAP_METHOD(NancyConsole, Cmd_playVideo));
 	registerCmd("play_audio", WRAP_METHOD(NancyConsole, Cmd_playAudio));
+	registerCmd("load_scene", WRAP_METHOD(NancyConsole, Cmd_loadScene));
+	registerCmd("scene_id", WRAP_METHOD(NancyConsole, Cmd_sceneID));
 }
 
 NancyConsole::~NancyConsole() {
@@ -279,4 +282,41 @@ bool NancyConsole::Cmd_playAudio(int argc, const char **argv) {
 	return true;
 }
 
+bool NancyConsole::Cmd_loadScene(int argc, const char **argv) {
+	if (argc != 2) {
+		debugPrintf("Loads a scene\n");
+		debugPrintf("Usage: %s sceneID\n", argv[0]);
+		return true;
+	}
+	
+	if (_vm->_gameFlow.minGameState != NancyEngine::GameState::kScene) {
+		debugPrintf("Not in the kScene state\n");
+		return true;
+	}
+
+	Common::String sceneName = Common::String::format("S%s", argv[1]);
+    IFF iff(_vm, sceneName);
+	if (!iff.load()) {
+		debugPrintf("Invalid scene S%s\n", argv[1]);
+		return true;
+	}
+
+	_vm->sceneManager->_sceneID = (uint16)atoi(argv[1]);
+	_vm->playState.queuedViewFrame = 0;
+	_vm->playState.queuedMaxVerticalScroll = 0;
+	_vm->sceneManager->doNotStartSound = false;
+	_vm->sceneManager->_state = SceneManager::kLoadNew;
+	return cmdExit(0, 0);
+}
+
+bool NancyConsole::Cmd_sceneID(int argc, const char **argv) {
+	if (_vm->_gameFlow.minGameState != NancyEngine::GameState::kScene) {
+		debugPrintf("Not in the kScene state\n");
+		return true;
+	}
+
+	debugPrintf("Scene: %u, Frame: %i \n", _vm->sceneManager->_sceneID, _vm->playState.currentViewFrame);
+	return true;
+}
+
 } // End of namespace Nancy
\ No newline at end of file
diff --git a/engines/nancy/console.h b/engines/nancy/console.h
index 8b6201c33a..f56ef0d40f 100644
--- a/engines/nancy/console.h
+++ b/engines/nancy/console.h
@@ -48,13 +48,8 @@ private:
 	bool Cmd_showImage(int argc, const char **argv);
 	bool Cmd_playVideo(int argc, const char **argv);
 	bool Cmd_playAudio(int argc, const char **argv);
-
-	bool Cmd_listScreens(int argc, const char **argv);
-	bool Cmd_listObjects(int argc, const char **argv);
-	bool Cmd_getObject(int argc, const char **argv);
-	bool Cmd_getAllObjects(int argc, const char **argv);
-	bool Cmd_gotoScreen(int argc, const char **argv);
-	bool Cmd_boundaries(int argc, const char **argv);
+	bool Cmd_loadScene(int argc, const char **argv);
+	bool Cmd_sceneID(int argc, const char **argv);
 
 	Common::String _videoFile;
 };


Commit: b2af943ad028d8cafd8dfea16b55d54cddbb7a61
    https://github.com/scummvm/scummvm/commit/b2af943ad028d8cafd8dfea16b55d54cddbb7a61
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Fix game crash

Fixed a game crash in PlayDigiSoundAndDie.

Changed paths:
    engines/nancy/action/recordtypes.cpp


diff --git a/engines/nancy/action/recordtypes.cpp b/engines/nancy/action/recordtypes.cpp
index bf4ede3b4d..8175b04c07 100644
--- a/engines/nancy/action/recordtypes.cpp
+++ b/engines/nancy/action/recordtypes.cpp
@@ -45,7 +45,7 @@ uint16 SceneChange::readData(Common::SeekableReadStream &stream) {
     frameID = stream.readUint16LE();
     verticalOffset = stream.readUint16LE();
     doNotStartSound = (bool)(stream.readUint16LE());
-    return 8; // TODO
+    return 8;
 }
 
 void SceneChange::execute(NancyEngine *engine) {
@@ -499,7 +499,9 @@ void PlayDigiSoundAndDie::execute(NancyEngine *engine){
             }
             break;
         case kEnd:
-            SceneChange::execute(engine);
+            if (sceneID != 9999) {
+                SceneChange::execute(engine);
+            }
             break;
     }
 }


Commit: 9184dd01bde75ceafb68603feec3137314161fb0
    https://github.com/scummvm/scummvm/commit/9184dd01bde75ceafb68603feec3137314161fb0
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Rename enum value

Rename ActionRecord::ExecutionState::kRun to kActionTrigger to better match original engine's naming convention.

Changed paths:
    engines/nancy/action/actionrecord.h
    engines/nancy/action/recordtypes.cpp
    engines/nancy/scene.cpp


diff --git a/engines/nancy/action/actionrecord.h b/engines/nancy/action/actionrecord.h
index 3d90c528da..b46666ad0c 100644
--- a/engines/nancy/action/actionrecord.h
+++ b/engines/nancy/action/actionrecord.h
@@ -63,7 +63,7 @@ struct DependencyRecord {
 
 class ActionRecord {
 public:
-    enum ExecutionState { kBegin, kRun, kEnd };
+    enum ExecutionState { kBegin, kRun, kActionTrigger };
     ActionRecord() :
         type(0),
         execType(0),
diff --git a/engines/nancy/action/recordtypes.cpp b/engines/nancy/action/recordtypes.cpp
index 8175b04c07..21789f86d1 100644
--- a/engines/nancy/action/recordtypes.cpp
+++ b/engines/nancy/action/recordtypes.cpp
@@ -85,7 +85,7 @@ void HotMultiframeSceneChange::execute(NancyEngine *engine) {
                 }
             }
             break;
-        case kEnd:
+        case kActionTrigger:
             SceneChange::execute(engine);
             break;
     }
@@ -110,7 +110,7 @@ void Hot1FrSceneChange::execute(NancyEngine *engine) {
                 hasHotspot = false;
             }
             break;
-        case kEnd:
+        case kActionTrigger:
             SceneChange::execute(engine);
             break;
     }
@@ -384,7 +384,7 @@ void EventFlagsMultiHS::execute(NancyEngine *engine) {
                 }
             }
             break;
-        case kEnd:
+        case kActionTrigger:
             hasHotspot = false;
             EventFlags::execute(engine);
             break;
@@ -495,10 +495,10 @@ void PlayDigiSoundAndDie::execute(NancyEngine *engine){
             break;
         case kRun:
             if (!engine->sound->isSoundPlaying(id)) {
-                state = kEnd;
+                state = kActionTrigger;
             }
             break;
-        case kEnd:
+        case kActionTrigger:
             if (sceneID != 9999) {
                 SceneChange::execute(engine);
             }
diff --git a/engines/nancy/scene.cpp b/engines/nancy/scene.cpp
index a6c972ecb5..825daf9a4d 100644
--- a/engines/nancy/scene.cpp
+++ b/engines/nancy/scene.cpp
@@ -428,7 +428,7 @@ void SceneManager::run() {
         ActionRecord *rec = _engine->logic->getActionRecord(hovered);
         if (rec->isActive /*&& another condition !- 0*/) {
             // TODO item holding logic
-            rec->state = ActionRecord::ExecutionState::kEnd;
+            rec->state = ActionRecord::ExecutionState::kActionTrigger;
         }
 
 


Commit: f683d414f9c84215cbe472667f6b5c16b9f668f9
    https://github.com/scummvm/scummvm/commit/f683d414f9c84215cbe472667f6b5c16b9f668f9
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Fix scene change sound behavior

Replaced the hacky code that kept the backgound music alive between scene changes with the proper behavior from the engine.

Changed paths:
    engines/nancy/audio.cpp
    engines/nancy/audio.h
    engines/nancy/scene.cpp


diff --git a/engines/nancy/audio.cpp b/engines/nancy/audio.cpp
index 6c36bf0881..b0e04098ac 100644
--- a/engines/nancy/audio.cpp
+++ b/engines/nancy/audio.cpp
@@ -212,16 +212,8 @@ bool SoundManager::isSoundPlaying(int16 id) {
 }
 
 // Returns whether the exception was skipped
-bool SoundManager::stopAllSounds(Common::String *except) {
-	bool ret = false;
-	for (uint i = 0; i < 20; ++i) {
-		if (except == nullptr || names[i] != *except) {
-			stopSound(i);
-		} else {
-			ret = true;
-		}
-	}
-	return ret;
+void SoundManager::stopAllSounds() {
+	_mixer->stopAll();
 }
 
 } // End of namespace Nancy
\ No newline at end of file
diff --git a/engines/nancy/audio.h b/engines/nancy/audio.h
index 0bc396b234..b647dab313 100644
--- a/engines/nancy/audio.h
+++ b/engines/nancy/audio.h
@@ -51,7 +51,7 @@ public:
     void loadSound(Common::String &name, int16 id, uint16 numLoops = 0, uint16 volume = 60);
     void stopSound(int16 id);
     bool isSoundPlaying(int16 id);
-    bool stopAllSounds(Common::String *except = nullptr);
+    void stopAllSounds();
 
 private:
     NancyEngine *_engine;
diff --git a/engines/nancy/scene.cpp b/engines/nancy/scene.cpp
index 825daf9a4d..bccc6b3214 100644
--- a/engines/nancy/scene.cpp
+++ b/engines/nancy/scene.cpp
@@ -56,10 +56,14 @@ void SceneManager::process() {
     case kStartSound:
         // Stop all sounds, unless the new scene's background music matches the last one's;
         // in that case, stop everything except the background music
-        if (!_engine->sound->stopAllSounds(&currentScene.audioFile)) {
+        /*if (!_engine->sound->stopAllSounds(&currentScene.audioFile)) {
+            
+        }*/
+        _state = kRun;
+        if (!doNotStartSound) {
+            _engine->sound->stopAllSounds();
             _engine->sound->loadSound(currentScene.audioFile, currentScene.audioID, 0, currentScene.audioVolume);
         }
-        _state = kRun;
         // fall through
     case kRun:
         run();


Commit: da461f1d6e719bb7e52d2658d28a7cd21b948f74
    https://github.com/scummvm/scummvm/commit/da461f1d6e719bb7e52d2658d28a7cd21b948f74
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Upgrade video code

Added code to make AVF videos seekable. Not tested on video type 2, but will almost definitely break it. Also updated class definitions to properly mark virtual methods.

Changed paths:
    engines/nancy/video.cpp
    engines/nancy/video.h


diff --git a/engines/nancy/video.cpp b/engines/nancy/video.cpp
index b31ab6035c..9f4fa453e0 100644
--- a/engines/nancy/video.cpp
+++ b/engines/nancy/video.cpp
@@ -116,6 +116,12 @@ AVFDecoder::AVFVideoTrack::~AVFVideoTrack() {
 	delete _dec;
 }
 
+bool AVFDecoder::AVFVideoTrack::seek(const Audio::Timestamp &time) {
+	// TODO this will almost definitely break video type 2
+	_curFrame = getFrameAtTime(time);
+	return true;
+}
+
 bool AVFDecoder::AVFVideoTrack::decode(byte *outBuf, uint32 frameSize, Common::ReadStream &inBuf) const {
 	byte cmd = inBuf.readByte();
 	while (!inBuf.eos()) {
diff --git a/engines/nancy/video.h b/engines/nancy/video.h
index 937656f4dc..85bc85fcc5 100644
--- a/engines/nancy/video.h
+++ b/engines/nancy/video.h
@@ -44,22 +44,24 @@ class AVFDecoder : public Video::VideoDecoder {
 public:
 	virtual ~AVFDecoder();
 
-	bool loadStream(Common::SeekableReadStream *stream);
+	virtual bool loadStream(Common::SeekableReadStream *stream) override;
 	const Graphics::Surface *decodeFrame(uint frameNr);
 
 private:
 	class AVFVideoTrack : public FixedRateVideoTrack {
 	public:
 		AVFVideoTrack(Common::SeekableReadStream *stream);
-		~AVFVideoTrack();
-
-		uint16 getWidth() const { return _width; }
-		uint16 getHeight() const { return _height; }
-		Graphics::PixelFormat getPixelFormat() const { return _pixelFormat; }
-		int getCurFrame() const { return _curFrame; }
-		int getFrameCount() const { return _frameCount; }
+		virtual ~AVFVideoTrack();
+
+		virtual uint16 getWidth() const override { return _width; }
+		virtual uint16 getHeight() const override { return _height; }
+		virtual Graphics::PixelFormat getPixelFormat() const override { return _pixelFormat; }
+		virtual int getCurFrame() const override { return _curFrame; }
+		virtual int getFrameCount() const override { return _frameCount; }
+		virtual bool isSeekable() const override { return true; }
+		virtual bool seek(const Audio::Timestamp &time);
+		virtual const Graphics::Surface *decodeNextFrame() override;
 		const Graphics::Surface *decodeFrame(uint frameNr);
-		const Graphics::Surface *decodeNextFrame();
 
 	protected:
 		Common::Rational getFrameRate() const { return Common::Rational(1000, _frameTime); }


Commit: 75ba5e6528c69cf2ed9f17378dbb89ee409ab473
    https://github.com/scummvm/scummvm/commit/75ba5e6528c69cf2ed9f17378dbb89ee409ab473
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add reverse video playback

Implemented reverse video playback for AVF video. Just like last commit, this is not tested on video type 2 and will almost definitely break.

Changed paths:
    engines/nancy/video.cpp
    engines/nancy/video.h


diff --git a/engines/nancy/video.cpp b/engines/nancy/video.cpp
index 9f4fa453e0..b9a9437012 100644
--- a/engines/nancy/video.cpp
+++ b/engines/nancy/video.cpp
@@ -77,6 +77,7 @@ AVFDecoder::AVFVideoTrack::AVFVideoTrack(Common::SeekableReadStream *stream) {
 	_fileStream = stream;
 	_curFrame = -1;
 	_refFrame = -1;
+	_reversed = false;
 	_dec = new Decompressor;
 
 	stream->skip(1); // Unknown
@@ -122,6 +123,18 @@ bool AVFDecoder::AVFVideoTrack::seek(const Audio::Timestamp &time) {
 	return true;
 }
 
+bool AVFDecoder::AVFVideoTrack::setReverse(bool reverse) {
+	_reversed = reverse;
+	return true;
+}
+
+bool AVFDecoder::AVFVideoTrack::endOfTrack() const {
+	if (_reversed)
+		return _curFrame < 0;
+
+	return _curFrame >= (getFrameCount() - 1);
+}
+
 bool AVFDecoder::AVFVideoTrack::decode(byte *outBuf, uint32 frameSize, Common::ReadStream &inBuf) const {
 	byte cmd = inBuf.readByte();
 	while (!inBuf.eos()) {
@@ -224,7 +237,7 @@ const Graphics::Surface *AVFDecoder::AVFVideoTrack::decodeFrame(uint frameNr) {
 }
 
 const Graphics::Surface *AVFDecoder::AVFVideoTrack::decodeNextFrame() {
-	return decodeFrame(++_curFrame);
+	return decodeFrame(_reversed ? _curFrame-- : ++_curFrame);
 }
 
 } // End of namespace Nancy
\ No newline at end of file
diff --git a/engines/nancy/video.h b/engines/nancy/video.h
index 85bc85fcc5..1296773a06 100644
--- a/engines/nancy/video.h
+++ b/engines/nancy/video.h
@@ -60,6 +60,9 @@ private:
 		virtual int getFrameCount() const override { return _frameCount; }
 		virtual bool isSeekable() const override { return true; }
 		virtual bool seek(const Audio::Timestamp &time);
+		virtual bool setReverse(bool reverse) override;
+		virtual bool isReversed() const override { return _reversed; }
+		virtual bool endOfTrack() const override;
 		virtual const Graphics::Surface *decodeNextFrame() override;
 		const Graphics::Surface *decodeFrame(uint frameNr);
 
@@ -87,6 +90,7 @@ private:
 		int _refFrame;
 		Common::Array<ChunkInfo> _chunkInfo;
 		Decompressor *_dec;
+		bool _reversed;
 	};
 };
 


Commit: 46341dde11197dab40b29c7bc069c4f4b4164f3f
    https://github.com/scummvm/scummvm/commit/46341dde11197dab40b29c7bc069c4f4b4164f3f
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Implement secondary video

Implemented the two SecondaryVideo channels, allowing the game to display NPC animations outside of dialogue. The implementation repeats some frames and plays at the wrong speed so further work is needed.

Changed paths:
    engines/nancy/action/recordtypes.cpp
    engines/nancy/action/recordtypes.h
    engines/nancy/graphics.cpp
    engines/nancy/graphics.h
    engines/nancy/scene.cpp


diff --git a/engines/nancy/action/recordtypes.cpp b/engines/nancy/action/recordtypes.cpp
index 21789f86d1..a93ee36e4e 100644
--- a/engines/nancy/action/recordtypes.cpp
+++ b/engines/nancy/action/recordtypes.cpp
@@ -27,6 +27,7 @@
 #include "engines/nancy/nancy.h"
 #include "engines/nancy/graphics.h"
 #include "engines/nancy/audio.h"
+#include "engines/nancy/input.h"
 
 #include "common/str.h"
 
@@ -188,11 +189,132 @@ uint16 PlayPrimaryVideoChan0::readData(Common::SeekableReadStream &stream) {
 }
 
 uint16 PlaySecondaryVideo::readData(Common::SeekableReadStream &stream) {
-    stream.seek(0x33, SEEK_CUR);
-    int16 size = stream.readSint16LE() * 0x42 + 0x35;
-    stream.seek(-0x35, SEEK_CUR);
+    char buf[10];
+    stream.read(buf, 10);
+    filename = Common::String(buf);
+    stream.skip(0x14);
+    loopFirstFrame = stream.readUint16LE();
+    loopLastFrame = stream.readUint16LE();
+    onHoverFirstFrame = stream.readUint16LE();
+    onHoverLastFrame = stream.readUint16LE();
+    onHoverEndFirstFrame = stream.readUint16LE();
+    onHoverEndLastFrame = stream.readUint16LE();
+    SceneChange::readData(stream);
+    stream.skip(1);
 
-    return readRaw(stream, size); // TODO
+    uint16 numVideoDescs = stream.readUint16LE();
+    for (uint i = 0; i < numVideoDescs; ++i) {
+        videoDescs.push_back(SecondaryVideoDesc());
+        SecondaryVideoDesc &cur = videoDescs[i];
+        cur.frameID = stream.readSint16LE();
+        cur.srcRect.left = stream.readUint32LE();
+        cur.srcRect.top = stream.readUint32LE();
+        cur.srcRect.right = stream.readUint32LE();
+        cur.srcRect.bottom = stream.readUint32LE();
+        cur.destRect.left = stream.readUint32LE();
+        cur.destRect.top = stream.readUint32LE();
+        cur.destRect.right = stream.readUint32LE();
+        cur.destRect.bottom = stream.readUint32LE();
+        stream.skip(0x20);
+    }
+
+    return 0x35 + (numVideoDescs * 0x42);
+}
+
+void PlaySecondaryVideo::execute(NancyEngine *engine) {
+    switch (state) {
+        case kBegin:
+            engine->graphics->loadSecondaryVideo(channelID(), filename, this);
+            engine->graphics->setupSecondaryVideo(channelID(), loopFirstFrame, loopLastFrame, true);
+            state = kRun;
+            // fall through
+        case kRun: {
+            ZRenderStruct &zr = engine->graphics->getZRenderStruct("SEC VIDEO 0");
+            zr.isActive = false;
+            hasHotspot = false;
+
+            uint activeFrame = 0;
+
+            for (uint i = 0; i < videoDescs.size(); ++i) {
+                if (videoDescs[i].frameID == engine->playState.currentViewFrame) {
+                    activeFrame = i;
+                }
+            }
+
+            if (activeFrame) {
+                // Activate the ZRenderStruct
+                zr.sourceRect = videoDescs[activeFrame].srcRect;
+                zr.destRect = videoDescs[activeFrame].destRect;
+                zr.destRect.left += engine->graphics->viewportDesc.destination.left;
+                zr.destRect.top += engine->graphics->viewportDesc.destination.top;
+                zr.destRect.top -= engine->playState.verticalScroll;
+                zr.destRect.right += engine->graphics->viewportDesc.destination.left;
+                zr.destRect.bottom += engine->graphics->viewportDesc.destination.top;
+                zr.destRect.bottom -= engine->playState.verticalScroll;
+                zr.isActive = true;
+
+                // Activate the hotspot
+                hotspot = videoDescs[activeFrame].destRect;
+                hasHotspot = true;
+
+                // check if we're hovered this frame
+                bool isHovered = engine->logic->getActionRecord(engine->input->hoveredElementID) == this;
+
+                switch (hoverState) {
+                    case kEndHoverDone:
+                        hoverState = kNoHover;
+                        engine->graphics->setupSecondaryVideo(channelID(), loopFirstFrame, loopLastFrame, true);
+                        break;
+                    case kNoHover:
+                        if (isHovered) {
+                            // Player has just hovered over, play the hover animation once
+                            hoverState = kHover;
+                            engine->graphics->setupSecondaryVideo(channelID(), onHoverFirstFrame, onHoverLastFrame, false);
+                        }
+                        break;
+                    case kHover:
+                        if (!isHovered) {
+                            // Player has just stopped hovering, reverse the playback and go back to frame 0
+                            hoverState = kEndHover;
+                            engine->graphics->setupSecondaryVideo(channelID(), onHoverEndLastFrame, onHoverEndFirstFrame, false);
+                        }
+                        break;
+                    case kEndHover:
+                        break;
+                }
+
+                /*// The reverse playback of the whole animation ended, go back to regular loop
+                if (hoverAnimationEnded) {
+                    hoverAnimationEnded = false;
+                    engine->graphics->setupSecondaryVideo(channelID(), loopFirstFrame, loopLastFrame, true);
+                } else {
+                    bool isHovered = engine->logic->getActionRecord(engine->input->hoveredElementID) == this;
+                    if (!wasHovered) {
+                        if (isHovered) {
+                            // Player has just hovered over, play the hover animation once
+                            wasHovered = true;
+                            engine->graphics->setupSecondaryVideo(channelID(), onHoverFirstFrame, onHoverLastFrame, false);
+                        }
+                    } else {
+                        if (!isHovered) {
+                            // Player has just stopped hovering, reverse the playback and go back to frame 0
+                            wasHovered = false;
+                            engine->graphics->setupSecondaryVideo(channelID(), onHoverEndLastFrame, onHoverEndFirstFrame, false);
+                        }
+                    }
+                }*/
+
+                engine->graphics->playSecondaryVideo(channelID());
+            } else {
+                engine->graphics->stopSecondaryVideo(channelID());
+            }
+
+            break;
+        }
+        case kActionTrigger:
+            SceneChange::execute(engine);
+            break;
+    }
 }
 
 uint16 PlaySecondaryMovie::readData(Common::SeekableReadStream &stream) {
diff --git a/engines/nancy/action/recordtypes.h b/engines/nancy/action/recordtypes.h
index b5af7eb436..99d8a99e15 100644
--- a/engines/nancy/action/recordtypes.h
+++ b/engines/nancy/action/recordtypes.h
@@ -112,17 +112,44 @@ public:
 };
 
 // Base class for PlaySecondaryVideoChan0 and PlaySecondaryVideoChan1
-class PlaySecondaryVideo : public ActionRecord {
+class PlaySecondaryVideo : public SceneChange {
 public:
+    struct SecondaryVideoDesc {
+        int16 frameID;
+        Common::Rect srcRect;
+        Common::Rect destRect;
+        // 2 unknown/empty rects
+    };
+
+    enum HoverState { kNoHover, kHover, kEndHover, kEndHoverDone };
+
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void execute(NancyEngine *engine) override;
+
+    virtual uint channelID() =0;
+
+    Common::String filename;
+    //...
+    uint16 loopFirstFrame = 0; // 0x1E
+    uint16 loopLastFrame = 0; // 0x20
+    uint16 onHoverFirstFrame = 0; // 0x22
+    uint16 onHoverLastFrame = 0; // 0x24
+    uint16 onHoverEndFirstFrame = 0; // 0x26
+    uint16 onHoverEndLastFrame = 0; // 0x28
+    // SceneChange data is at 0x2A
+    // unknown byte
+    Common::Array<SecondaryVideoDesc> videoDescs; // 0x35
+
+    // not present in original data
+    HoverState hoverState = kNoHover;
 };
 
 class PlaySecondaryVideoChan0 : public PlaySecondaryVideo {
-    // TODO
+    virtual uint channelID() override { return 0; }
 };
 
 class PlaySecondaryVideoChan1 : public PlaySecondaryVideo {
-    // TODO
+    virtual uint channelID() override { return 1; }
 };
 
 class PlaySecondaryMovie : public ActionRecord {
diff --git a/engines/nancy/graphics.cpp b/engines/nancy/graphics.cpp
index 8269d5bf67..73e5481185 100644
--- a/engines/nancy/graphics.cpp
+++ b/engines/nancy/graphics.cpp
@@ -21,6 +21,8 @@
  */
 
 #include "engines/nancy/graphics.h"
+#include "engines/nancy/resource.h"
+#include "engines/nancy/action/recordtypes.h"
 
 #include "common/error.h"
 #include "common/system.h"
@@ -65,6 +67,11 @@ GraphicsManager::~GraphicsManager() {
     _background.free();
     _frameTextBox.free();
     _screen.free();
+    _primaryFrameSurface.free();
+    _object0Surface.free();
+    _inventoryBoxIconsSurface.free();
+    _inventoryCursorsSurface.free();
+    _object0Surface.free();
 
     for (auto st : _ZRender) {
         delete st._value.renderFunction;
@@ -164,10 +171,8 @@ void GraphicsManager::initSceneZRenderStructs() {
     initZRenderStruct(  "INV BITMAP", 9, false, ZRenderStruct::BltType::kNoTrans);
     initZRenderStruct(  "PRIMARY VIDEO", 8, false, ZRenderStruct::BltType::kNoTrans, nullptr,
                         new RenderFunction(this, &GraphicsManager::renderPrimaryVideo));
-    initZRenderStruct(  "SEC VIDEO 0", 8, false, ZRenderStruct::BltType::kTrans, nullptr,
-                        new RenderFunction(this, &GraphicsManager::renderSecVideo0));
-    initZRenderStruct(  "SEC VIDEO 1", 8, false, ZRenderStruct::BltType::kTrans, nullptr,
-                        new RenderFunction(this, &GraphicsManager::renderSecVideo1));
+    initZRenderStruct(  "SEC VIDEO 0", 8, false, ZRenderStruct::BltType::kTrans, &channels[0].surf);
+    initZRenderStruct(  "SEC VIDEO 1", 8, false, ZRenderStruct::BltType::kTrans, &channels[1].surf);
     initZRenderStruct(  "SEC MOVIE", 8, false, ZRenderStruct::BltType::kNoTrans, nullptr,
                         new RenderFunction(this, &GraphicsManager::renderSecMovie));
     initZRenderStruct(  "ORDERING PUZZLE", 7, false, ZRenderStruct::BltType::kNoTrans, nullptr,
@@ -264,6 +269,68 @@ uint32 GraphicsManager::getBackgroundHeight() {
     return _videoDecoder.getHeight();
 }
 
+void GraphicsManager::loadSecondaryVideo(uint channel, Common::String &filename, PlaySecondaryVideo *record) {
+    AVFDecoder &decoder = channels[channel].decoder;
+    if (decoder.isVideoLoaded()) {
+        decoder.close();
+    }
+    decoder.loadFile(filename + ".avf");
+    channels[channel].record = record;
+}
+
+void GraphicsManager::setupSecondaryVideo(uint channel, uint16 begin, uint16 end, bool loop) {
+    channels[channel].beginFrame = begin;
+    channels[channel].endFrame = end;
+    channels[channel].loop = loop;
+    channels[channel].decoder.seekToFrame(begin);
+}
+
+void GraphicsManager::playSecondaryVideo(uint channel) {
+    AVFDecoder &decoder = channels[channel].decoder;
+    VideoChannel &chan = channels[channel];
+    if (!decoder.isVideoLoaded()) {
+        return;
+    }
+
+    // toggle between normal and reverse playback if needed
+    bool isReversed = chan.endFrame < chan.beginFrame;
+    bool wasReversed = decoder.getRate() < 0;
+    if (isReversed != wasReversed) {
+        decoder.setRate(-decoder.getRate());
+    }
+
+    if (!decoder.isPlaying()) {
+        decoder.start();
+        decoder.seekToFrame(chan.beginFrame);
+        
+        chan.surf.w = decoder.getWidth();
+        chan.surf.h = decoder.getHeight();
+        chan.surf.format = decoder.getPixelFormat();
+    }
+
+
+    if (decoder.needsUpdate()) {
+        const Graphics::Surface *fr = nullptr;
+        fr = decoder.decodeNextFrame();
+        chan.surf = *fr;
+    }
+    
+    // TODO loop is choppy and repeats a frame
+    if (decoder.getCurFrame() == chan.endFrame || decoder.endOfVideo()) {
+        if (chan.record->hoverState == PlaySecondaryVideo::kEndHover) {
+            chan.record->hoverState = PlaySecondaryVideo::kEndHoverDone;
+        }
+
+        if (chan.loop) {
+            decoder.seekToFrame(chan.beginFrame);
+        }
+    }
+}
+
+void GraphicsManager::stopSecondaryVideo(uint channel) {
+    channels[channel].decoder.stop();
+}
+
 void GraphicsManager::renderFrame() {
     ZRenderStruct &zr = getZRenderStruct("FRAME");
     Common::Point dest(zr.destRect.left, zr.destRect.top);
diff --git a/engines/nancy/graphics.h b/engines/nancy/graphics.h
index 00fd1338c8..07c61ea338 100644
--- a/engines/nancy/graphics.h
+++ b/engines/nancy/graphics.h
@@ -33,6 +33,8 @@
 
 namespace Nancy {
 
+class PlaySecondaryVideo;
+
 typedef Common::Functor0Mem<void, GraphicsManager> RenderFunction;
 
 struct ZRenderStruct {
@@ -50,6 +52,15 @@ public:
     Common::String name;
 };
 
+struct VideoChannel {
+        Graphics::Surface surf;
+        uint16 beginFrame = 0;
+        uint16 endFrame = 0;
+        bool loop = false;
+        PlaySecondaryVideo *record = nullptr;
+        AVFDecoder decoder;
+};
+
 class GraphicsManager {
 public:
     GraphicsManager(NancyEngine *engine);
@@ -80,6 +91,11 @@ public:
     uint32 getBackgroundWidth();
     uint32 getBackgroundHeight();
 
+    void loadSecondaryVideo(uint channel, Common::String &filename, PlaySecondaryVideo *record);
+    void setupSecondaryVideo(uint channel, uint16 begin, uint16 end, bool loop);
+    void playSecondaryVideo(uint channel);
+    void stopSecondaryVideo(uint channel);
+
     Graphics::Surface _background;
     Graphics::Surface _frameTextBox;
     Graphics::Surface _primaryFrameSurface;
@@ -87,6 +103,9 @@ public:
     Graphics::Surface _inventoryBoxIconsSurface;
     Graphics::Surface _inventoryCursorsSurface;
 
+    // move these to their own struct
+    VideoChannel channels[2];
+
     View viewportDesc;
 
     static const Graphics::PixelFormat pixelFormat;
diff --git a/engines/nancy/scene.cpp b/engines/nancy/scene.cpp
index bccc6b3214..689b817429 100644
--- a/engines/nancy/scene.cpp
+++ b/engines/nancy/scene.cpp
@@ -39,10 +39,6 @@ namespace Nancy {
 
 SceneManager::~SceneManager() {
     clearSceneData();
-    _engine->graphics->_primaryFrameSurface.free();
-    _engine->graphics->_object0Surface.free();
-    _engine->graphics->_inventoryBoxIconsSurface.free();
-    _engine->graphics->_inventoryCursorsSurface.free();
 }
 
 void SceneManager::process() {
@@ -54,11 +50,6 @@ void SceneManager::process() {
         load();
         break;
     case kStartSound:
-        // Stop all sounds, unless the new scene's background music matches the last one's;
-        // in that case, stop everything except the background music
-        /*if (!_engine->sound->stopAllSounds(&currentScene.audioFile)) {
-            
-        }*/
         _state = kRun;
         if (!doNotStartSound) {
             _engine->sound->stopAllSounds();
@@ -85,8 +76,6 @@ void SceneManager::init() {
 
     _sceneID = _engine->_firstSceneID;
     _engine->_gameFlow.previousGameState = NancyEngine::kScene;
-
-    // TODO init action records
     
     _engine->graphics->clearZRenderStructs();
 
@@ -217,7 +206,7 @@ void SceneManager::load() {
     _engine->playState.lastDrawnViewFrame = -1;
     _engine->input->setPointerBitmap(0, 0, false);
 
-    _state = kStartSound; // TODO temp, is actually StartSound
+    _state = kStartSound;
 }
 
 void SceneManager::run() {
@@ -651,6 +640,9 @@ void SceneManager::clearSceneData() {
     }
 
     _engine->logic->clearActionRecords();
+
+    _engine->graphics->getZRenderStruct("SEC VIDEO 0").isActive = false;
+    _engine->graphics->getZRenderStruct("SEC VIDEO 1").isActive = false;
 }
 
 } // End of namespace Nancy
\ No newline at end of file


Commit: cd66fa975d2b057c351b88e2b96c2aa955a767e3
    https://github.com/scummvm/scummvm/commit/cd66fa975d2b057c351b88e2b96c2aa955a767e3
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Correct video timing

Added a potentially hacky fix for the wrong timings in video playback.

Changed paths:
    engines/nancy/video.cpp


diff --git a/engines/nancy/video.cpp b/engines/nancy/video.cpp
index b9a9437012..43feb6e28a 100644
--- a/engines/nancy/video.cpp
+++ b/engines/nancy/video.cpp
@@ -88,6 +88,11 @@ AVFDecoder::AVFVideoTrack::AVFVideoTrack(Common::SeekableReadStream *stream) {
 	_depth = stream->readByte();
 	_frameTime = stream->readUint32LE();
 
+	// TODO hack(?)
+	// Nancy1 consistently adds 12 ms between every frame (for secondary videos). I'm not sure
+	// where exactly it happens, so for now just manually add them to the frame time
+	_frameTime += 12;
+
 	byte comp = stream->readByte();
 
 	if (comp != 2)
@@ -98,6 +103,7 @@ AVFDecoder::AVFVideoTrack::AVFVideoTrack(Common::SeekableReadStream *stream) {
 	_surface->create(_width, _height, _pixelFormat);
 	_frameSize = _width * _height * _pixelFormat.bytesPerPixel;
 
+
 	for (uint i = 0; i < _frameCount; i++) {
 		ChunkInfo info;
 		info.index = stream->readUint16LE();


Commit: b4315872b5aa030041285ac234fca6d02b2d8c3b
    https://github.com/scummvm/scummvm/commit/b4315872b5aa030041285ac234fca6d02b2d8c3b
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Fix sigtraps when closing

Fixed two issues that caused a sigtrap when closing ScummVM.

Changed paths:
    engines/nancy/datatypes.cpp
    engines/nancy/datatypes.h
    engines/nancy/scene.cpp
    engines/nancy/scene.h


diff --git a/engines/nancy/datatypes.cpp b/engines/nancy/datatypes.cpp
index 07021d2889..1f854782cb 100644
--- a/engines/nancy/datatypes.cpp
+++ b/engines/nancy/datatypes.cpp
@@ -57,11 +57,6 @@ SceneSummary::SceneSummary(Common::SeekableReadStream *stream) {
     fastMoveTimeDelta = stream->readUint16LE();
     unknown7C = stream->readByte();
 
-    // put the entire chunk into a temp buffer until we figure out the rest of the structure
-    stream->seek(0);
-    chunkData = new byte[stream->size()];
-    stream->read(chunkData, stream->size());
-
     delete[] buf;
 }
 
diff --git a/engines/nancy/datatypes.h b/engines/nancy/datatypes.h
index e5dc8260b1..e5b4c74c50 100644
--- a/engines/nancy/datatypes.h
+++ b/engines/nancy/datatypes.h
@@ -36,9 +36,8 @@ namespace Nancy {
 
 // Describes a scene
 struct SceneSummary {
-    SceneSummary() { chunkData = nullptr; }
+    SceneSummary() =default;
     SceneSummary(Common::SeekableReadStream *stream);
-    ~SceneSummary() { delete[] chunkData; }
 
     Common::String description; // 0x00
     Common::String videoFile;   // 0x32
@@ -55,7 +54,6 @@ struct SceneSummary {
     uint16 fastMoveTimeDelta;   // 0x7A
     byte unknown7C;             // 0x7C, enum with 4 values
     //
-    byte *chunkData;
 };
 
 // Describes the viewport
diff --git a/engines/nancy/scene.cpp b/engines/nancy/scene.cpp
index 689b817429..9559fd48bf 100644
--- a/engines/nancy/scene.cpp
+++ b/engines/nancy/scene.cpp
@@ -37,10 +37,6 @@
 
 namespace Nancy {
 
-SceneManager::~SceneManager() {
-    clearSceneData();
-}
-
 void SceneManager::process() {
     switch (_state) {
     case kInit:
diff --git a/engines/nancy/scene.h b/engines/nancy/scene.h
index 62b663dcfd..05c4866081 100644
--- a/engines/nancy/scene.h
+++ b/engines/nancy/scene.h
@@ -50,7 +50,6 @@ public:
         _state {kInit},
         _sceneID {0},
         movementDirection{0} { }
-    ~SceneManager();
 
     void process();
 


Commit: 2a6555fe3646c1aee32cef88deb29d1a6d97e886
    https://github.com/scummvm/scummvm/commit/2a6555fe3646c1aee32cef88deb29d1a6d97e886
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Partially implement bitmap animations

Implemented PlayIntStaticBitmapAnimation, which allows the teacher's room PC screensaver to display properly. This impementation skips the sound component, which will be added later if needed.

Changed paths:
    engines/nancy/action/recordtypes.cpp
    engines/nancy/action/recordtypes.h
    engines/nancy/graphics.cpp
    engines/nancy/graphics.h
    engines/nancy/scene.cpp


diff --git a/engines/nancy/action/recordtypes.cpp b/engines/nancy/action/recordtypes.cpp
index a93ee36e4e..80599e8c5e 100644
--- a/engines/nancy/action/recordtypes.cpp
+++ b/engines/nancy/action/recordtypes.cpp
@@ -28,17 +28,23 @@
 #include "engines/nancy/graphics.h"
 #include "engines/nancy/audio.h"
 #include "engines/nancy/input.h"
+#include "engines/nancy/resource.h"
 
 #include "common/str.h"
 
 namespace Nancy {
 
+// Simple helper function to read rectangles
+static void readRect(Common::SeekableReadStream &stream, Common::Rect &inRect) {
+    inRect.left = stream.readUint32LE();
+    inRect.top = stream.readUint32LE();
+    inRect.right = stream.readUint32LE();
+    inRect.bottom = stream.readUint32LE();
+}
+
 void HotspotDesc::readData(Common::SeekableReadStream &stream) {
     frameID = stream.readUint16LE();
-    coords.left = stream.readUint32LE();
-    coords.top = stream.readUint32LE();
-    coords.right = stream.readUint32LE();
-    coords.bottom = stream.readUint32LE();
+    readRect(stream, coords);
 }
 
 uint16 SceneChange::readData(Common::SeekableReadStream &stream) {
@@ -207,14 +213,8 @@ uint16 PlaySecondaryVideo::readData(Common::SeekableReadStream &stream) {
         videoDescs.push_back(SecondaryVideoDesc());
         SecondaryVideoDesc &cur = videoDescs[i];
         cur.frameID = stream.readSint16LE();
-        cur.srcRect.left = stream.readUint32LE();
-        cur.srcRect.top = stream.readUint32LE();
-        cur.srcRect.right = stream.readUint32LE();
-        cur.srcRect.bottom = stream.readUint32LE();
-        cur.destRect.left = stream.readUint32LE();
-        cur.destRect.top = stream.readUint32LE();
-        cur.destRect.right = stream.readUint32LE();
-        cur.destRect.bottom = stream.readUint32LE();
+        readRect(stream, cur.srcRect);
+        readRect(stream, cur.destRect);
         stream.skip(0x20);
     }
 
@@ -349,7 +349,50 @@ uint16 PlayStaticBitmapAnimation::readData(Common::SeekableReadStream &stream) {
 }
 
 uint16 PlayIntStaticBitmapAnimation::readData(Common::SeekableReadStream &stream) {
-    // TODO
+    uint beginOffset = stream.pos();
+    char name[10];
+    stream.read(name, 10);
+    imageName = Common::String(name);
+
+    stream.skip(0xA);
+    firstFrame = stream.readUint16LE();
+    stream.skip(2);
+    lastFrame = stream.readUint16LE();
+    frameTime = Common::Rational(1000, stream.readUint16LE()).toInt();
+    stream.skip(2);
+    soundFlagDesc.label = stream.readSint16LE();
+    soundFlagDesc.flag = (PlayState::Flag)stream.readUint16LE();
+
+    SceneChange::readData(stream);
+
+    for (uint i = 0; i < 10; ++i) {
+        triggerFlagDescs[i].label = stream.readSint16LE();
+        triggerFlagDescs[i].flag = (PlayState::Flag)stream.readUint16LE();
+    }
+
+    stream.read(name, 10);
+    soundName = Common::String(name);
+    channelID = stream.readUint16LE();
+
+    stream.seek(beginOffset + 0x74, SEEK_SET);
+    uint numFrames = stream.readUint16LE();
+
+    for (uint i = firstFrame; i <= lastFrame; ++i) {
+        frameRects.push_back(Common::Rect());
+        readRect(stream, frameRects[i]);
+    }
+
+    for (uint i = 0; i < numFrames; ++i) {
+        srcDestRects.push_back(SrcDestDesc());
+        SrcDestDesc &rects = srcDestRects[i];
+        rects.frameId = stream.readUint16LE();
+        readRect(stream, rects.src);
+        readRect(stream, rects.dest);
+    }
+
+    return 0x76 + numFrames * 0x22 + (lastFrame - firstFrame + 1) * 16;
+
+    /* TODO
     uint16 bytesRead = stream.pos();
     byte *seek = bitmapData;
     
@@ -368,7 +411,97 @@ uint16 PlayIntStaticBitmapAnimation::readData(Common::SeekableReadStream &stream
     stream.read(seek, currentSize);
 
     bytesRead= stream.pos() - bytesRead;
-    return bytesRead;
+    return bytesRead;*/
+}
+
+void PlayIntStaticBitmapAnimation::execute(NancyEngine *engine) {
+    // TODO handle sound, event flags
+    ZRenderStruct &zr = engine->graphics->getZRenderStruct("STATIC BITMAP ANIMATION");
+    uint32 currentFrameTime = engine->getTotalPlayTime();
+    switch (state) {
+        case kBegin:
+
+            // find the correct source and destination for the current viewport frame
+            lastViewFrame = engine->playState.currentViewFrame;
+            for (uint i = 0; i < srcDestRects.size(); ++i) {
+                if (lastViewFrame == srcDestRects[i].frameId) {
+                    currentViewFrameID = i;
+                    break;
+                }
+            }
+
+            currentFrame = firstFrame;
+            nextFrameTime = currentFrameTime + frameTime;
+
+            // Load the image directly into the generic surface
+            // instead of inside the record; this also means we skip
+            // using the source rect in srcDestRect.
+            // This _may_ lead to problems (but probably won't)
+            zr.sourceSurface->free();
+            engine->_res->loadImage("ciftree", imageName, *zr.sourceSurface);
+
+            // Set up the ZRenderStruct and its surface
+            zr.sourceRect = frameRects[currentFrame - firstFrame];
+            if (currentViewFrameID != -1) {
+                zr.isActive = true;
+                zr.destRect = srcDestRects[currentViewFrameID].dest;
+                zr.destRect.left += engine->graphics->viewportDesc.destination.left;
+                zr.destRect.top += engine->graphics->viewportDesc.destination.top;
+                zr.destRect.top -= engine->playState.verticalScroll;
+                zr.destRect.right += engine->graphics->viewportDesc.destination.left;
+                zr.destRect.bottom += engine->graphics->viewportDesc.destination.top;
+                zr.destRect.bottom -= engine->playState.verticalScroll;
+            }
+
+            if (soundName != "NO SOUND") {
+                warning("PlayIntStaticBitmapAnimation has a sound, please implement it!");
+            }
+            state = kRun;
+            // fall through
+        case kRun:
+            // Check if we've moved the viewport
+            if (lastViewFrame != engine->playState.currentViewFrame) {
+                lastViewFrame = engine->playState.currentViewFrame;
+                currentViewFrameID = -1;
+                for (uint i = 0; i < srcDestRects.size(); ++i) {
+                    if (lastViewFrame == srcDestRects[i].frameId) {
+                        currentViewFrameID = i;
+                        zr.isActive = true;
+                        zr.destRect = srcDestRects[currentViewFrameID].dest;
+                        zr.destRect.left += engine->graphics->viewportDesc.destination.left;
+                        zr.destRect.top += engine->graphics->viewportDesc.destination.top;
+                        zr.destRect.top -= engine->playState.verticalScroll;
+                        zr.destRect.right += engine->graphics->viewportDesc.destination.left;
+                        zr.destRect.bottom += engine->graphics->viewportDesc.destination.top;
+                        zr.destRect.bottom -= engine->playState.verticalScroll;
+                        break;
+                    }
+                }
+            }
+
+            if (currentViewFrameID == -1) {
+                break;
+            }
+
+            // Check the timer to see if we need to draw the next animation frame
+            if (nextFrameTime <= currentFrameTime) {
+                nextFrameTime = currentFrameTime + frameTime;
+
+                currentFrame = ++currentFrame > lastFrame ? firstFrame : currentFrame;
+
+                zr.sourceRect = frameRects[currentFrame - firstFrame];
+            }
+            break;
+        case kActionTrigger:
+            for (uint i = 0; i < 10; ++i) {
+                if (triggerFlagDescs[i].label != -1) {
+                    engine->playState.eventFlags[triggerFlagDescs[i].label] = triggerFlagDescs[i].flag;
+                }
+            }
+
+            SceneChange::execute(engine);
+            break;
+    }
 }
 
 uint16 MapCall::readData(Common::SeekableReadStream &stream) {
diff --git a/engines/nancy/action/recordtypes.h b/engines/nancy/action/recordtypes.h
index 99d8a99e15..cb18e46622 100644
--- a/engines/nancy/action/recordtypes.h
+++ b/engines/nancy/action/recordtypes.h
@@ -41,6 +41,12 @@ struct HotspotDesc {
     void readData(Common::SeekableReadStream &stream);
 };
 
+// Describes an event flag change or comparison
+struct FlagDesc {
+    int16 label;
+    PlayState::Flag flag;
+};
+
 class SceneChange : public ActionRecord {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
@@ -164,12 +170,39 @@ public:
     byte bitmapData[0xA88];
 };
 
-// TODO should inherit from above as the only difference is one int of data
-class PlayIntStaticBitmapAnimation : public ActionRecord {
+class PlayIntStaticBitmapAnimation : public SceneChange {
+// TODO this effectively also contains an EventFlags, consider multiple inheritance
+// or maybe splitting EventFlags into a separate struct
 public:
+    struct SrcDestDesc {
+        uint16 frameId = 0;
+        Common::Rect src;
+        Common::Rect dest;
+    };
+
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void execute(NancyEngine *engine) override;
 
-    byte bitmapData[0xA8C];
+    Common::String imageName;
+    uint16 firstFrame;
+    uint16 lastFrame;
+    FlagDesc soundFlagDesc;
+    FlagDesc triggerFlagDescs[10];
+    Time frameTime;
+
+    Common::String soundName;
+    uint16 channelID;
+
+    // Describes a single frame in this animation
+    Common::Array<Common::Rect> frameRects;
+    // Describes how the animation will be displayed on a single
+    // frame of the viewport
+    Common::Array<SrcDestDesc> srcDestRects;
+
+    uint16 currentFrame = 0;
+    uint16 lastViewFrame = 0;
+    int16 currentViewFrameID = -1;
+    Time nextFrameTime;
 };
 
 class MapCall : public ActionRecord {
@@ -263,10 +296,6 @@ public:
 
 class EventFlags : public ActionRecord {
 public:
-    struct FlagDesc {
-        int16 label;
-        PlayState::Flag flag;
-    };
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
     virtual void execute(NancyEngine *engine) override;
 
diff --git a/engines/nancy/graphics.cpp b/engines/nancy/graphics.cpp
index 73e5481185..9f84c3ed42 100644
--- a/engines/nancy/graphics.cpp
+++ b/engines/nancy/graphics.cpp
@@ -72,6 +72,7 @@ GraphicsManager::~GraphicsManager() {
     _inventoryBoxIconsSurface.free();
     _inventoryCursorsSurface.free();
     _object0Surface.free();
+    _genericSurface.free();
 
     for (auto st : _ZRender) {
         delete st._value.renderFunction;
@@ -191,6 +192,10 @@ void GraphicsManager::initSceneZRenderStructs() {
     // Moved here from SceneManager::load(), should be ok
     initZRenderStruct(  "VIEWPORT AVF", 6, true, ZRenderStruct::BltType::kNoTrans,
                         &_background, nullptr, &viewportDesc.source, &viewportDesc.destination);
+
+    // Moved from PlayIntStaticBitmap
+    initZRenderStruct(  "STATIC BITMAP ANIMATION", 7, false, ZRenderStruct::BltType::kNoTrans,
+                        &_genericSurface);
     #undef READ_RECT
 
     delete source;
diff --git a/engines/nancy/graphics.h b/engines/nancy/graphics.h
index 07c61ea338..31001f5c7e 100644
--- a/engines/nancy/graphics.h
+++ b/engines/nancy/graphics.h
@@ -102,6 +102,7 @@ public:
     Graphics::Surface _object0Surface;
     Graphics::Surface _inventoryBoxIconsSurface;
     Graphics::Surface _inventoryCursorsSurface;
+    Graphics::Surface _genericSurface;
 
     // move these to their own struct
     VideoChannel channels[2];
diff --git a/engines/nancy/scene.cpp b/engines/nancy/scene.cpp
index 9559fd48bf..57f0e5cb01 100644
--- a/engines/nancy/scene.cpp
+++ b/engines/nancy/scene.cpp
@@ -639,6 +639,7 @@ void SceneManager::clearSceneData() {
 
     _engine->graphics->getZRenderStruct("SEC VIDEO 0").isActive = false;
     _engine->graphics->getZRenderStruct("SEC VIDEO 1").isActive = false;
+    _engine->graphics->getZRenderStruct("STATIC BITMAP ANIMATION").isActive = false;
 }
 
 } // End of namespace Nancy
\ No newline at end of file


Commit: b642ca3b4d51c9e1ccb962fef770776b513cde52
    https://github.com/scummvm/scummvm/commit/b642ca3b4d51c9e1ccb962fef770776b513cde52
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Implement secondary movie

Implemented the PlaySecondaryMovie action record, which is used in some death scenes. Also made some minor changes to a few other action records and the sound manager.

Changed paths:
    engines/nancy/action/recordtypes.cpp
    engines/nancy/action/recordtypes.h
    engines/nancy/audio.cpp
    engines/nancy/audio.h
    engines/nancy/graphics.cpp
    engines/nancy/graphics.h
    engines/nancy/logo.cpp
    engines/nancy/scene.cpp


diff --git a/engines/nancy/action/recordtypes.cpp b/engines/nancy/action/recordtypes.cpp
index 80599e8c5e..4dd06fd2d4 100644
--- a/engines/nancy/action/recordtypes.cpp
+++ b/engines/nancy/action/recordtypes.cpp
@@ -47,6 +47,28 @@ void HotspotDesc::readData(Common::SeekableReadStream &stream) {
     readRect(stream, coords);
 }
 
+void SecondaryVideoDesc::readData(Common::SeekableReadStream &stream) {
+    frameID = stream.readUint16LE();
+    readRect(stream, srcRect);
+    readRect(stream, destRect);
+    stream.skip(0x20);
+}
+
+void EventFlagsDesc::readData(Common::SeekableReadStream &stream) {
+    for (uint i = 0; i < 10; ++i) {
+        descs[i].label = stream.readSint16LE();
+        descs[i].flag = (PlayState::Flag)stream.readUint16LE();
+    }
+}
+
+void EventFlagsDesc::execute(NancyEngine *engine) {
+    for (uint i = 0; i < 10; ++i) {
+        if (descs[i].label != -1) {
+            engine->playState.eventFlags[descs[i].label] = descs[i].flag;
+        }
+    }
+}
+
 uint16 SceneChange::readData(Common::SeekableReadStream &stream) {
     sceneID = stream.readUint16LE();
     frameID = stream.readUint16LE();
@@ -211,11 +233,7 @@ uint16 PlaySecondaryVideo::readData(Common::SeekableReadStream &stream) {
     uint16 numVideoDescs = stream.readUint16LE();
     for (uint i = 0; i < numVideoDescs; ++i) {
         videoDescs.push_back(SecondaryVideoDesc());
-        SecondaryVideoDesc &cur = videoDescs[i];
-        cur.frameID = stream.readSint16LE();
-        readRect(stream, cur.srcRect);
-        readRect(stream, cur.destRect);
-        stream.skip(0x20);
+        videoDescs[i].readData(stream);
     }
 
     return 0x35 + (numVideoDescs * 0x42);
@@ -233,7 +251,7 @@ void PlaySecondaryVideo::execute(NancyEngine *engine) {
             zr.isActive = false;
             hasHotspot = false;
 
-            uint activeFrame = 0;
+            int activeFrame = -1;
 
             for (uint i = 0; i < videoDescs.size(); ++i) {
                 if (videoDescs[i].frameID == engine->playState.currentViewFrame) {
@@ -241,7 +259,7 @@ void PlaySecondaryVideo::execute(NancyEngine *engine) {
                 }
             }
 
-            if (activeFrame) {
+            if (activeFrame != -1) {
                 // Activate the ZRenderStruct
                 zr.sourceRect = videoDescs[activeFrame].srcRect;
                 zr.destRect = videoDescs[activeFrame].destRect;
@@ -283,27 +301,6 @@ void PlaySecondaryVideo::execute(NancyEngine *engine) {
                         break;
                 }
 
-                /*// The reverse playback of the whole animation ended, go back to regular loop
-                if (hoverAnimationEnded) {
-                    hoverAnimationEnded = false;
-                    engine->graphics->setupSecondaryVideo(channelID(), loopFirstFrame, loopLastFrame, true);
-                } else {
-                    bool isHovered = engine->logic->getActionRecord(engine->input->hoveredElementID) == this;
-                    if (!wasHovered) {
-                        if (isHovered) {
-                            // Player has just hovered over, play the hover animation once
-                            wasHovered = true;
-                            engine->graphics->setupSecondaryVideo(channelID(), onHoverFirstFrame, onHoverLastFrame, false);
-                        }
-                    } else {
-                        if (!isHovered) {
-                            // Player has just stopped hovering, reverse the playback and go back to frame 0
-                            wasHovered = false;
-                            engine->graphics->setupSecondaryVideo(channelID(), onHoverEndLastFrame, onHoverEndFirstFrame, false);
-                        }
-                    }
-                }*/
-
                 engine->graphics->playSecondaryVideo(channelID());
             } else {
                 engine->graphics->stopSecondaryVideo(channelID());
@@ -317,12 +314,95 @@ void PlaySecondaryVideo::execute(NancyEngine *engine) {
     }
 }
 
-uint16 PlaySecondaryMovie::readData(Common::SeekableReadStream &stream) {
-    stream.seek(0xD2, SEEK_CUR);
-    uint16 size = stream.readUint16LE() * 0x42 + 0xD4;
-    stream.seek(-0xD4, SEEK_CUR);
+uint16 PlaySecondaryMovie::readData(Common::SeekableReadStream &stream) {  
+    char name[10];
+    stream.read(name, 10);
+    videoName = Common::String(name);
 
-    return readRaw(stream, size); // TODO
+    stream.skip(0x1C);
+    for (uint i = 0; i < 15; ++i) {
+        frameFlags[i].frameID = stream.readSint16LE();
+        frameFlags[i].flagDesc.label = stream.readSint16LE();
+        frameFlags[i].flagDesc.flag = (PlayState::Flag)stream.readUint16LE();
+    }
+
+    triggerFlags.readData(stream);
+    stream.read(name, 10);
+    soundName = Common::String(name);
+    soundChannel = stream.readUint16LE();
+    stream.skip(0xE);
+    soundVolume = stream.readUint16LE();
+    
+    stream.skip(6);
+    SceneChange::readData(stream);
+
+    uint16 numVideoDescs = stream.readUint16LE();
+    for (uint i = 0; i < numVideoDescs; ++i) {
+        videoDescs.push_back(SecondaryVideoDesc());
+        videoDescs[i].readData(stream);
+    }
+
+    return 0xD4 + numVideoDescs * 0x42; // TODO
+}
+
+void PlaySecondaryMovie::execute(NancyEngine *engine) {
+    ZRenderStruct &zr = engine->graphics->getZRenderStruct("SEC MOVIE");
+
+    switch (state) {
+        case kBegin:
+            engine->graphics->loadSecondaryMovie(videoName);
+            if (soundName != "NO SOUND") {
+                engine->sound->loadSound(soundName, soundChannel, 1, soundVolume);
+            }
+            state = kRun;
+            // fall through
+        case kRun: {
+            zr.isActive = false;
+            engine->graphics->getZRenderStruct("CUR IMAGE CURSOR").isActive = false;
+            
+            int activeFrame = -1;
+
+            for (uint i = 0; i < videoDescs.size(); ++i) {
+                if (videoDescs[i].frameID == engine->playState.currentViewFrame) {
+                    activeFrame = i;
+                }
+            }
+
+            if (activeFrame != -1) {
+                // Activate the ZRenderStruct
+                zr.sourceRect = videoDescs[activeFrame].srcRect;
+                zr.destRect = videoDescs[activeFrame].destRect;
+                zr.destRect.left += engine->graphics->viewportDesc.destination.left;
+                zr.destRect.top += engine->graphics->viewportDesc.destination.top;
+                zr.destRect.top -= engine->playState.verticalScroll;
+                zr.destRect.right += engine->graphics->viewportDesc.destination.left;
+                zr.destRect.bottom += engine->graphics->viewportDesc.destination.top;
+                zr.destRect.bottom -= engine->playState.verticalScroll;
+                zr.isActive = true;
+
+                // Start sound if any
+                if (soundName != "NO SOUND") {
+                    engine->sound->pauseSound(soundChannel, false);
+                }
+            }
+
+            uint16 frame = 0;
+            if (!engine->graphics->playSecondaryMovie(frame)) {
+                // No new frame, check for flags
+                for (uint i = 0; i < 16; ++i) {
+                    if (frameFlags[i].frameID == frame) {
+                        engine->playState.eventFlags[frameFlags[i].flagDesc.label] = frameFlags[i].flagDesc.flag;
+                    }
+                }
+            }
+
+            break;
+        }
+        case kActionTrigger:
+            triggerFlags.execute(engine);
+            SceneChange::execute(engine);
+            break;
+    }
 }
 
 uint16 PlayStaticBitmapAnimation::readData(Common::SeekableReadStream &stream) {
@@ -365,10 +445,7 @@ uint16 PlayIntStaticBitmapAnimation::readData(Common::SeekableReadStream &stream
 
     SceneChange::readData(stream);
 
-    for (uint i = 0; i < 10; ++i) {
-        triggerFlagDescs[i].label = stream.readSint16LE();
-        triggerFlagDescs[i].flag = (PlayState::Flag)stream.readUint16LE();
-    }
+    triggerFlags.readData(stream);
 
     stream.read(name, 10);
     soundName = Common::String(name);
@@ -391,27 +468,6 @@ uint16 PlayIntStaticBitmapAnimation::readData(Common::SeekableReadStream &stream
     }
 
     return 0x76 + numFrames * 0x22 + (lastFrame - firstFrame + 1) * 16;
-
-    /* TODO
-    uint16 bytesRead = stream.pos();
-    byte *seek = bitmapData;
-    
-    uint16 currentSize = 0x76;
-    stream.read(seek, currentSize);
-
-    seek += currentSize;
-    currentSize = (uint16)(bitmapData[0x18]) - (uint16)(bitmapData[0x16]);
-    ++currentSize;
-    currentSize *= 16;
-    stream.read(seek, currentSize);
-
-    seek += 0x256;
-    currentSize = (uint16)(bitmapData[0x74]);
-    currentSize *= 34;
-    stream.read(seek, currentSize);
-
-    bytesRead= stream.pos() - bytesRead;
-    return bytesRead;*/
 }
 
 void PlayIntStaticBitmapAnimation::execute(NancyEngine *engine) {
@@ -493,11 +549,7 @@ void PlayIntStaticBitmapAnimation::execute(NancyEngine *engine) {
             }
             break;
         case kActionTrigger:
-            for (uint i = 0; i < 10; ++i) {
-                if (triggerFlagDescs[i].label != -1) {
-                    engine->playState.eventFlags[triggerFlagDescs[i].label] = triggerFlagDescs[i].flag;
-                }
-            }
+            triggerFlags.execute(engine);
 
             SceneChange::execute(engine);
             break;
@@ -595,19 +647,12 @@ void StopTimer::execute(NancyEngine *engine) {
 }
 
 uint16 EventFlags::readData(Common::SeekableReadStream &stream) {
-    for (uint i = 0; i < 10; ++i) {
-        descs[i].label = stream.readSint16LE();
-        descs[i].flag = (PlayState::Flag)(stream.readUint16LE());
-    }
+    flags.readData(stream);
     return 0x28;
 }
 
 void EventFlags::execute(NancyEngine *engine) {
-    for (uint i = 0; i < 10; ++i) {
-        if (descs[i].label != -1) {
-            engine->playState.eventFlags[descs[i].label] = descs[i].flag;
-        }
-    }
+    flags.execute(engine);
     isDone = true;
 }
 
@@ -746,6 +791,7 @@ void PlayDigiSoundAndDie::execute(NancyEngine *engine){
     switch (state) {
         case kBegin:
             engine->sound->loadSound(filename, id, numLoops, volume);
+            engine->sound->pauseSound(id, false);
             state = kRun;
             break;
         case kRun:
diff --git a/engines/nancy/action/recordtypes.h b/engines/nancy/action/recordtypes.h
index cb18e46622..5d276f4e0b 100644
--- a/engines/nancy/action/recordtypes.h
+++ b/engines/nancy/action/recordtypes.h
@@ -41,12 +41,30 @@ struct HotspotDesc {
     void readData(Common::SeekableReadStream &stream);
 };
 
-// Describes an event flag change or comparison
+// Describes a secondary video/movie's source and destination
+struct SecondaryVideoDesc {
+    int16 frameID;
+    Common::Rect srcRect;
+    Common::Rect destRect;
+    // 2 unknown/empty rects
+
+    void readData(Common::SeekableReadStream &stream);
+};
+
+// Describes a single event flag change or comparison
 struct FlagDesc {
     int16 label;
     PlayState::Flag flag;
 };
 
+// Describes 10 event flag changes to be executed when an action is triggered
+struct EventFlagsDesc {
+    FlagDesc descs[10];
+
+    void readData(Common::SeekableReadStream &stream);
+    void execute(NancyEngine *engine);
+};
+
 class SceneChange : public ActionRecord {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
@@ -120,12 +138,6 @@ public:
 // Base class for PlaySecondaryVideoChan0 and PlaySecondaryVideoChan1
 class PlaySecondaryVideo : public SceneChange {
 public:
-    struct SecondaryVideoDesc {
-        int16 frameID;
-        Common::Rect srcRect;
-        Common::Rect destRect;
-        // 2 unknown/empty rects
-    };
 
     enum HoverState { kNoHover, kHover, kEndHover, kEndHoverDone };
 
@@ -158,9 +170,29 @@ class PlaySecondaryVideoChan1 : public PlaySecondaryVideo {
     virtual uint channelID() override { return 1; }
 };
 
-class PlaySecondaryMovie : public ActionRecord {
+class PlaySecondaryMovie : public SceneChange {
 public:
+    struct FlagAtFrame {
+        int16 frameID;
+        FlagDesc flagDesc;
+    };
+
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void execute(NancyEngine *engine) override;
+
+    Common::String videoName; // 0x00
+
+    FlagAtFrame frameFlags[15]; // 0x26
+    EventFlagsDesc triggerFlags; // 0x80
+
+    Common::String soundName; // 0xA8
+    uint16 soundChannel = 0; // 0xB2
+    uint16 soundVolume = 0; // 0xC2
+
+    // SceneChange data at 0xCA
+    Common::Array<SecondaryVideoDesc> videoDescs; // 0xD4
+
+
 };
 
 class PlayStaticBitmapAnimation : public ActionRecord {
@@ -187,9 +219,10 @@ public:
     uint16 firstFrame;
     uint16 lastFrame;
     FlagDesc soundFlagDesc;
-    FlagDesc triggerFlagDescs[10];
+    EventFlagsDesc triggerFlags;
     Time frameTime;
 
+    // Todo
     Common::String soundName;
     uint16 channelID;
 
@@ -299,7 +332,7 @@ public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
     virtual void execute(NancyEngine *engine) override;
 
-    FlagDesc descs[10];
+    EventFlagsDesc flags;
 };
 
 class EventFlagsMultiHS : public EventFlags {
@@ -402,7 +435,7 @@ public:
     Common::String filename;
     int16 id = -1; // 0xA
     uint16 numLoops = 0; // 0x10
-    uint16 volume = 0; // 0x16, between 0 and 60
+    uint16 volume = 0; // 0x16, maximum is 65?
     // ...
     // SceneChange elements at 0x1E
 };
diff --git a/engines/nancy/audio.cpp b/engines/nancy/audio.cpp
index b0e04098ac..c9c6ecc3f7 100644
--- a/engines/nancy/audio.cpp
+++ b/engines/nancy/audio.cpp
@@ -181,7 +181,7 @@ SoundManager::SoundManager(NancyEngine *engine) :
 	_mixer = _engine->_system->getMixer();
 }
 
-// Combine load and play until i find a reason not to
+
 void SoundManager::loadSound(Common::String &name, int16 id, uint16 numLoops, uint16 volume) {
 	if (_mixer->isSoundHandleActive(handles[id])) {
 		_mixer->stopHandle(handles[id]);
@@ -191,12 +191,20 @@ void SoundManager::loadSound(Common::String &name, int16 id, uint16 numLoops, ui
 		Audio::RewindableAudioStream *aStr = makeHISStream(mSnd, DisposeAfterUse::YES);
 		if (aStr) {
 			Audio::AudioStream *aStrLoop = Audio::makeLoopingAudioStream(aStr, numLoops);
-			_engine->_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &handles[id], aStrLoop, -1, volume * 255 / 60);
+			_engine->_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &handles[id], aStrLoop, -1, volume * 255 / 64);
+			_engine->_system->getMixer()->pauseHandle(handles[id], true);
 			names[id] = name;
 		}
 	}
 }
 
+void SoundManager::pauseSound(int16 id, bool pause) {
+	if (id < 0 || id > 20)
+		return;
+
+	_engine->_system->getMixer()->pauseHandle(handles[id], pause);
+}
+
 void SoundManager::stopSound(int16 id) {
 	if (isSoundPlaying(id)) {
 		_mixer->stopHandle(handles[id]);
diff --git a/engines/nancy/audio.h b/engines/nancy/audio.h
index b647dab313..a19980b499 100644
--- a/engines/nancy/audio.h
+++ b/engines/nancy/audio.h
@@ -49,6 +49,7 @@ public:
     ~SoundManager() =default;
 
     void loadSound(Common::String &name, int16 id, uint16 numLoops = 0, uint16 volume = 60);
+    void pauseSound(int16 id, bool pause);
     void stopSound(int16 id);
     bool isSoundPlaying(int16 id);
     void stopAllSounds();
diff --git a/engines/nancy/graphics.cpp b/engines/nancy/graphics.cpp
index 9f84c3ed42..064c197619 100644
--- a/engines/nancy/graphics.cpp
+++ b/engines/nancy/graphics.cpp
@@ -74,6 +74,10 @@ GraphicsManager::~GraphicsManager() {
     _object0Surface.free();
     _genericSurface.free();
 
+    _secMovieSurface.free();
+    _secMovieDecoder.close();
+
+
     for (auto st : _ZRender) {
         delete st._value.renderFunction;
     }
@@ -174,8 +178,7 @@ void GraphicsManager::initSceneZRenderStructs() {
                         new RenderFunction(this, &GraphicsManager::renderPrimaryVideo));
     initZRenderStruct(  "SEC VIDEO 0", 8, false, ZRenderStruct::BltType::kTrans, &channels[0].surf);
     initZRenderStruct(  "SEC VIDEO 1", 8, false, ZRenderStruct::BltType::kTrans, &channels[1].surf);
-    initZRenderStruct(  "SEC MOVIE", 8, false, ZRenderStruct::BltType::kNoTrans, nullptr,
-                        new RenderFunction(this, &GraphicsManager::renderSecMovie));
+    initZRenderStruct(  "SEC MOVIE", 8, false, ZRenderStruct::BltType::kNoTrans, &_secMovieSurface);
     initZRenderStruct(  "ORDERING PUZZLE", 7, false, ZRenderStruct::BltType::kNoTrans, nullptr,
                         new RenderFunction(this, &GraphicsManager::renderOrderingPuzzle));
     initZRenderStruct(  "ROTATING LOCK PUZZLE", 7, false, ZRenderStruct::BltType::kNoTrans, nullptr,
@@ -315,9 +318,7 @@ void GraphicsManager::playSecondaryVideo(uint channel) {
 
 
     if (decoder.needsUpdate()) {
-        const Graphics::Surface *fr = nullptr;
-        fr = decoder.decodeNextFrame();
-        chan.surf = *fr;
+        chan.surf = *decoder.decodeNextFrame();
     }
     
     // TODO loop is choppy and repeats a frame
@@ -336,6 +337,31 @@ void GraphicsManager::stopSecondaryVideo(uint channel) {
     channels[channel].decoder.stop();
 }
 
+void GraphicsManager::loadSecondaryMovie(Common::String &filename) {
+    if (_secMovieDecoder.isVideoLoaded()) {
+        _secMovieDecoder.close();
+    }
+    _secMovieDecoder.loadFile(filename + ".avf");
+}
+
+bool GraphicsManager::playSecondaryMovie(uint16 &outFrameNr) {
+    if (!_secMovieDecoder.isPlaying()) {
+        _secMovieDecoder.start();
+        
+        _secMovieSurface.w = _secMovieDecoder.getWidth();
+        _secMovieSurface.h = _secMovieDecoder.getHeight();
+        _secMovieSurface.format = _secMovieDecoder.getPixelFormat();
+    }
+
+    outFrameNr = _secMovieDecoder.getCurFrame();
+    if (_secMovieDecoder.needsUpdate()) {
+        _secMovieSurface = *_secMovieDecoder.decodeNextFrame();
+        return true;
+    }
+
+    return false;
+}
+
 void GraphicsManager::renderFrame() {
     ZRenderStruct &zr = getZRenderStruct("FRAME");
     Common::Point dest(zr.destRect.left, zr.destRect.top);
diff --git a/engines/nancy/graphics.h b/engines/nancy/graphics.h
index 31001f5c7e..1c98bb499f 100644
--- a/engines/nancy/graphics.h
+++ b/engines/nancy/graphics.h
@@ -59,6 +59,8 @@ struct VideoChannel {
         bool loop = false;
         PlaySecondaryVideo *record = nullptr;
         AVFDecoder decoder;
+
+        ~VideoChannel() { surf.free(); decoder.close(); }
 };
 
 class GraphicsManager {
@@ -96,6 +98,9 @@ public:
     void playSecondaryVideo(uint channel);
     void stopSecondaryVideo(uint channel);
 
+    void loadSecondaryMovie(Common::String &filename);
+    bool playSecondaryMovie(uint16 &outFrameNr);
+
     Graphics::Surface _background;
     Graphics::Surface _frameTextBox;
     Graphics::Surface _primaryFrameSurface;
@@ -104,8 +109,9 @@ public:
     Graphics::Surface _inventoryCursorsSurface;
     Graphics::Surface _genericSurface;
 
-    // move these to their own struct
     VideoChannel channels[2];
+    Graphics::Surface _secMovieSurface;
+    AVFDecoder _secMovieDecoder;
 
     View viewportDesc;
 
diff --git a/engines/nancy/logo.cpp b/engines/nancy/logo.cpp
index a9d18778a6..c751e4dd85 100644
--- a/engines/nancy/logo.cpp
+++ b/engines/nancy/logo.cpp
@@ -69,6 +69,7 @@ void LogoSequence::startSound() {
 	msnd->read(name, 10);
 	Common::String sname(name);
 	_engine->sound->loadSound(sname, 0);
+	_engine->sound->pauseSound(0, false);
 
 	_startTicks = _engine->_system->getMillis();
 	_state = kRun;
diff --git a/engines/nancy/scene.cpp b/engines/nancy/scene.cpp
index 57f0e5cb01..04005307a3 100644
--- a/engines/nancy/scene.cpp
+++ b/engines/nancy/scene.cpp
@@ -50,6 +50,7 @@ void SceneManager::process() {
         if (!doNotStartSound) {
             _engine->sound->stopAllSounds();
             _engine->sound->loadSound(currentScene.audioFile, currentScene.audioID, 0, currentScene.audioVolume);
+            _engine->sound->pauseSound(currentScene.audioID, false);
         }
         // fall through
     case kRun:
@@ -639,6 +640,7 @@ void SceneManager::clearSceneData() {
 
     _engine->graphics->getZRenderStruct("SEC VIDEO 0").isActive = false;
     _engine->graphics->getZRenderStruct("SEC VIDEO 1").isActive = false;
+    _engine->graphics->getZRenderStruct("SEC MOVIE").isActive = false;
     _engine->graphics->getZRenderStruct("STATIC BITMAP ANIMATION").isActive = false;
 }
 


Commit: 9152deeac76a9fe6feb2de4ca250add5adb4b656
    https://github.com/scummvm/scummvm/commit/9152deeac76a9fe6feb2de4ca250add5adb4b656
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Implement map

Implemented the map state, as well as the two main action record types that call it. Also made some minor related changes to the scene and graphics managers.

Changed paths:
  A engines/nancy/map.cpp
  A engines/nancy/map.h
    engines/nancy/action/recordtypes.cpp
    engines/nancy/action/recordtypes.h
    engines/nancy/graphics.cpp
    engines/nancy/graphics.h
    engines/nancy/module.mk
    engines/nancy/nancy.cpp
    engines/nancy/nancy.h
    engines/nancy/scene.cpp
    engines/nancy/scene.h


diff --git a/engines/nancy/action/recordtypes.cpp b/engines/nancy/action/recordtypes.cpp
index 4dd06fd2d4..c14cc1ea80 100644
--- a/engines/nancy/action/recordtypes.cpp
+++ b/engines/nancy/action/recordtypes.cpp
@@ -78,11 +78,7 @@ uint16 SceneChange::readData(Common::SeekableReadStream &stream) {
 }
 
 void SceneChange::execute(NancyEngine *engine) {
-    engine->sceneManager->_sceneID = sceneID;
-    engine->playState.queuedViewFrame = frameID;
-    engine->playState.queuedMaxVerticalScroll = verticalOffset;
-    engine->sceneManager->doNotStartSound = doNotStartSound;
-    engine->sceneManager->_state = SceneManager::kLoadNew;
+    engine->sceneManager->changeScene(sceneID, frameID, verticalOffset, doNotStartSound);
     isDone = true;
 }
 
@@ -557,19 +553,66 @@ void PlayIntStaticBitmapAnimation::execute(NancyEngine *engine) {
 }
 
 uint16 MapCall::readData(Common::SeekableReadStream &stream) {
-    mapData = stream.readByte();
+    stream.skip(1);
     return 1;
 }
 
+void MapCall::execute(NancyEngine *engine) {
+    execType = 2;
+    engine->sceneManager->stateChangeRequests |= SceneManager::kMap;
+    // call base, depends on execType
+}
+
 uint16 MapCallHot1Fr::readData(Common::SeekableReadStream &stream) {
-    return readRaw(stream, 0x12); // TODO
+    hotspotDesc.readData(stream);
+    return 0x12;
+}
+
+void MapCallHot1Fr::execute(NancyEngine *engine) {
+    switch (state) {
+        case kBegin:
+            hotspot = hotspotDesc.coords;
+            state = kRun;
+            // fall through
+        case kRun:
+            if (engine->playState.currentViewFrame == hotspotDesc.frameID) {
+                hasHotspot = true;
+            }
+            break;
+        case kActionTrigger:
+            MapCall::execute(engine);
+            break;
+    }
 }
 
 uint16 MapCallHotMultiframe::readData(Common::SeekableReadStream &stream) {
-    uint16 size = stream.readUint16LE() * 0x12 + 0x2;
-    stream.seek(-2, SEEK_CUR);
+    uint16 numDescs = stream.readUint16LE();
+    for (uint i = 0; i < numDescs; ++i) {
+        hotspots.push_back(HotspotDesc());
+        hotspots[i].readData(stream);
+    }
 
-    return readRaw(stream, size); // TODO
+    return 2 + numDescs * 0x12;
+}
+
+void MapCallHotMultiframe::execute(NancyEngine *engine) {
+    switch (state) {
+        case kBegin:
+            state = kRun;
+            // fall through
+        case kRun:
+            hasHotspot = false;
+            for (uint i = 0; i < hotspots.size(); ++i) {
+                if (hotspots[i].frameID == engine->playState.currentViewFrame) {
+                    hasHotspot = true;
+                    hotspot = hotspots[i].coords;
+                }
+            }
+            break;
+        case kActionTrigger:
+            MapCall::execute(engine);
+            break;  
+    }
 }
 
 uint16 MapLocationAccess::readData(Common::SeekableReadStream &stream) {
diff --git a/engines/nancy/action/recordtypes.h b/engines/nancy/action/recordtypes.h
index 5d276f4e0b..c7b85797c4 100644
--- a/engines/nancy/action/recordtypes.h
+++ b/engines/nancy/action/recordtypes.h
@@ -241,18 +241,23 @@ public:
 class MapCall : public ActionRecord {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
-
-    byte mapData = 0;
+    virtual void execute(NancyEngine *engine) override;
 };
 
-class MapCallHot1Fr : public ActionRecord {
+class MapCallHot1Fr : public MapCall {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void execute(NancyEngine *engine) override;
+
+    HotspotDesc hotspotDesc;
 };
 
-class MapCallHotMultiframe : public ActionRecord {
+class MapCallHotMultiframe : public MapCall {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void execute(NancyEngine *engine) override;
+
+    Common::Array<HotspotDesc> hotspots;
 };
 
 class MapLocationAccess : public ActionRecord {
diff --git a/engines/nancy/graphics.cpp b/engines/nancy/graphics.cpp
index 064c197619..f1f77854aa 100644
--- a/engines/nancy/graphics.cpp
+++ b/engines/nancy/graphics.cpp
@@ -96,7 +96,7 @@ ZRenderStruct &GraphicsManager::getZRenderStruct(Common::String name) {
     return _ZRender[name];
 }
 
-void GraphicsManager::initZRenderStruct(char const *name,
+Common::String &GraphicsManager::initZRenderStruct(char const *name,
                                         uint32 z,
                                         bool isActive,
                                         ZRenderStruct::BltType bltType,
@@ -119,6 +119,8 @@ void GraphicsManager::initZRenderStruct(char const *name,
         st.destRect = *destRect;
     else st.destRect = Common::Rect();
     st.renderFunction = func;
+
+    return st.name;
 }
 
 // TODO nancy1 only, move to subclass whenever we support multiple games
@@ -127,7 +129,7 @@ void GraphicsManager::initZRenderStruct(char const *name,
 // their location with zrender structs whose names start with RES.
 // I'm using a more naive implementation where everything is redrawn every frame
 // for code simplicity, but that can be changed in the future if needed
-void GraphicsManager::initSceneZRenderStructs() {
+void GraphicsManager::initSceneZRenderStructs(Common::Array<Common::String> &outNames) {
     Common::Rect *source = new Common::Rect();
     Common::Rect *dest = new Common::Rect();
     Common::SeekableReadStream *chunk = nullptr;
@@ -141,70 +143,91 @@ void GraphicsManager::initSceneZRenderStructs() {
     chunk = _engine->getBootChunkStream("MENU");
     READ_RECT(source, 16)
     // Skip the custom rendering function since we're not doing dirty rectangles
-    initZRenderStruct(  "FRAME", 1, true, ZRenderStruct::BltType::kNoTrans, &_primaryFrameSurface,
-                        nullptr, source, source);
-    initZRenderStruct(  "CUR IMAGE CURSOR", 11, true, ZRenderStruct::BltType::kTrans, &_object0Surface);
+    outNames.push_back(initZRenderStruct(  "FRAME", 1, true, ZRenderStruct::kNoTrans, &_primaryFrameSurface,
+                        nullptr, source, source));
+    outNames.push_back(initZRenderStruct(  "CUR IMAGE CURSOR", 11, true, ZRenderStruct::kTrans, &_object0Surface));
 
     chunk = _engine->getBootChunkStream("TBOX");
     READ_RECT(source, 0)
-    initZRenderStruct(  "CUR TB BAT SLIDER", 9, true, ZRenderStruct::BltType::kTrans,
-                        &_object0Surface, nullptr, source, nullptr);
+    outNames.push_back(initZRenderStruct(  "CUR TB BAT SLIDER", 9, true, ZRenderStruct::kTrans,
+                        &_object0Surface, nullptr, source, nullptr));
 
     chunk = _engine->getBootChunkStream("BSUM");
     READ_RECT(dest, 356)
-    initZRenderStruct(  "FRAME TB SURF", 6, false, ZRenderStruct::BltType::kNoTrans,
-                        &_frameTextBox, nullptr, nullptr, dest);
+    outNames.push_back(initZRenderStruct(  "FRAME TB SURF", 6, false, ZRenderStruct::kNoTrans,
+                        &_frameTextBox, nullptr, nullptr, dest));
 
     READ_RECT(source, 388)
     READ_RECT(dest, 420)
-    initZRenderStruct(  "MENU BUT DN", 5, false, ZRenderStruct::BltType::kTrans,
-                        &_object0Surface, nullptr, source, dest);
+    outNames.push_back(initZRenderStruct(  "MENU BUT DN", 5, false, ZRenderStruct::kTrans,
+                        &_object0Surface, nullptr, source, dest));
 
     READ_RECT(source, 404)
     READ_RECT(dest, 436)
-    initZRenderStruct(  "HELP BUT DN", 5, false, ZRenderStruct::BltType::kTrans,
-                        &_object0Surface, nullptr, source, dest);
+    outNames.push_back(initZRenderStruct(  "HELP BUT DN", 5, false, ZRenderStruct::kTrans,
+                        &_object0Surface, nullptr, source, dest));
 
     chunk = _engine->getBootChunkStream("INV");
     READ_RECT(source, 0)
-    initZRenderStruct(  "CUR INV SLIDER", 9, true, ZRenderStruct::BltType::kTrans,
-                         &_object0Surface, nullptr, source, nullptr);
+    outNames.push_back(initZRenderStruct(  "CUR INV SLIDER", 9, true, ZRenderStruct::kTrans,
+                         &_object0Surface, nullptr, source, nullptr));
 
-    initZRenderStruct(  "FRAME INV BOX", 6, false, ZRenderStruct::BltType::kNoTrans, nullptr,
-                        new RenderFunction(this, &GraphicsManager::renderFrameInvBox));
+    outNames.push_back(initZRenderStruct(  "FRAME INV BOX", 6, false, ZRenderStruct::kNoTrans, nullptr,
+                        new RenderFunction(this, &GraphicsManager::renderFrameInvBox)));
     
-    initZRenderStruct(  "INV BITMAP", 9, false, ZRenderStruct::BltType::kNoTrans);
-    initZRenderStruct(  "PRIMARY VIDEO", 8, false, ZRenderStruct::BltType::kNoTrans, nullptr,
-                        new RenderFunction(this, &GraphicsManager::renderPrimaryVideo));
-    initZRenderStruct(  "SEC VIDEO 0", 8, false, ZRenderStruct::BltType::kTrans, &channels[0].surf);
-    initZRenderStruct(  "SEC VIDEO 1", 8, false, ZRenderStruct::BltType::kTrans, &channels[1].surf);
-    initZRenderStruct(  "SEC MOVIE", 8, false, ZRenderStruct::BltType::kNoTrans, &_secMovieSurface);
-    initZRenderStruct(  "ORDERING PUZZLE", 7, false, ZRenderStruct::BltType::kNoTrans, nullptr,
-                        new RenderFunction(this, &GraphicsManager::renderOrderingPuzzle));
-    initZRenderStruct(  "ROTATING LOCK PUZZLE", 7, false, ZRenderStruct::BltType::kNoTrans, nullptr,
-                        new RenderFunction(this, &GraphicsManager::renderRotatingLockPuzzle));
-    initZRenderStruct(  "LEVER PUZZLE", 7, false, ZRenderStruct::BltType::kNoTrans, nullptr,
-                        new RenderFunction(this, &GraphicsManager::renderLeverPuzzle));
-    initZRenderStruct(  "TELEPHONE", 7, false, ZRenderStruct::BltType::kNoTrans, nullptr,
-                        new RenderFunction(this, &GraphicsManager::renderTelephone));
-    initZRenderStruct(  "SLIDER PUZZLE", 7, false, ZRenderStruct::BltType::kNoTrans, nullptr,
-                        new RenderFunction(this, &GraphicsManager::renderSliderPuzzle));
-    initZRenderStruct(  "PASSWORD PUZZLE", 7, false, ZRenderStruct::BltType::kNoTrans, nullptr,
-                        new RenderFunction(this, &GraphicsManager::renderPasswordPuzzle));
+    outNames.push_back(initZRenderStruct(  "INV BITMAP", 9, false, ZRenderStruct::kNoTrans));
+    outNames.push_back(initZRenderStruct(  "PRIMARY VIDEO", 8, false, ZRenderStruct::kNoTrans, nullptr,
+                        new RenderFunction(this, &GraphicsManager::renderPrimaryVideo)));
+    outNames.push_back(initZRenderStruct(  "SEC VIDEO 0", 8, false, ZRenderStruct::kTrans, &channels[0].surf));
+    outNames.push_back(initZRenderStruct(  "SEC VIDEO 1", 8, false, ZRenderStruct::kTrans, &channels[1].surf));
+    outNames.push_back(initZRenderStruct(  "SEC MOVIE", 8, false, ZRenderStruct::kNoTrans, &_secMovieSurface));
+    outNames.push_back(initZRenderStruct(  "ORDERING PUZZLE", 7, false, ZRenderStruct::kNoTrans, nullptr,
+                        new RenderFunction(this, &GraphicsManager::renderOrderingPuzzle)));
+    outNames.push_back(initZRenderStruct(  "ROTATING LOCK PUZZLE", 7, false, ZRenderStruct::kNoTrans, nullptr,
+                        new RenderFunction(this, &GraphicsManager::renderRotatingLockPuzzle)));
+    outNames.push_back(initZRenderStruct(  "LEVER PUZZLE", 7, false, ZRenderStruct::kNoTrans, nullptr,
+                        new RenderFunction(this, &GraphicsManager::renderLeverPuzzle)));
+    outNames.push_back(initZRenderStruct(  "TELEPHONE", 7, false, ZRenderStruct::kNoTrans, nullptr,
+                        new RenderFunction(this, &GraphicsManager::renderTelephone)));
+    outNames.push_back(initZRenderStruct(  "SLIDER PUZZLE", 7, false, ZRenderStruct::kNoTrans, nullptr,
+                        new RenderFunction(this, &GraphicsManager::renderSliderPuzzle)));
+    outNames.push_back(initZRenderStruct(  "PASSWORD PUZZLE", 7, false, ZRenderStruct::kNoTrans, nullptr,
+                        new RenderFunction(this, &GraphicsManager::renderPasswordPuzzle)));
 
     // Moved here from SceneManager::load(), should be ok
-    initZRenderStruct(  "VIEWPORT AVF", 6, true, ZRenderStruct::BltType::kNoTrans,
-                        &_background, nullptr, &viewportDesc.source, &viewportDesc.destination);
+    outNames.push_back(initZRenderStruct(  "VIEWPORT AVF", 6, true, ZRenderStruct::kNoTrans,
+                        &_background, nullptr, &viewportDesc.source, &viewportDesc.destination));
 
     // Moved from PlayIntStaticBitmap
-    initZRenderStruct(  "STATIC BITMAP ANIMATION", 7, false, ZRenderStruct::BltType::kNoTrans,
-                        &_genericSurface);
+    outNames.push_back(initZRenderStruct(  "STATIC BITMAP ANIMATION", 7, false, ZRenderStruct::kNoTrans,
+                        &_genericSurface));
     #undef READ_RECT
 
     delete source;
     delete dest;
 }
 
+void GraphicsManager::initMapRenderStructs(Common::Array<Common::String> &outNames) {
+    outNames.push_back("FRAME");
+    outNames.push_back("VIEWPORT AVF"); // Replaces MAP AVF
+    outNames.push_back("CUR IMAGE CURSOR"); // Replaces CUR MAP CURSOR
+    outNames.push_back("CUR TB BAT SLIDER");
+    outNames.push_back("CUR INV SLIDER");
+    outNames.push_back(initZRenderStruct("MAP LABELS", 7, true, ZRenderStruct::kTrans, &_object0Surface,
+                        new RenderFunction(this, &GraphicsManager::renderMapLabels)));
+    outNames.push_back(initZRenderStruct("MAP ANIM", 9, true, ZRenderStruct::kNoTrans, &_object0Surface,
+                        new RenderFunction(this, &GraphicsManager::renderMapLabels)));
+
+    Common::Rect src;
+    Common::SeekableReadStream *chunk = _engine->getBootChunkStream("MAP");
+    chunk->seek(0x58, SEEK_SET);
+    src.left = chunk->readUint32LE();
+    src.top = chunk->readUint32LE();
+    src.right = chunk->readUint32LE();
+    src.bottom = chunk->readUint32LE();
+    getZRenderStruct("VIEWPORT AVF").sourceRect = src;
+}
+
 void GraphicsManager::renderDisplay() {
     // Construct a list containing every struct and pass it along
     Common::Array<Common::String> array;
@@ -226,12 +249,12 @@ void GraphicsManager::renderDisplay(Common::Array<Common::String> ids) {
                 else {
                     switch (current.bltType) {
                         // making some assumptions here
-                        case ZRenderStruct::BltType::kNoTrans: {
+                        case ZRenderStruct::kNoTrans: {
                             Common::Point dest(current.destRect.left, current.destRect.top);
                             _screen.blitFrom(*current.sourceSurface, current.sourceRect, dest);
                             break;
                         }
-                        case ZRenderStruct::BltType::kTrans: {
+                        case ZRenderStruct::kTrans: {
                             Common::Point dest(current.destRect.left, current.destRect.top);
                             _screen.transBlitFrom(*current.sourceSurface, current.sourceRect, dest, transColor);
                             break;
@@ -421,4 +444,8 @@ void GraphicsManager::renderPasswordPuzzle() {
     // TODO
 }
 
+void GraphicsManager::renderMapLabels() {
+    // TODO
+}
+
 } // End of namespace Nancy
\ No newline at end of file
diff --git a/engines/nancy/graphics.h b/engines/nancy/graphics.h
index 1c98bb499f..ca9d43c14a 100644
--- a/engines/nancy/graphics.h
+++ b/engines/nancy/graphics.h
@@ -73,7 +73,7 @@ public:
     void clearZRenderStruct(Common::String name);
     void clearZRenderStructs();
     ZRenderStruct &getZRenderStruct(Common::String name);
-    void initZRenderStruct( char const *name,
+    Common::String &initZRenderStruct( char const *name,
                             uint32 z,
                             bool isActive,
                             ZRenderStruct::BltType bltType,
@@ -82,7 +82,8 @@ public:
                             Common::Rect *sourceRect = nullptr,
                             Common::Rect *destRect = nullptr );
 
-    virtual void initSceneZRenderStructs();
+    virtual void initSceneZRenderStructs(Common::Array<Common::String> &outNames);
+    virtual void initMapRenderStructs(Common::Array<Common::String> &outNames);
 
     void renderDisplay();
     void renderDisplay(Common::Array<Common::String> ids);
@@ -141,6 +142,7 @@ public:
     void renderTelephone();
     void renderSliderPuzzle();
     void renderPasswordPuzzle();
+    void renderMapLabels();
 };
 
 } // End of namespace Nancy
diff --git a/engines/nancy/map.cpp b/engines/nancy/map.cpp
new file mode 100644
index 0000000000..bc5adbfe29
--- /dev/null
+++ b/engines/nancy/map.cpp
@@ -0,0 +1,159 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/nancy/map.h"
+#include "engines/nancy/graphics.h"
+#include "engines/nancy/resource.h"
+#include "engines/nancy/audio.h"
+#include "engines/nancy/scene.h"
+#include "engines/nancy/input.h"
+
+namespace Nancy {
+
+void Map::process() {
+    switch (_state) {
+        case kInit:
+            init();
+            break;
+        case kRun:
+            run();
+            break;
+    }
+}
+
+void Map::init() {
+    Common::SeekableReadStream *chunk = _engine->getBootChunkStream("MAP");
+    if (_decoder.isVideoLoaded()) {
+        _decoder.close();
+    }
+
+    if (_engine->playState.eventFlags[40] == PlayState::kTrue &&
+        _engine->playState.eventFlags[95] == PlayState::kTrue) {
+        _mapID = 1;
+    } else {
+        _mapID = 0;
+    }
+
+    // Load the video
+    chunk->seek(_mapID * 10, SEEK_SET);
+    char name[10];
+    chunk->read(name, 10);
+    Common::String n(name);
+    _decoder.loadFile(n + ".avf");
+    _mapImage = *_decoder.decodeNextFrame();
+
+    // Load the audio
+    chunk->seek(0x18 + _mapID * 0x20, SEEK_SET);
+    chunk->read(name, 10);
+    n = Common::String(n);
+    uint16 channel = chunk->readUint16LE();
+    chunk->skip(0xA);
+    uint16 volume = chunk->readUint16LE();
+    _engine->sound->loadSound(n, channel, 0, volume);
+    _engine->sound->pauseSound(channel, false);
+
+    for (uint i = 0; i < 4; ++i) {
+        chunk->seek(0x162 + i * 16, SEEK_SET);
+        _hotspots.push_back(Hotspot());
+        Hotspot &h = _hotspots[i];
+        h.hotspot.left = chunk->readUint32LE();
+        h.hotspot.top = chunk->readUint32LE();
+        h.hotspot.right = chunk->readUint32LE();
+        h.hotspot.bottom = chunk->readUint32LE();
+
+        if (_mapID == 1 && (i % 2) != 0) {
+            h.isActive = false;
+        } else {
+            h.isActive = true;
+        }
+
+        for (uint j = 0; j < 2; ++j) {
+            h.scenes.push_back(Hotspot::SceneChange());
+            Hotspot::SceneChange &sc = h.scenes[j];
+            chunk->seek(0x1BE + 6 * i * (j + 1));
+            sc.sceneID = chunk->readUint16LE();
+            sc.frameID = chunk->readUint16LE();
+            sc.verticalOffset = chunk->readUint16LE();
+        }
+    }
+
+    ZRenderStruct &zr = _engine->graphics->getZRenderStruct("VIEWPORT AVF");
+    zr.sourceSurface = &_mapImage;
+
+    _engine->graphics->initMapRenderStructs(_ZRenderFilter);
+    _state = kRun;
+}
+
+void Map::run() {
+    handleMouse();
+
+    if ((_engine->sceneManager->stateChangeRequests & SceneManager::kMap) == 0 &&
+            !(_engine->input->isClickValidLMB && _engine->input->hoveredElementID == InputManager::mapButtonID)) {
+        if (_engine->input->isClickValidLMB) {
+            Hotspot::SceneChange &sc = _hotspots[_engine->input->hoveredElementID].scenes[_mapID];
+            _engine->sceneManager->changeScene(sc.sceneID, sc.frameID, sc.verticalOffset, false);
+            _state = kInit;
+            _engine->_gameFlow.minGameState = NancyEngine::kScene;
+            _engine->_gameFlow.previousGameState = NancyEngine::kMap;
+        }
+    } else {
+        _state = kInit;
+        _engine->_gameFlow.minGameState = NancyEngine::kScene;
+        _engine->_gameFlow.previousGameState = NancyEngine::kMap;
+        return;
+    }
+
+    _engine->graphics->renderDisplay(_ZRenderFilter);
+}
+
+void Map::handleMouse() {
+    ZRenderStruct &zr = _engine->graphics->getZRenderStruct("CUR IMAGE CURSOR");
+    Common::Point mousePos = _engine->input->getMousePosition();
+    zr.destRect.left = zr.destRect.right = mousePos.x;
+    zr.destRect.top = zr.destRect.bottom = mousePos.y;
+    _engine->input->hoveredElementID = -1;
+
+    View &view = _engine->graphics->viewportDesc;
+
+    // TODO incorrect magic number, figure out where this comes from
+    Common::Point viewportMouse = mousePos + Common::Point(10, 10);
+
+    if (view.destination.contains(viewportMouse)) {
+        _engine->input->setPointerBitmap(0, 0, 0);
+        for (uint i = 0; i < _hotspots.size(); ++i) {
+            // Adjust the hotspot coordinates
+            Common::Rect hs = _hotspots[i].hotspot;
+            hs.left += view.destination.left;
+            hs.top += view.destination.top;
+            hs.right += view.destination.left;
+            hs.bottom += view.destination.top;
+
+            if (_hotspots[i].isActive && hs.contains(viewportMouse)) {
+                _engine->input->hoveredElementID = i;
+                _engine->input->setPointerBitmap(-1, 1, 0);   
+                break;             
+            }
+        }
+    }
+}
+
+} // End of namespace Nancy
\ No newline at end of file
diff --git a/engines/nancy/map.h b/engines/nancy/map.h
new file mode 100644
index 0000000000..f05b60bc3b
--- /dev/null
+++ b/engines/nancy/map.h
@@ -0,0 +1,75 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef NANCY_MAP_H
+#define NANCY_MAP_H
+
+#include "engines/nancy/video.h"
+
+#include "common/str.h"
+#include "common/array.h"
+#include "common/rect.h"
+
+#include "graphics/surface.h"
+
+namespace Nancy {
+
+class NancyEngine;
+
+class Map {
+public:
+    enum State { kInit, kRun };
+    Map(NancyEngine *engine) : _engine(engine), _state(kInit), _mapID(0) {}
+
+    void process();
+
+private:
+    struct Hotspot {
+        struct SceneChange {
+            uint16 sceneID = 0;
+            uint16 frameID = 0;
+            uint16 verticalOffset = 0;
+        };
+
+        bool isActive = false;
+        Common::Rect hotspot;
+        Common::Array<SceneChange> scenes;
+    };
+
+    void init();
+    void run();
+
+    void handleMouse();
+
+    NancyEngine *_engine;
+    State _state;
+    uint16 _mapID;
+    AVFDecoder _decoder;
+    Graphics::Surface _mapImage;
+    Common::Array<Hotspot> _hotspots;
+
+    Common::Array<Common::String> _ZRenderFilter;
+};
+
+} // End of namespace Nancy
+
+#endif
\ No newline at end of file
diff --git a/engines/nancy/module.mk b/engines/nancy/module.mk
index 5fd15a111b..b68aafe547 100644
--- a/engines/nancy/module.mk
+++ b/engines/nancy/module.mk
@@ -12,6 +12,7 @@ MODULE_OBJS = \
   input.o \
   logic.o \
   logo.o \
+  map.o \
   metaengine.o \
   nancy.o \
   resource.o \
diff --git a/engines/nancy/nancy.cpp b/engines/nancy/nancy.cpp
index 19c9151d5b..4cacdeae1e 100644
--- a/engines/nancy/nancy.cpp
+++ b/engines/nancy/nancy.cpp
@@ -30,6 +30,7 @@
 #include "engines/nancy/graphics.h"
 #include "engines/nancy/input.h"
 #include "engines/nancy/audio.h"
+#include "engines/nancy/map.h"
 
 #include "common/system.h"
 #include "common/random.h"
@@ -76,6 +77,7 @@ NancyEngine::NancyEngine(OSystem *syst, const NancyGameDescription *gd) :
 
 	logic = new Logic(this);
 	sceneManager = new SceneManager(this);
+	map = new Map(this);
 	graphics = new GraphicsManager(this);
 	input = new InputManager(this);
 	sound = new SoundManager(this);
@@ -91,6 +93,8 @@ NancyEngine::~NancyEngine() {
 	
 	delete logic;
 	delete sceneManager;
+	delete map;
+	delete graphics;
 	delete input;
 	delete sound;
 }
@@ -161,6 +165,10 @@ Common::Error NancyEngine::run() {
 			break;
 		case kScene:
 			sceneManager->process();
+			break;
+		case kMap:
+			map->process();
+			break;
 		case kIdle:
 		default:
 			break;
diff --git a/engines/nancy/nancy.h b/engines/nancy/nancy.h
index addd523e99..4db298e1f5 100644
--- a/engines/nancy/nancy.h
+++ b/engines/nancy/nancy.h
@@ -66,6 +66,7 @@ class ResourceManager;
 class IFF;
 class LogoSequence;
 class SceneManager;
+class Map;
 class Logic;
 class GraphicsManager;
 class InputManager;
@@ -77,6 +78,7 @@ public:
 	friend class LogoSequence;
 	friend class SceneManager;
 	friend class NancyConsole;
+	friend class Map;
 
 	NancyEngine(OSystem *syst, const NancyGameDescription *gd);
 	~NancyEngine();
@@ -114,6 +116,7 @@ public:
 	ResourceManager *_res;
 	Logic *logic;
 	SceneManager *sceneManager;
+	Map *map;
 	GraphicsManager *graphics;
 	InputManager *input;
 	SoundManager *sound;
diff --git a/engines/nancy/scene.cpp b/engines/nancy/scene.cpp
index 04005307a3..308152a115 100644
--- a/engines/nancy/scene.cpp
+++ b/engines/nancy/scene.cpp
@@ -62,6 +62,14 @@ void SceneManager::process() {
     }
 }
 
+void SceneManager::changeScene(uint16 id, uint16 frame, uint16 verticalOffset, bool noSound) {
+    _sceneID = id;
+    _engine->playState.queuedViewFrame = frame;
+    _engine->playState.queuedMaxVerticalScroll = verticalOffset;
+    doNotStartSound = noSound;
+    _state = kLoadNew;
+}
+
 void SceneManager::init() {
     for (uint i = 0; i < 168; ++i) {
         _engine->playState.eventFlags[i] = PlayState::Flag::kFalse;
@@ -104,7 +112,7 @@ void SceneManager::init() {
 
     delete[] name;
 
-    _engine->graphics->initSceneZRenderStructs();
+    _engine->graphics->initSceneZRenderStructs(_ZRenderFilter);
 
     // Set the scroll bar destinations
     Common::SeekableReadStream *tbox = _engine->getBootChunkStream("TBOX");
@@ -225,67 +233,67 @@ void SceneManager::run() {
     if (telephoneIsActive)
         _engine->graphics->getZRenderStruct("TELEPHONE").isActive = true;
 
-    if (helpMenuRequested) {
+    if (stateChangeRequests & kHelpMenu) {
         _stashedTickCount = _engine->getTotalPlayTime();
         _engine->_gameFlow.previousGameState = NancyEngine::GameState::kScene;
         _engine->_gameFlow.minGameState = NancyEngine::GameState::kHelp;
         // _engine->helpMenu->state = HelpMenu::State::kInit;
-        helpMenuRequested = false;
+        stateChangeRequests &= ~kHelpMenu;
         // _engine->sound->stopSceneSpecificSounds();
         return;
     }
 
-    if (mainMenuRequested) {
+    if (stateChangeRequests & kMainMenu) {
         _stashedTickCount = _engine->getTotalPlayTime();
         _engine->_gameFlow.previousGameState = NancyEngine::GameState::kScene;
         _engine->_gameFlow.minGameState = NancyEngine::GameState::kMainMenu;
         // _engine->mainMenu->state = MainMenu::State::kInit;
-        mainMenuRequested = false;
+        stateChangeRequests &= ~kMainMenu;
         // _engine->sound->stopSceneSpecificSounds();
         return;
     }
 
-    if (saveLoadRequested) {
+    if (stateChangeRequests & kSaveLoad) {
         _stashedTickCount = _engine->getTotalPlayTime();
         _engine->_gameFlow.previousGameState = NancyEngine::GameState::kScene;
         _engine->_gameFlow.minGameState = NancyEngine::GameState::kLoadSave;
         // _engine->loadSaveMenu->state = LoadSaveMenu::State::kInit;
-        saveLoadRequested = false;
+        stateChangeRequests &= ~kSaveLoad;
         // _engine->sound->stopSceneSpecificSounds();
         return;
     }
 
-    if (saveReloadRequested) {
+    if (stateChangeRequests & kReloadSave) {
         // TODO
     }
 
-    if (setupMenuRequested) {
+    if (stateChangeRequests & kSetupMenu) {
         _stashedTickCount = _engine->getTotalPlayTime();
         _engine->_gameFlow.previousGameState = NancyEngine::GameState::kScene;
         _engine->_gameFlow.minGameState = NancyEngine::GameState::kSetup;
         // _engine->setupMenu->state = setupMenu::State::kInit;
-        setupMenuRequested = false;
+        stateChangeRequests &= ~kSetupMenu;
         // _engine->sound->stopSceneSpecificSounds();
         return;
     }
 
-    if (creditsSequenceRequested) {
+    if (stateChangeRequests & kCredits) {
         _stashedTickCount = _engine->getTotalPlayTime();
         _engine->_gameFlow.previousGameState = NancyEngine::GameState::kScene;
         _engine->_gameFlow.minGameState = NancyEngine::GameState::kHelp;
         // _engine->credits->state = CreditsSequence::State::kInit;
-        creditsSequenceRequested = false;
+        stateChangeRequests &= ~kCredits;
         // _engine->sound->stopSceneSpecificSounds();
         return;
     }
 
-    if (mapScreenRequested) {
+    if (stateChangeRequests & kMap) {
         _stashedTickCount = _engine->getTotalPlayTime();
         _engine->_gameFlow.previousGameState = NancyEngine::GameState::kScene;
-        _engine->_gameFlow.minGameState = NancyEngine::GameState::kHelp;
+        _engine->_gameFlow.minGameState = NancyEngine::GameState::kMap;
         // _engine->map->state = Map::State::kInit;
-        mapScreenRequested = false;
-        // _engine->sound->stopSceneSpecificSounds();
+        stateChangeRequests &= ~kMap;
+        _engine->sound->stopAllSounds();
         return;
     }
 
@@ -310,6 +318,12 @@ void SceneManager::run() {
         _engine->input->hoveredElementID = -1;
         // TODO a bunch of function calls
         _engine->_gameFlow.previousGameState = NancyEngine::GameState::kScene;
+
+        // Viewport gets reused, set it back to the correct surface
+        ZRenderStruct &zr = _engine->graphics->getZRenderStruct("VIEWPORT AVF");
+        zr.sourceSurface = &_engine->graphics->_background;
+        zr.sourceRect = _engine->graphics->viewportDesc.source;
+        _engine->playState.lastDrawnViewFrame = -1;
         return;
     }
 
@@ -367,7 +381,7 @@ void SceneManager::run() {
 
         if (hovered == InputManager::mapButtonID) {
             // TODO another if
-            mapScreenRequested = true;
+            stateChangeRequests |= kMap;
             return;
         }
 
@@ -542,7 +556,7 @@ void SceneManager::run() {
 
     // TODO
     _engine->playState.lastVerticalScroll = _engine->playState.verticalScroll;
-    _engine->graphics->renderDisplay();
+    _engine->graphics->renderDisplay(_ZRenderFilter);
 }
 
 void SceneManager::handleMouse() {
@@ -555,7 +569,7 @@ void SceneManager::handleMouse() {
 
     View &view = _engine->graphics->viewportDesc;
 
-    // TODO hotpoints for scene movement are not correct, figure out how they're actually calculated
+    // TODO incorrect magic number, figure out where this comes from
     Common::Point viewportMouse = mousePos + Common::Point(10, 10);
     
     // Check if the mouse is within the viewport
@@ -594,9 +608,9 @@ void SceneManager::handleMouse() {
                     hotspot.bottom += view.destination.top - _engine->playState.verticalScroll;
 
                     if (hotspot.contains(viewportMouse)) {
-                        if (r->type == 0xE) {
+                        if (r->type == 0xE || r->type == 0x3D || r->type == 0x3E) {
                             // Set the pointer to the U-shaped arrow if
-                            // the type is Hot1FrExitSceneChange
+                            // the type is Hot1FrExitSceneChange, MapCallHot1Fr, or MapCallHotMultiframe
                             _engine->input->setPointerBitmap(1, 0, 0);
                         } else {
                             _engine->input->setPointerBitmap(-1, 1, 0);
diff --git a/engines/nancy/scene.h b/engines/nancy/scene.h
index 05c4866081..dd38496fd6 100644
--- a/engines/nancy/scene.h
+++ b/engines/nancy/scene.h
@@ -28,6 +28,7 @@
 #include "engines/nancy/datatypes.h"
 
 #include "common/scummsys.h"
+#include "common/array.h"
 #include "common/str.h"
 
 namespace Graphics {
@@ -43,16 +44,20 @@ namespace Nancy {
 class NancyEngine;
 
 class SceneManager {
+    friend class ActionRecord;
 public:
     enum MovementDirection : byte { kUp = 1, kDown = 2, kLeft = 4, kRight = 8 };
     SceneManager(NancyEngine *engine) :
-        _engine {engine},
-        _state {kInit},
-        _sceneID {0},
-        movementDirection{0} { }
+        _engine (engine),
+        _state (kInit),
+        _sceneID (0),
+        movementDirection(0),
+        stateChangeRequests(0) { }
 
     void process();
 
+    void changeScene(uint16 id, uint16 frame, uint16 verticalOffset, bool noSound);
+
 private:
     void init();
     void load();
@@ -69,7 +74,19 @@ public:
         kRun,
         kLoadNew
     };
+
+    enum GameStateChange : byte {
+        kHelpMenu = 1 << 0,
+        kMainMenu = 1 << 1,
+        kSaveLoad = 1 << 2,
+        kReloadSave = 1 << 3,
+        kSetupMenu = 1 << 4,
+        kCredits = 1 << 5,
+        kMap = 1 << 6
+    };
     
+    byte stateChangeRequests; // GameStateChange
+
     int32 playerTimeMinuteLength;
     byte movementDirection;
     State _state;
@@ -94,15 +111,7 @@ private:
     bool passwordPuzzleIsActive = false;
     bool telephoneIsActive = false;
 
-    // These could be condensed into an enum
-    bool helpMenuRequested = false;
-    bool mainMenuRequested = false;
-    bool saveLoadRequested = false;
-    bool saveReloadRequested = false;
-    bool setupMenuRequested = false;
-    bool creditsSequenceRequested = false;
-    bool mapScreenRequested = false;
-    
+    Common::Array<Common::String> _ZRenderFilter;
 };
 
 } // End of namespace Nancy


Commit: 6ce4390b934796519d5e24ed287e5ff033f3e813
    https://github.com/scummvm/scummvm/commit/6ce4390b934796519d5e24ed287e5ff033f3e813
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add map location labels and button

Added the place names that appear when hovering a location on the map screen and the button to return to the last scene. Also made some minor improvements to the input manager.

Changed paths:
    engines/nancy/action/recordtypes.cpp
    engines/nancy/graphics.cpp
    engines/nancy/graphics.h
    engines/nancy/input.cpp
    engines/nancy/input.h
    engines/nancy/logo.cpp
    engines/nancy/map.cpp
    engines/nancy/map.h
    engines/nancy/nancy.cpp
    engines/nancy/scene.cpp


diff --git a/engines/nancy/action/recordtypes.cpp b/engines/nancy/action/recordtypes.cpp
index c14cc1ea80..6af0d98710 100644
--- a/engines/nancy/action/recordtypes.cpp
+++ b/engines/nancy/action/recordtypes.cpp
@@ -561,6 +561,7 @@ void MapCall::execute(NancyEngine *engine) {
     execType = 2;
     engine->sceneManager->stateChangeRequests |= SceneManager::kMap;
     // call base, depends on execType
+    state = kBegin;
 }
 
 uint16 MapCallHot1Fr::readData(Common::SeekableReadStream &stream) {
diff --git a/engines/nancy/graphics.cpp b/engines/nancy/graphics.cpp
index f1f77854aa..a841e4517b 100644
--- a/engines/nancy/graphics.cpp
+++ b/engines/nancy/graphics.cpp
@@ -122,6 +122,11 @@ Common::String &GraphicsManager::initZRenderStruct(char const *name,
 
     return st.name;
 }
+#define READ_RECT(where, x) chunk->seek(x); \
+                            where->left = chunk->readUint32LE(); \
+                            where->top = chunk->readUint32LE(); \
+                            where->right = chunk->readUint32LE(); \
+                            where->bottom = chunk->readUint32LE();
 
 // TODO nancy1 only, move to subclass whenever we support multiple games
 // TODO most of these are wrong and/or incomplete
@@ -134,11 +139,6 @@ void GraphicsManager::initSceneZRenderStructs(Common::Array<Common::String> &out
     Common::Rect *dest = new Common::Rect();
     Common::SeekableReadStream *chunk = nullptr;
     
-    #define READ_RECT(where, x) chunk->seek(x); \
-                                where->left = chunk->readUint32LE(); \
-                                where->top = chunk->readUint32LE(); \
-                                where->right = chunk->readUint32LE(); \
-                                where->bottom = chunk->readUint32LE();
 
     chunk = _engine->getBootChunkStream("MENU");
     READ_RECT(source, 16)
@@ -201,32 +201,36 @@ void GraphicsManager::initSceneZRenderStructs(Common::Array<Common::String> &out
     // Moved from PlayIntStaticBitmap
     outNames.push_back(initZRenderStruct(  "STATIC BITMAP ANIMATION", 7, false, ZRenderStruct::kNoTrans,
                         &_genericSurface));
-    #undef READ_RECT
+
 
     delete source;
     delete dest;
 }
 
 void GraphicsManager::initMapRenderStructs(Common::Array<Common::String> &outNames) {
+    Common::Rect *src = new Common::Rect();
+    Common::Rect *dest = new Common::Rect();
+    Common::SeekableReadStream *chunk = _engine->getBootChunkStream("MAP");
+
     outNames.push_back("FRAME");
     outNames.push_back("VIEWPORT AVF"); // Replaces MAP AVF
     outNames.push_back("CUR IMAGE CURSOR"); // Replaces CUR MAP CURSOR
     outNames.push_back("CUR TB BAT SLIDER");
     outNames.push_back("CUR INV SLIDER");
-    outNames.push_back(initZRenderStruct("MAP LABELS", 7, true, ZRenderStruct::kTrans, &_object0Surface,
-                        new RenderFunction(this, &GraphicsManager::renderMapLabels)));
+    outNames.push_back(initZRenderStruct("MAP LABELS", 7, true, ZRenderStruct::kTrans, &_object0Surface));
+    READ_RECT(src, 0x7A);
+    READ_RECT(dest, 0x8A);
     outNames.push_back(initZRenderStruct("MAP ANIM", 9, true, ZRenderStruct::kNoTrans, &_object0Surface,
-                        new RenderFunction(this, &GraphicsManager::renderMapLabels)));
+                        nullptr, src, dest));
 
-    Common::Rect src;
-    Common::SeekableReadStream *chunk = _engine->getBootChunkStream("MAP");
-    chunk->seek(0x58, SEEK_SET);
-    src.left = chunk->readUint32LE();
-    src.top = chunk->readUint32LE();
-    src.right = chunk->readUint32LE();
-    src.bottom = chunk->readUint32LE();
-    getZRenderStruct("VIEWPORT AVF").sourceRect = src;
-}
+    READ_RECT(src, 0x58)
+    getZRenderStruct("VIEWPORT AVF").sourceRect = *src;
+
+    delete src;
+    delete dest;
+}   
+
+#undef READ_RECT
 
 void GraphicsManager::renderDisplay() {
     // Construct a list containing every struct and pass it along
@@ -444,8 +448,4 @@ void GraphicsManager::renderPasswordPuzzle() {
     // TODO
 }
 
-void GraphicsManager::renderMapLabels() {
-    // TODO
-}
-
 } // End of namespace Nancy
\ No newline at end of file
diff --git a/engines/nancy/graphics.h b/engines/nancy/graphics.h
index ca9d43c14a..0b52cc5c96 100644
--- a/engines/nancy/graphics.h
+++ b/engines/nancy/graphics.h
@@ -142,7 +142,6 @@ public:
     void renderTelephone();
     void renderSliderPuzzle();
     void renderPasswordPuzzle();
-    void renderMapLabels();
 };
 
 } // End of namespace Nancy
diff --git a/engines/nancy/input.cpp b/engines/nancy/input.cpp
index a777337f2c..a86a47155f 100644
--- a/engines/nancy/input.cpp
+++ b/engines/nancy/input.cpp
@@ -58,8 +58,6 @@ const int16 InputManager::passwordPuzzleEndID       = 10020;
 void InputManager::processEvents() {
     using namespace Common;
 
-    isClickValidLMB = false;
-    isClickValidRMB = false;
     _inputs &= ~(kLeftMouseButtonUp | kRightMouseButtonUp);
 
     Common::Event event;
@@ -120,16 +118,10 @@ void InputManager::processEvents() {
                     case kNancyActionLeftClick:
                         _inputs &= ~kLeftMouseButtonDown;
                         _inputs |= kLeftMouseButtonUp;
-                        if (hoveredElementID != -1) {
-                            isClickValidLMB = true;
-                        }
                         break;
                     case kNancyActionRightClick:
                         _inputs &= ~kRightMouseButtonDown;
                         _inputs |= kRightMouseButtonUp;
-                        if (hoveredElementID != -1) {
-                            isClickValidRMB = true;
-                        }
                         break;
                     default:
                         break;
@@ -175,6 +167,11 @@ bool InputManager::getInput(InputManager::InputType type) {
     return _inputs & type;
 }
 
+void InputManager::clearInput() {
+    _inputs = 0;
+    hoveredElementID = -1;
+}
+
 Common::Point InputManager::getMousePosition() {
     return _engine->getEventManager()->getMousePos();
 }
diff --git a/engines/nancy/input.h b/engines/nancy/input.h
index cb18683139..6e25e14f50 100644
--- a/engines/nancy/input.h
+++ b/engines/nancy/input.h
@@ -71,11 +71,14 @@ enum InputType : uint16 {
     kMoveFastModifier   = 1 << 8
 };
 
-    InputManager(NancyEngine *engine) : _engine(engine), _inputs(0), isClickValidLMB(false), isClickValidRMB(false), hoveredElementID(-1) {}
+    InputManager(NancyEngine *engine) : _engine(engine), _inputs(0), hoveredElementID(-1) {}
     void processEvents();
 
     bool getInput(InputType type);
     byte getInput() { return _inputs; }
+    void clearInput();
+    bool isClickValidLMB() { return hoveredElementID != -1 && _inputs & kLeftMouseButtonUp; }
+    bool isClickValidRMB() { return hoveredElementID != -1 && _inputs & kLeftMouseButtonDown; }
 
     Common::Point getMousePosition();
     void setMousePosition(const Common::Point &newPos);
@@ -83,8 +86,6 @@ enum InputType : uint16 {
 
     static void initKeymaps(Common::KeymapArray &keymaps);
 
-    bool isClickValidLMB;
-    bool isClickValidRMB;
     int16 hoveredElementID;
     Cursors cursorsData;
     
@@ -119,7 +120,6 @@ private:
     bool itemHeld = false;
 
     uint16 _inputs;
-    Common::Point _mousePos;
 };
 
 } // End of namespace Nancy
diff --git a/engines/nancy/logo.cpp b/engines/nancy/logo.cpp
index c751e4dd85..a48f16b7d6 100644
--- a/engines/nancy/logo.cpp
+++ b/engines/nancy/logo.cpp
@@ -60,6 +60,7 @@ void LogoSequence::init() {
 		error("Failed to load %s", _engine->_logos[0].name.c_str());
 
 	_state = kStartSound;
+	_engine->_gameFlow.previousGameState = NancyEngine::kLogo;
 }
 
 void LogoSequence::startSound() {
diff --git a/engines/nancy/map.cpp b/engines/nancy/map.cpp
index bc5adbfe29..5e0c62f87a 100644
--- a/engines/nancy/map.cpp
+++ b/engines/nancy/map.cpp
@@ -73,8 +73,8 @@ void Map::init() {
 
     for (uint i = 0; i < 4; ++i) {
         chunk->seek(0x162 + i * 16, SEEK_SET);
-        _hotspots.push_back(Hotspot());
-        Hotspot &h = _hotspots[i];
+        _locations.push_back(Location());
+        Location &h = _locations[i];
         h.hotspot.left = chunk->readUint32LE();
         h.hotspot.top = chunk->readUint32LE();
         h.hotspot.right = chunk->readUint32LE();
@@ -87,39 +87,61 @@ void Map::init() {
         }
 
         for (uint j = 0; j < 2; ++j) {
-            h.scenes.push_back(Hotspot::SceneChange());
-            Hotspot::SceneChange &sc = h.scenes[j];
-            chunk->seek(0x1BE + 6 * i * (j + 1));
+            h.scenes.push_back(Location::SceneChange());
+            Location::SceneChange &sc = h.scenes[j];
+            chunk->seek(0x1BE + 6 * i * (j + 1), SEEK_SET);
             sc.sceneID = chunk->readUint16LE();
             sc.frameID = chunk->readUint16LE();
             sc.verticalOffset = chunk->readUint16LE();
         }
+
+        chunk->seek(0x9A + i * 16, SEEK_SET);
+        h.labelSrc.left = chunk->readUint32LE();
+        h.labelSrc.top = chunk->readUint32LE();
+        h.labelSrc.right = chunk->readUint32LE();
+        h.labelSrc.bottom = chunk->readUint32LE();
+
+        // TODO this gets initialized using MAP and the textbox's on-screen location
+        // but the code is annoyingly long so fpr now i just directly write the result
+        h.labelDest = Common::Rect(0x56, 0x166, 0x15E, 0x19B);
+        
     }
 
     ZRenderStruct &zr = _engine->graphics->getZRenderStruct("VIEWPORT AVF");
     zr.sourceSurface = &_mapImage;
 
     _engine->graphics->initMapRenderStructs(_ZRenderFilter);
+	_engine->_gameFlow.previousGameState = NancyEngine::kMap;
     _state = kRun;
 }
 
 void Map::run() {
     handleMouse();
+    int16 hover = _engine->input->hoveredElementID;
+    ZRenderStruct &labels = _engine->graphics->getZRenderStruct("MAP LABELS");
+    if (hover != -1 && hover < 10000) {
+        labels.isActive = true;
+        labels.sourceRect = _locations[hover].labelSrc;
+        labels.destRect = _locations[hover].labelDest;
+    } else {
+        labels.isActive = false;
+    }
 
-    if ((_engine->sceneManager->stateChangeRequests & SceneManager::kMap) == 0 &&
-            !(_engine->input->isClickValidLMB && _engine->input->hoveredElementID == InputManager::mapButtonID)) {
-        if (_engine->input->isClickValidLMB) {
-            Hotspot::SceneChange &sc = _hotspots[_engine->input->hoveredElementID].scenes[_mapID];
-            _engine->sceneManager->changeScene(sc.sceneID, sc.frameID, sc.verticalOffset, false);
+    if (_engine->input->isClickValidLMB()) {
+        if (_engine->sceneManager->stateChangeRequests & SceneManager::kMap ||
+                hover == InputManager::mapButtonID) {
             _state = kInit;
             _engine->_gameFlow.minGameState = NancyEngine::kScene;
             _engine->_gameFlow.previousGameState = NancyEngine::kMap;
+            _engine->sceneManager->stateChangeRequests &= ~NancyEngine::kMap;
+            return;
         }
-    } else {
+
+        Location::SceneChange &sc = _locations[hover].scenes[_mapID];
+        _engine->sceneManager->changeScene(sc.sceneID, sc.frameID, sc.verticalOffset, false);
         _state = kInit;
         _engine->_gameFlow.minGameState = NancyEngine::kScene;
         _engine->_gameFlow.previousGameState = NancyEngine::kMap;
-        return;
     }
 
     _engine->graphics->renderDisplay(_ZRenderFilter);
@@ -139,20 +161,27 @@ void Map::handleMouse() {
 
     if (view.destination.contains(viewportMouse)) {
         _engine->input->setPointerBitmap(0, 0, 0);
-        for (uint i = 0; i < _hotspots.size(); ++i) {
+        for (uint i = 0; i < _locations.size(); ++i) {
             // Adjust the hotspot coordinates
-            Common::Rect hs = _hotspots[i].hotspot;
+            Common::Rect hs = _locations[i].hotspot;
             hs.left += view.destination.left;
             hs.top += view.destination.top;
             hs.right += view.destination.left;
             hs.bottom += view.destination.top;
 
-            if (_hotspots[i].isActive && hs.contains(viewportMouse)) {
+            if (_locations[i].isActive && hs.contains(viewportMouse)) {
                 _engine->input->hoveredElementID = i;
                 _engine->input->setPointerBitmap(-1, 1, 0);   
                 break;             
             }
         }
+    } else {
+        if (_engine->graphics->getZRenderStruct("MAP ANIM").destRect.contains(mousePos)) {
+            _engine->input->hoveredElementID = InputManager::mapButtonID;
+            _engine->input->setPointerBitmap(1, 2, -1);
+        } else {
+            _engine->input->setPointerBitmap(1, 1, 0);
+        }
     }
 }
 
diff --git a/engines/nancy/map.h b/engines/nancy/map.h
index f05b60bc3b..5b11a8d6ab 100644
--- a/engines/nancy/map.h
+++ b/engines/nancy/map.h
@@ -43,7 +43,7 @@ public:
     void process();
 
 private:
-    struct Hotspot {
+    struct Location {
         struct SceneChange {
             uint16 sceneID = 0;
             uint16 frameID = 0;
@@ -53,6 +53,9 @@ private:
         bool isActive = false;
         Common::Rect hotspot;
         Common::Array<SceneChange> scenes;
+
+        Common::Rect labelSrc;
+        Common::Rect labelDest;
     };
 
     void init();
@@ -65,7 +68,7 @@ private:
     uint16 _mapID;
     AVFDecoder _decoder;
     Graphics::Surface _mapImage;
-    Common::Array<Hotspot> _hotspots;
+    Common::Array<Location> _locations;
 
     Common::Array<Common::String> _ZRenderFilter;
 };
diff --git a/engines/nancy/nancy.cpp b/engines/nancy/nancy.cpp
index 4cacdeae1e..87bbc9b5f9 100644
--- a/engines/nancy/nancy.cpp
+++ b/engines/nancy/nancy.cpp
@@ -151,6 +151,11 @@ Common::Error NancyEngine::run() {
 	_gameFlow.minGameState = kBoot;
 
 	while (!shouldQuit()) {
+		input->processEvents();
+		if (_gameFlow.minGameState != _gameFlow.previousGameState) {
+			input->clearInput();
+		}
+
 		switch (_gameFlow.minGameState) {
 		case kBoot:
 			bootGameEngine();
@@ -174,7 +179,6 @@ Common::Error NancyEngine::run() {
 			break;
 		}
 
-		input->processEvents();
 
 		if (launchConsole) {
 			_console->attach();
diff --git a/engines/nancy/scene.cpp b/engines/nancy/scene.cpp
index 308152a115..0d4961f8d0 100644
--- a/engines/nancy/scene.cpp
+++ b/engines/nancy/scene.cpp
@@ -352,7 +352,7 @@ void SceneManager::run() {
         _engine->playState.timeOfDay = _engine->playState.kDuskDawn;
     }
 
-    if (_engine->input->isClickValidLMB) {
+    if (_engine->input->isClickValidLMB()) {
         if (orderingPuzzleIsActive) {
             // TODO
         }
@@ -436,7 +436,7 @@ void SceneManager::run() {
         }
 
 
-    } else if (_engine->input->isClickValidRMB) {
+    } else if (_engine->input->isClickValidRMB()) {
         if (_engine->input->hoveredElementID == InputManager::textBoxScrollbarID) {
             // TODO, moves scrollbar one line up
         } else if (_engine->input->hoveredElementID == InputManager::inventoryScrollbarID) {


Commit: d5b7053dbe80791b7e797430a126dbbbc204321f
    https://github.com/scummvm/scummvm/commit/d5b7053dbe80791b7e797430a126dbbbc204321f
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add scrollbar movement and inventory contents

Added code to move the two scrollbars and implemented renderFrameInvBox(), which displays the inventory. Actually populating the inventory and interacting with it are still TODO.

Changed paths:
    engines/nancy/datatypes.cpp
    engines/nancy/datatypes.h
    engines/nancy/graphics.cpp
    engines/nancy/nancy.cpp
    engines/nancy/scene.cpp
    engines/nancy/scene.h


diff --git a/engines/nancy/datatypes.cpp b/engines/nancy/datatypes.cpp
index 1f854782cb..816ad0b7d0 100644
--- a/engines/nancy/datatypes.cpp
+++ b/engines/nancy/datatypes.cpp
@@ -26,73 +26,103 @@
 
 namespace Nancy {
 
-SceneSummary::SceneSummary(Common::SeekableReadStream *stream) {
+// Simple helper function to read rectangles
+// TODO identical to the one in recordtypes.h, move somewhere else
+static void readRect(Common::SeekableReadStream &stream, Common::Rect &inRect) {
+    inRect.left = stream.readUint32LE();
+    inRect.top = stream.readUint32LE();
+    inRect.right = stream.readUint32LE();
+    inRect.bottom = stream.readUint32LE();
+}
+
+void SceneSummary::read(Common::SeekableReadStream &stream) {
     char *buf = new char[0x32];
 
-    stream->read(buf, 0x31);
+    stream.read(buf, 0x31);
     buf[32] = 0;
     description = Common::String(buf);
 
-    stream->seek(1, SEEK_CUR);
-    stream->read(buf, 9);
+    stream.seek(1, SEEK_CUR);
+    stream.read(buf, 9);
     buf[9] = 0;
     videoFile = Common::String(buf);
 
     // skip 1 extra byte & 2 unknown bytes
-    stream->seek(3, SEEK_CUR);
-    videoFormat = stream->readUint16LE();
+    stream.seek(3, SEEK_CUR);
+    videoFormat = stream.readUint16LE();
 
-    stream->read(buf, 10);
+    stream.read(buf, 10);
     buf[9] = 0;
     audioFile = Common::String(buf);
-    audioID = stream->readSint16LE();
-    stream->skip(0xE);
-    audioVolume = stream->readUint16LE();
-
-    stream->seek(0x72);
-    verticalScrollDelta = stream->readUint16LE();
-    horizontalEdgeSize = stream->readUint16LE();
-    verticalEdgeSize = stream->readUint16LE();
-    slowMoveTimeDelta = stream->readUint16LE();
-    fastMoveTimeDelta = stream->readUint16LE();
-    unknown7C = stream->readByte();
+    audioID = stream.readSint16LE();
+    stream.skip(0xE);
+    audioVolume = stream.readUint16LE();
+
+    stream.seek(0x72);
+    verticalScrollDelta = stream.readUint16LE();
+    horizontalEdgeSize = stream.readUint16LE();
+    verticalEdgeSize = stream.readUint16LE();
+    slowMoveTimeDelta = stream.readUint16LE();
+    fastMoveTimeDelta = stream.readUint16LE();
+    unknown7C = stream.readByte();
 
     delete[] buf;
 }
 
 // Takes a VIEW chunk as input
-View::View(Common::SeekableReadStream *stream) {
-    stream->seek(0);
-    destination.left = stream->readUint32LE();
-    destination.top = stream->readUint32LE();
-    destination.right = stream->readUint32LE();
-    destination.bottom = stream->readUint32LE();
-    source.left = stream->readUint32LE();
-    source.top = stream->readUint32LE();
-    source.right = stream->readUint32LE();
-    source.bottom = stream->readUint32LE();
-    f1Dest.left = stream->readUint32LE();
-    f1Dest.top = stream->readUint32LE();
-    f1Dest.right = stream->readUint32LE();
-    f1Dest.bottom = stream->readUint32LE();
-    f2Dest.left = stream->readUint32LE();
-    f2Dest.top = stream->readUint32LE();
-    f2Dest.right = stream->readUint32LE();
-    f2Dest.bottom = stream->readUint32LE();
+void View::read(Common::SeekableReadStream &stream) {
+    stream.seek(0);
+    readRect(stream, destination);
+    readRect(stream, source);
+    readRect(stream, f1Dest);
+    readRect(stream, f2Dest);
 }
 
 // Takes a CURS chunk as input
-Cursors::Cursors(Common::SeekableReadStream *stream) {
-    stream->seek(0);
+void Cursors::read(Common::SeekableReadStream &stream) {
+    stream.seek(0);
     for (uint i = 0; i < 85; ++i) {
         Common::Rect &rect = rects[i];
-        rect.left = stream->readUint32LE();
-        rect.top = stream->readUint32LE();
-        rect.right = stream->readUint32LE();
-        rect.bottom = stream->readUint32LE();
+        rect.left = stream.readUint32LE();
+        rect.top = stream.readUint32LE();
+        rect.right = stream.readUint32LE();
+        rect.bottom = stream.readUint32LE();
+    }
+    primaryVideoCursorX = stream.readUint16LE();
+    primaryVideoCursorY = stream.readUint16LE();
+}
+
+void Inventory::read(Common::SeekableReadStream &stream) {
+    stream.seek(0, SEEK_SET);
+
+    readRect(stream, sliderSource);
+    // Stored with uint16s for some reason
+    sliderDefaultDest.x = stream.readUint16LE();
+    sliderDefaultDest.y = stream.readUint16LE();
+
+    stream.seek(0xD6, SEEK_SET);
+    for (uint i = 0; i < 14; ++i) {
+        readRect(stream, shadesSrc[i]);
+    }
+    readRect(stream, shadesDst);
+    shadesFrameTime = stream.readUint16LE();
+
+    char name[10];
+    stream.read(name, 10);
+    inventoryBoxIconsImageName = Common::String(name);
+    stream.read(name, 10);
+    inventoryCursorsImageName = Common::String(name);
+
+    stream.skip(8);
+    readRect(stream, emptySpaceSource);
+
+    char itemName[14];
+    for (uint i = 0; i < 11; ++i) {
+        stream.read(itemName, 14);
+        items[i].name = Common::String(itemName);
+        items[i].unknown = stream.readUint16LE();
+        readRect(stream, items[i].sourceRect);
     }
-    primaryVideoCursorX = stream->readUint16LE();
-    primaryVideoCursorY = stream->readUint16LE();
 }
 
 } // End of namespace Nancy
\ No newline at end of file
diff --git a/engines/nancy/datatypes.h b/engines/nancy/datatypes.h
index e5b4c74c50..5d86463163 100644
--- a/engines/nancy/datatypes.h
+++ b/engines/nancy/datatypes.h
@@ -36,8 +36,7 @@ namespace Nancy {
 
 // Describes a scene
 struct SceneSummary {
-    SceneSummary() =default;
-    SceneSummary(Common::SeekableReadStream *stream);
+    void read(Common::SeekableReadStream &stream);
 
     Common::String description; // 0x00
     Common::String videoFile;   // 0x32
@@ -58,8 +57,7 @@ struct SceneSummary {
 
 // Describes the viewport
 struct View {
-    View() =default;
-    View(Common::SeekableReadStream *stream);
+    void read(Common::SeekableReadStream &stream);
     // The bounds of the destination rectangle on screen
     Common::Rect destination;
     // The bounds of the source rectangle (Background -> screen)
@@ -72,14 +70,36 @@ struct View {
 
 // Holds the coordinates for the bitmaps of all cursors
 struct Cursors {
-    Cursors() =default;
-    Cursors(Common::SeekableReadStream *stream);
+    void read(Common::SeekableReadStream &stream);
+
     Common::Rect rects[85];
     // The cursor gets set to this location at some point during PrimaryVideoSequence 
     uint16 primaryVideoCursorX;
     uint16 primaryVideoCursorY;
 };
 
+struct Inventory {
+    struct ItemDesc {
+        Common::String name; // 0x00
+        byte unknown = 0; // 0x14
+        Common::Rect sourceRect; // 0x16
+    };
+
+    void read(Common::SeekableReadStream &stream);
+
+    Common::Rect sliderSource; // 0x00
+    Common::Point sliderDefaultDest; // 0x10
+    //...
+    Common::Rect shadesSrc[14]; // 0xD6
+    Common::Rect shadesDst; // 0x1B6
+    uint16 shadesFrameTime; // 0x1C6
+    Common::String inventoryBoxIconsImageName; // 0x1C8
+    Common::String inventoryCursorsImageName; // 0x1D2
+
+    Common::Rect emptySpaceSource; // 0x1E4
+    ItemDesc items[11]; // 0x1F4
+};
+
 } // End of namespace Nancy
 
 #endif // NANCY_DATATYPES_H
\ No newline at end of file
diff --git a/engines/nancy/graphics.cpp b/engines/nancy/graphics.cpp
index a841e4517b..d065a3b7db 100644
--- a/engines/nancy/graphics.cpp
+++ b/engines/nancy/graphics.cpp
@@ -23,6 +23,7 @@
 #include "engines/nancy/graphics.h"
 #include "engines/nancy/resource.h"
 #include "engines/nancy/action/recordtypes.h"
+#include "engines/nancy/scene.h"
 
 #include "common/error.h"
 #include "common/system.h"
@@ -47,7 +48,7 @@ GraphicsManager::GraphicsManager(NancyEngine *engine) :
 
 void GraphicsManager::init() {  
     Common::SeekableReadStream *chunk = _engine->getBootChunkStream("VIEW");
-    viewportDesc = View(chunk);
+    viewportDesc.read(*chunk);
     uint32 width = viewportDesc.destination.right - viewportDesc.destination.left;
     uint32 height = viewportDesc.destination.bottom - viewportDesc.destination.top;
     _background.create(width, height, pixelFormat);
@@ -172,7 +173,7 @@ void GraphicsManager::initSceneZRenderStructs(Common::Array<Common::String> &out
     outNames.push_back(initZRenderStruct(  "CUR INV SLIDER", 9, true, ZRenderStruct::kTrans,
                          &_object0Surface, nullptr, source, nullptr));
 
-    outNames.push_back(initZRenderStruct(  "FRAME INV BOX", 6, false, ZRenderStruct::kNoTrans, nullptr,
+    outNames.push_back(initZRenderStruct(  "FRAME INV BOX", 6, true, ZRenderStruct::kNoTrans, nullptr,
                         new RenderFunction(this, &GraphicsManager::renderFrameInvBox)));
     
     outNames.push_back(initZRenderStruct(  "INV BITMAP", 9, false, ZRenderStruct::kNoTrans));
@@ -405,7 +406,27 @@ void GraphicsManager::renderFrame() {
 }
 
 void GraphicsManager::renderFrameInvBox() {
-    // TODO
+    Inventory &inv = _engine->sceneManager->inventoryDesc;
+    SceneManager::InventoryBox &invBox = _engine->sceneManager->inventoryBoxDesc;
+
+    // Draw the current four visible items if any
+    for (uint i = 0; i < 4; ++i) {
+        if (invBox.onScreenItems[i].itemId != -1) {
+            Common::Point dest(invBox.onScreenItems[i].dest.left, invBox.onScreenItems[i].dest.top);
+            _screen.blitFrom(_inventoryBoxIconsSurface, invBox.onScreenItems->source, dest);
+        }
+    }
+
+    // Draw the shades
+    if (invBox.blindsAnimFrame < 7) {
+        // Draw left shade
+        Common::Point dest(inv.shadesDst.left, inv.shadesDst.top);
+        _screen.blitFrom(_object0Surface, inv.shadesSrc[(6 - invBox.blindsAnimFrame) * 2], dest);
+
+        // Draw right shade
+        dest.x = inv.shadesDst.right - inv.shadesSrc[(6 - invBox.blindsAnimFrame) * 2 + 1].width();
+        _screen.blitFrom(_object0Surface, inv.shadesSrc[(6 - invBox.blindsAnimFrame) * 2 + 1], dest);
+    }
 }
 
 void GraphicsManager::renderPrimaryVideo() {
diff --git a/engines/nancy/nancy.cpp b/engines/nancy/nancy.cpp
index 87bbc9b5f9..db4b095b32 100644
--- a/engines/nancy/nancy.cpp
+++ b/engines/nancy/nancy.cpp
@@ -216,7 +216,7 @@ void NancyEngine::bootGameEngine() {
 		addBootChunk(n, boot->getChunkStream(n));
 	}
 
-	input->cursorsData = Cursors(getBootChunkStream("CURS"));
+	input->cursorsData.read(*getBootChunkStream("CURS"));
 
 	// The FR, LG and OB chunks get added here	
 
diff --git a/engines/nancy/scene.cpp b/engines/nancy/scene.cpp
index 0d4961f8d0..912143d0bd 100644
--- a/engines/nancy/scene.cpp
+++ b/engines/nancy/scene.cpp
@@ -96,17 +96,14 @@ void SceneManager::init() {
         error("Failed to load %s", _engine->_objects[0].name.c_str());
     }
 
-    // Load inventory icons
-    Common::SeekableReadStream *inv = _engine->getBootChunkStream("INV");
-    inv->seek(0x1C8);
-    inv->read(name, 10); // should be TOOL
-    if (!_engine->_res->loadImage("ciftree", Common::String(name), _engine->graphics->_inventoryBoxIconsSurface)) {
+    // Load inventory
+    inventoryDesc.read(*_engine->getBootChunkStream("INV"));
+    if (!_engine->_res->loadImage("ciftree", inventoryDesc.inventoryBoxIconsImageName, _engine->graphics->_inventoryBoxIconsSurface)) {
         error("Failed to load inventory icons (TOOL)");
     }
 
     // Load the cursors
-    inv->read(name, 10); // should be TOOLCUR1
-    if (!_engine->_res->loadImage("ciftree", Common::String(name), _engine->graphics->_inventoryCursorsSurface)) {
+    if (!_engine->_res->loadImage("ciftree", inventoryDesc.inventoryCursorsImageName, _engine->graphics->_inventoryCursorsSurface)) {
         error("Failed to load cursors (TOOLCUR1)");
     }
 
@@ -118,21 +115,16 @@ void SceneManager::init() {
     Common::SeekableReadStream *tbox = _engine->getBootChunkStream("TBOX");
     tbox->seek(0x30);
     ZRenderStruct &tbzr = _engine->graphics->getZRenderStruct("CUR TB BAT SLIDER");
-    uint16 width = tbzr.sourceRect.right - tbzr.sourceRect.left;
-    uint16 height = tbzr.sourceRect.bottom - tbzr.sourceRect.top;
-    tbzr.destRect.left =  tbox->readUint16LE() - (width / 2); // coords in file are for center position
+    tbzr.destRect.left =  tbox->readUint16LE() - (tbzr.sourceRect.width() / 2); // coords in file are for center position
     tbzr.destRect.top =  tbox->readUint16LE();
-    tbzr.destRect.right = tbzr.destRect.left + width;
-    tbzr.destRect.bottom = tbzr.destRect.top + height;
+    tbzr.destRect.right = tbzr.destRect.left + tbzr.sourceRect.width();
+    tbzr.destRect.bottom = tbzr.destRect.top + tbzr.sourceRect.height();
 
-    inv->seek(0x10);
     ZRenderStruct &invzr = _engine->graphics->getZRenderStruct("CUR INV SLIDER");
-    width = invzr.sourceRect.right - invzr.sourceRect.left;
-    height = invzr.sourceRect.bottom - invzr.sourceRect.top;
-    invzr.destRect.left = inv->readUint16LE() - (width / 2); // coords in file are for center position
-    invzr.destRect.top = inv->readUint16LE();
-    invzr.destRect.right = invzr.destRect.left + width;
-    invzr.destRect.bottom = invzr.destRect.top + height;
+    invzr.destRect.left = inventoryDesc.sliderDefaultDest.x - (invzr.sourceRect.width() / 2); // coords in file are for center position
+    invzr.destRect.top = inventoryDesc.sliderDefaultDest.y;
+    invzr.destRect.right = invzr.destRect.left + invzr.sourceRect.width();
+    invzr.destRect.bottom = invzr.destRect.top + invzr.sourceRect.height();
 
     _state = kLoad;
 }
@@ -152,7 +144,8 @@ void SceneManager::load() {
         error("Invalid IFF Chunk SSUM");
     }
 
-    currentScene = SceneSummary(sceneSummaryChunk);
+    currentScene = SceneSummary();
+    currentScene.read(*sceneSummaryChunk);
 
     // The check to see if we need to switch the CD is performed here
 
@@ -383,56 +376,40 @@ void SceneManager::run() {
             // TODO another if
             stateChangeRequests |= kMap;
             return;
-        }
-
-        if (hovered == InputManager::textBoxID) {
+        } else if (hovered == InputManager::textBoxID) {
             // TODO
-        }
-
-        if (hovered == InputManager::inventoryItemTakeID) {
+        } else if (hovered == InputManager::inventoryItemTakeID) {
             // TODO
-        }
-
-        if (hovered == InputManager::inventoryItemReturnID) {
+        } else if (hovered == InputManager::inventoryItemReturnID) {
             // TODO
-        }
-
-        if (hovered == InputManager::textBoxScrollbarID) {
+        } else if (hovered == InputManager::textBoxScrollbarID) {
+            handleScrollbar(0);
+        } else if (hovered == InputManager::inventoryScrollbarID) {
+            handleScrollbar(1);
+        } else if (hovered == InputManager::menuButtonID) {
             // TODO
-        }
-
-        if (hovered == InputManager::inventoryScrollbarID) {
-            // TODO
-        }
-
-        if (hovered == InputManager::menuButtonID) {
+        } else if (hovered == InputManager::helpButtonID) {
             // TODO
-        }
-
-        if (hovered == InputManager::helpButtonID) {
+        } else if (hovered == InputManager::orderingPuzzleID ||
+                    hovered == InputManager::orderingPuzzleEndID ||
+                    hovered == InputManager::rotatingLockPuzzleUpID ||
+                    hovered == InputManager::rotatingLockPuzzleDownID ||
+                    hovered == InputManager::rotatingLockPuzzleEndID ||
+                    hovered == InputManager::leverPuzzleID ||
+                    hovered == InputManager::leverPuzzleEndID ||
+                    hovered == InputManager::telephoneID ||
+                    hovered == InputManager::telephoneEndID ||
+                    hovered == InputManager::sliderPuzzleID ||
+                    hovered == InputManager::sliderPuzzleEndID ||
+                    hovered == InputManager::passwordPuzzleEndID) {
             // TODO
-        }
-
-        if (    hovered == InputManager::orderingPuzzleID ||
-                hovered == InputManager::orderingPuzzleEndID ||
-                hovered == InputManager::rotatingLockPuzzleUpID ||
-                hovered == InputManager::rotatingLockPuzzleDownID ||
-                hovered == InputManager::rotatingLockPuzzleEndID ||
-                hovered == InputManager::leverPuzzleID ||
-                hovered == InputManager::leverPuzzleEndID ||
-                hovered == InputManager::telephoneID ||
-                hovered == InputManager::telephoneEndID ||
-                hovered == InputManager::sliderPuzzleID ||
-                hovered == InputManager::sliderPuzzleEndID ||
-                hovered == InputManager::passwordPuzzleEndID) {
-            // TODO
-        }
-
-        // ID must be an action record's
-        ActionRecord *rec = _engine->logic->getActionRecord(hovered);
-        if (rec->isActive /*&& another condition !- 0*/) {
-            // TODO item holding logic
-            rec->state = ActionRecord::ExecutionState::kActionTrigger;
+        } else {
+            // ID must be an action record's
+            ActionRecord *rec = _engine->logic->getActionRecord(hovered);
+            if (rec->isActive /*&& another condition !- 0*/) {
+                // TODO item holding logic
+                rec->state = ActionRecord::ExecutionState::kActionTrigger;
+            }
         }
 
 
@@ -625,12 +602,12 @@ void SceneManager::handleMouse() {
         ZRenderStruct *uizr = &_engine->graphics->getZRenderStruct("CUR TB BAT SLIDER");
         if (uizr->destRect.contains(mousePos)) {
             _engine->input->hoveredElementID = InputManager::textBoxScrollbarID;
-            // call handler function
             _engine->input->setPointerBitmap(1, 2, -1);
+            handleScrollbar(0);
         } else if (uizr = &_engine->graphics->getZRenderStruct("CUR INV SLIDER"), uizr->destRect.contains(mousePos)) {
             _engine->input->hoveredElementID = InputManager::inventoryScrollbarID;
-            // call handler function
             _engine->input->setPointerBitmap(1, 2, -1);
+            handleScrollbar(1);
         } else {
             _engine->input->setPointerBitmap(1, 1, 0);
         }
@@ -658,4 +635,46 @@ void SceneManager::clearSceneData() {
     _engine->graphics->getZRenderStruct("STATIC BITMAP ANIMATION").isActive = false;
 }
 
+// 0 is textbox, 1 is inventory, returns -1 when movement is stopped/disabled
+float SceneManager::handleScrollbar(uint id) {
+    Common::SeekableReadStream *chunk;
+    ZRenderStruct *zr;
+    if (id == 0) {
+        chunk = _engine->getBootChunkStream("TBOX");
+        chunk->seek(0x30);
+        zr = &_engine->graphics->getZRenderStruct("CUR TB BAT SLIDER");
+    } else if (id == 1) {
+        chunk = _engine->getBootChunkStream("INV");
+        chunk->seek(0x10);
+        zr = &_engine->graphics->getZRenderStruct("CUR INV SLIDER");
+    }
+    Common::Point origDest;
+    origDest.x = chunk->readUint16LE() - (zr->sourceRect.width() / 2); // coords in file are for center position
+    origDest.y = chunk->readUint16LE();
+
+    Common::Rect &tboxRect = _engine->graphics->getZRenderStruct("FRAME TB SURF").destRect;
+    Common::Rect &scrollRect = zr->destRect;
+    Common::Point newMousePos = _engine->input->getMousePosition();
+
+    if (_engine->input->getInput() & InputManager::kLeftMouseButtonDown) {
+        if (scrollbarMouse.x == -1 && scrollbarMouse.y == -1) {
+            scrollbarMouse = newMousePos - Common::Point(scrollRect.left, scrollRect.top);
+        }
+        newMousePos -= Common::Point(scrollRect.left, scrollRect.top);
+
+        uint16 minY = origDest.y;
+        uint16 maxY = tboxRect.bottom - scrollRect.height() + tboxRect.top - origDest.y; // TODO goes a little out of bounds
+        uint16 newTop = CLIP((uint16)(scrollRect.top + newMousePos.y - scrollbarMouse.y), minY, maxY);
+        scrollRect.bottom += newTop - scrollRect.top;
+        scrollRect.top = newTop;
+
+        return (float)(maxY - minY) / newTop;
+    } else {
+        scrollbarMouse.x = -1;
+        scrollbarMouse.y = -1;
+    }
+
+    return -1;
+}
+
 } // End of namespace Nancy
\ No newline at end of file
diff --git a/engines/nancy/scene.h b/engines/nancy/scene.h
index dd38496fd6..b52fcbc339 100644
--- a/engines/nancy/scene.h
+++ b/engines/nancy/scene.h
@@ -52,7 +52,8 @@ public:
         _state (kInit),
         _sceneID (0),
         movementDirection(0),
-        stateChangeRequests(0) { }
+        stateChangeRequests(0),
+        scrollbarMouse(-1, -1) { }
 
     void process();
 
@@ -66,6 +67,8 @@ private:
     void handleMouse();
     void clearSceneData();
 
+    float handleScrollbar(uint id);
+
 public:
     enum State {
         kInit,
@@ -84,6 +87,19 @@ public:
         kCredits = 1 << 5,
         kMap = 1 << 6
     };
+
+    struct InventoryBox {
+        struct Item {
+            Common::Rect source;
+            Common::Rect dest;
+            int16 itemId = -1;
+        };
+
+        Item onScreenItems[4];
+        uint16 blindsAnimFrame = 6;
+
+        Time nextFrameTime;
+    };
     
     byte stateChangeRequests; // GameStateChange
 
@@ -93,6 +109,9 @@ public:
     uint16 _sceneID;
     bool doNotStartSound = false;
 
+    Inventory inventoryDesc;
+    InventoryBox inventoryBoxDesc;
+
 private:
     NancyEngine *_engine;
     SceneSummary currentScene;
@@ -101,6 +120,8 @@ private:
     uint32 _stashedTickCount;
     Time _nextBackgroundMovement;
 
+    Common::Point scrollbarMouse;
+
     bool isComingFromMenu = true;
     bool hasLoadedFromSavefile = false;
 


Commit: fb2a65a8e76c8911550ae5f866276d7ce572e19f
    https://github.com/scummvm/scummvm/commit/fb2a65a8e76c8911550ae5f866276d7ce572e19f
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Initial inventory implementation

Added an initial implementation of the inventory, letting the player pick up and hold items. Interaction between items and the viewport is still TODO.

Changed paths:
    engines/nancy/action/recordtypes.cpp
    engines/nancy/action/recordtypes.h
    engines/nancy/datatypes.cpp
    engines/nancy/graphics.cpp
    engines/nancy/graphics.h
    engines/nancy/input.cpp
    engines/nancy/logic.cpp
    engines/nancy/playstate.h
    engines/nancy/scene.cpp
    engines/nancy/scene.h


diff --git a/engines/nancy/action/recordtypes.cpp b/engines/nancy/action/recordtypes.cpp
index 6af0d98710..7f8681fadc 100644
--- a/engines/nancy/action/recordtypes.cpp
+++ b/engines/nancy/action/recordtypes.cpp
@@ -47,6 +47,12 @@ void HotspotDesc::readData(Common::SeekableReadStream &stream) {
     readRect(stream, coords);
 }
 
+void BitmapDesc::readData(Common::SeekableReadStream &stream) {
+    frameID = stream.readUint16LE();
+    readRect(stream, src);
+    readRect(stream, dest);
+}
+
 void SecondaryVideoDesc::readData(Common::SeekableReadStream &stream) {
     frameID = stream.readUint16LE();
     readRect(stream, srcRect);
@@ -456,9 +462,9 @@ uint16 PlayIntStaticBitmapAnimation::readData(Common::SeekableReadStream &stream
     }
 
     for (uint i = 0; i < numFrames; ++i) {
-        srcDestRects.push_back(SrcDestDesc());
-        SrcDestDesc &rects = srcDestRects[i];
-        rects.frameId = stream.readUint16LE();
+        bitmaps.push_back(BitmapDesc());
+        BitmapDesc &rects = bitmaps[i];
+        rects.frameID = stream.readUint16LE();
         readRect(stream, rects.src);
         readRect(stream, rects.dest);
     }
@@ -475,8 +481,8 @@ void PlayIntStaticBitmapAnimation::execute(NancyEngine *engine) {
 
             // find the correct source and destination for the current viewport frame
             lastViewFrame = engine->playState.currentViewFrame;
-            for (uint i = 0; i < srcDestRects.size(); ++i) {
-                if (lastViewFrame == srcDestRects[i].frameId) {
+            for (uint i = 0; i < bitmaps.size(); ++i) {
+                if (lastViewFrame == bitmaps[i].frameID) {
                     currentViewFrameID = i;
                     break;
                 }
@@ -496,7 +502,7 @@ void PlayIntStaticBitmapAnimation::execute(NancyEngine *engine) {
             zr.sourceRect = frameRects[currentFrame - firstFrame];
             if (currentViewFrameID != -1) {
                 zr.isActive = true;
-                zr.destRect = srcDestRects[currentViewFrameID].dest;
+                zr.destRect = bitmaps[currentViewFrameID].dest;
                 zr.destRect.left += engine->graphics->viewportDesc.destination.left;
                 zr.destRect.top += engine->graphics->viewportDesc.destination.top;
                 zr.destRect.top -= engine->playState.verticalScroll;
@@ -515,11 +521,11 @@ void PlayIntStaticBitmapAnimation::execute(NancyEngine *engine) {
             if (lastViewFrame != engine->playState.currentViewFrame) {
                 lastViewFrame = engine->playState.currentViewFrame;
                 currentViewFrameID = -1;
-                for (uint i = 0; i < srcDestRects.size(); ++i) {
-                    if (lastViewFrame == srcDestRects[i].frameId) {
+                for (uint i = 0; i < bitmaps.size(); ++i) {
+                    if (lastViewFrame == bitmaps[i].frameID) {
                         currentViewFrameID = i;
                         zr.isActive = true;
-                        zr.destRect = srcDestRects[currentViewFrameID].dest;
+                        zr.destRect = bitmaps[currentViewFrameID].dest;
                         zr.destRect.left += engine->graphics->viewportDesc.destination.left;
                         zr.destRect.top += engine->graphics->viewportDesc.destination.top;
                         zr.destRect.top -= engine->playState.verticalScroll;
@@ -810,11 +816,56 @@ uint16 RotatingLockPuzzle::readData(Common::SeekableReadStream &stream) {
 }
 
 uint16 ShowInventoryItem::readData(Common::SeekableReadStream &stream) {
-    stream.seek(0xC, SEEK_CUR);
-    uint16 size = stream.readUint16LE() * 0x22 + 0xE;
-    stream.seek(-0xE, SEEK_CUR);
+    objectID = stream.readUint16LE();
+    char name[10];
+    stream.read(name, 10);
+    imageName = Common::String(name);
 
-    return readRaw(stream, size); // TODO
+    uint16 numFrames = stream.readUint16LE();
+
+    for (uint i = 0; i < numFrames; ++i) {
+        bitmaps.push_back(BitmapDesc());
+        bitmaps[i].readData(stream);
+    }
+
+    return 0xE + 0x22 * numFrames;
+}
+
+void ShowInventoryItem::execute(NancyEngine *engine) {
+    ZRenderStruct &zr = engine->graphics->getZRenderStruct("INV BITMAP");
+    switch (state) {
+        case kBegin:
+            zr.sourceSurface->free();
+            engine->_res->loadImage("ciftree", imageName, *zr.sourceSurface);
+            state = kRun;
+            // fall through
+        case kRun:
+            zr.isActive = false;
+            hasHotspot = false;
+
+            for (uint i = 0; i < bitmaps.size(); ++i) {
+                if (bitmaps[i].frameID == engine->playState.currentViewFrame) {
+                    hasHotspot = true;
+                    hotspot = bitmaps[i].dest;
+                    
+                    zr.isActive = true;
+                    zr.sourceRect = bitmaps[i].src;
+                    zr.destRect = bitmaps[i].dest;
+                    View &viewport = engine->graphics->viewportDesc;
+                    zr.destRect.left += viewport.destination.left;
+                    zr.destRect.right += viewport.destination.left;
+                    zr.destRect.top += viewport.destination.top - engine->playState.verticalScroll;
+                    zr.destRect.bottom += viewport.destination.top - engine->playState.verticalScroll;
+                }
+            }
+            break;
+        case kActionTrigger:
+            // TODO play sound
+            engine->sceneManager->addObjectToInventory(objectID);
+            zr.isActive = false;
+            hasHotspot = false;
+            isDone = true;
+    }
 }
 
 uint16 PlayDigiSoundAndDie::readData(Common::SeekableReadStream &stream) {
@@ -831,7 +882,7 @@ uint16 PlayDigiSoundAndDie::readData(Common::SeekableReadStream &stream) {
     return 0x2B;
 }
 
-void PlayDigiSoundAndDie::execute(NancyEngine *engine){
+void PlayDigiSoundAndDie::execute(NancyEngine *engine) {
     switch (state) {
         case kBegin:
             engine->sound->loadSound(filename, id, numLoops, volume);
diff --git a/engines/nancy/action/recordtypes.h b/engines/nancy/action/recordtypes.h
index c7b85797c4..05f5b56bd8 100644
--- a/engines/nancy/action/recordtypes.h
+++ b/engines/nancy/action/recordtypes.h
@@ -41,6 +41,15 @@ struct HotspotDesc {
     void readData(Common::SeekableReadStream &stream);
 };
 
+// Describes a single bitmap draw
+struct BitmapDesc {
+    uint16 frameID = 0;
+    Common::Rect src;
+    Common::Rect dest;
+
+    void readData(Common::SeekableReadStream &stream);
+};
+
 // Describes a secondary video/movie's source and destination
 struct SecondaryVideoDesc {
     int16 frameID;
@@ -206,11 +215,6 @@ class PlayIntStaticBitmapAnimation : public SceneChange {
 // TODO this effectively also contains an EventFlags, consider multiple inheritance
 // or maybe splitting EventFlags into a separate struct
 public:
-    struct SrcDestDesc {
-        uint16 frameId = 0;
-        Common::Rect src;
-        Common::Rect dest;
-    };
 
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
     virtual void execute(NancyEngine *engine) override;
@@ -230,7 +234,7 @@ public:
     Common::Array<Common::Rect> frameRects;
     // Describes how the animation will be displayed on a single
     // frame of the viewport
-    Common::Array<SrcDestDesc> srcDestRects;
+    Common::Array<BitmapDesc> bitmaps;
 
     uint16 currentFrame = 0;
     uint16 lastViewFrame = 0;
@@ -429,6 +433,11 @@ public:
 class ShowInventoryItem : public ActionRecord {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void execute(NancyEngine *engine) override;
+ 
+    uint16 objectID = 0;
+    Common::String imageName;
+    Common::Array<BitmapDesc> bitmaps;
 };
 
 class PlayDigiSoundAndDie : public SceneChange {
diff --git a/engines/nancy/datatypes.cpp b/engines/nancy/datatypes.cpp
index 816ad0b7d0..a90afa32f6 100644
--- a/engines/nancy/datatypes.cpp
+++ b/engines/nancy/datatypes.cpp
@@ -116,9 +116,9 @@ void Inventory::read(Common::SeekableReadStream &stream) {
     stream.skip(8);
     readRect(stream, emptySpaceSource);
 
-    char itemName[14];
+    char itemName[0x14];
     for (uint i = 0; i < 11; ++i) {
-        stream.read(itemName, 14);
+        stream.read(itemName, 0x14);
         items[i].name = Common::String(itemName);
         items[i].unknown = stream.readUint16LE();
         readRect(stream, items[i].sourceRect);
diff --git a/engines/nancy/graphics.cpp b/engines/nancy/graphics.cpp
index d065a3b7db..e9b100e88f 100644
--- a/engines/nancy/graphics.cpp
+++ b/engines/nancy/graphics.cpp
@@ -41,6 +41,7 @@ namespace Nancy {
 const Graphics::PixelFormat GraphicsManager::pixelFormat = Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0);
 const uint GraphicsManager::transColor = 0x3E0;
 
+
 GraphicsManager::GraphicsManager(NancyEngine *engine) :
         _engine(engine) {
     _screen.create(640, 480, pixelFormat);
@@ -72,6 +73,7 @@ GraphicsManager::~GraphicsManager() {
     _object0Surface.free();
     _inventoryBoxIconsSurface.free();
     _inventoryCursorsSurface.free();
+    _inventoryBitmapSurface.free();
     _object0Surface.free();
     _genericSurface.free();
 
@@ -176,7 +178,7 @@ void GraphicsManager::initSceneZRenderStructs(Common::Array<Common::String> &out
     outNames.push_back(initZRenderStruct(  "FRAME INV BOX", 6, true, ZRenderStruct::kNoTrans, nullptr,
                         new RenderFunction(this, &GraphicsManager::renderFrameInvBox)));
     
-    outNames.push_back(initZRenderStruct(  "INV BITMAP", 9, false, ZRenderStruct::kNoTrans));
+    outNames.push_back(initZRenderStruct(  "INV BITMAP", 9, false, ZRenderStruct::kNoTrans, &_inventoryBitmapSurface));
     outNames.push_back(initZRenderStruct(  "PRIMARY VIDEO", 8, false, ZRenderStruct::kNoTrans, nullptr,
                         new RenderFunction(this, &GraphicsManager::renderPrimaryVideo)));
     outNames.push_back(initZRenderStruct(  "SEC VIDEO 0", 8, false, ZRenderStruct::kTrans, &channels[0].surf));
@@ -390,6 +392,25 @@ bool GraphicsManager::playSecondaryMovie(uint16 &outFrameNr) {
     return false;
 }
 
+// Called when the order has changed
+void GraphicsManager::updateInvBox() {
+    Inventory &inventoryDesc = _engine->sceneManager->inventoryDesc;
+    for (uint i = 0; i < 4; ++i) {
+        if (i < inventoryBoxDesc.itemsOrder.size()) {
+            uint oi = inventoryBoxDesc.itemsOrder.size() - i - 1;
+            inventoryBoxDesc.onScreenItems[i].itemId = inventoryBoxDesc.itemsOrder[oi];
+            inventoryBoxDesc.onScreenItems[i].source = inventoryDesc.items[inventoryBoxDesc.itemsOrder[oi]].sourceRect;
+        } else {
+            inventoryBoxDesc.onScreenItems[i].itemId = -1;
+        }
+    }
+
+    // If size is 0 or 1, trigger the blinds closing/opening animation
+    if (inventoryBoxDesc.itemsOrder.size() < 2) {
+        inventoryBoxDesc.nextFrameTime = _engine->getTotalPlayTime() + inventoryDesc.shadesFrameTime;
+    }
+}
+
 void GraphicsManager::renderFrame() {
     ZRenderStruct &zr = getZRenderStruct("FRAME");
     Common::Point dest(zr.destRect.left, zr.destRect.top);
@@ -407,25 +428,45 @@ void GraphicsManager::renderFrame() {
 
 void GraphicsManager::renderFrameInvBox() {
     Inventory &inv = _engine->sceneManager->inventoryDesc;
-    SceneManager::InventoryBox &invBox = _engine->sceneManager->inventoryBoxDesc;
 
     // Draw the current four visible items if any
     for (uint i = 0; i < 4; ++i) {
-        if (invBox.onScreenItems[i].itemId != -1) {
-            Common::Point dest(invBox.onScreenItems[i].dest.left, invBox.onScreenItems[i].dest.top);
-            _screen.blitFrom(_inventoryBoxIconsSurface, invBox.onScreenItems->source, dest);
+        if (inventoryBoxDesc.onScreenItems[i].itemId != -1) {
+            Common::Point dest(inventoryBoxDesc.onScreenItems[i].dest.left, inventoryBoxDesc.onScreenItems[i].dest.top);
+            _screen.blitFrom(_inventoryBoxIconsSurface, inventoryBoxDesc.onScreenItems[i].source, dest);
         }
     }
 
+    // Check if we need to draw the next frame
+    if (inventoryBoxDesc.nextFrameTime != 0 &&
+        inventoryBoxDesc.nextFrameTime < _engine->getTotalPlayTime()) {
+            bool isInventoryEmpty = true;
+            for (uint i = 0; i < 11; ++i) {
+                if (_engine->playState.inventory.items[i] == PlayState::kTrue) {
+                    isInventoryEmpty = false;
+                    break;
+                }
+            }
+
+            inventoryBoxDesc.blindsAnimFrame += isInventoryEmpty ? 1 : -1;
+            if (inventoryBoxDesc.blindsAnimFrame < 0 || inventoryBoxDesc.blindsAnimFrame > 6) {
+                inventoryBoxDesc.blindsAnimFrame = CLIP<int16>(inventoryBoxDesc.blindsAnimFrame, 0, 6);
+                inventoryBoxDesc.nextFrameTime = 0;
+            } else {
+                inventoryBoxDesc.nextFrameTime = _engine->getTotalPlayTime() + _engine->sceneManager->inventoryDesc.shadesFrameTime;
+            }
+        }
+
     // Draw the shades
-    if (invBox.blindsAnimFrame < 7) {
+    if (inventoryBoxDesc.blindsAnimFrame < 7 &&
+        inventoryBoxDesc.blindsAnimFrame > 0) {
         // Draw left shade
         Common::Point dest(inv.shadesDst.left, inv.shadesDst.top);
-        _screen.blitFrom(_object0Surface, inv.shadesSrc[(6 - invBox.blindsAnimFrame) * 2], dest);
+        _screen.blitFrom(_object0Surface, inv.shadesSrc[(6 - inventoryBoxDesc.blindsAnimFrame) * 2], dest);
 
         // Draw right shade
-        dest.x = inv.shadesDst.right - inv.shadesSrc[(6 - invBox.blindsAnimFrame) * 2 + 1].width();
-        _screen.blitFrom(_object0Surface, inv.shadesSrc[(6 - invBox.blindsAnimFrame) * 2 + 1], dest);
+        dest.x = inv.shadesDst.right - inv.shadesSrc[(6 - inventoryBoxDesc.blindsAnimFrame) * 2 + 1].width();
+        _screen.blitFrom(_object0Surface, inv.shadesSrc[(6 - inventoryBoxDesc.blindsAnimFrame) * 2 + 1], dest);
     }
 }
 
diff --git a/engines/nancy/graphics.h b/engines/nancy/graphics.h
index 0b52cc5c96..43e7fcc69f 100644
--- a/engines/nancy/graphics.h
+++ b/engines/nancy/graphics.h
@@ -28,6 +28,7 @@
 #include "engines/nancy/datatypes.h"
 
 #include "common/func.h"
+#include "common/stack.h"
 
 #include "graphics/screen.h"
 
@@ -65,6 +66,29 @@ struct VideoChannel {
 
 class GraphicsManager {
 public:
+    struct InventoryBox {
+        struct Item {
+            Common::Rect source;
+            Common::Rect dest;
+            int16 itemId = -1;
+        };
+
+        // The generation of these coordinates is really long and stupid so
+        // just directly copy them for now
+        InventoryBox() {
+            onScreenItems[0].dest = Common::Rect(0x1B6, 0x152, 0x200, 0x18F);
+            onScreenItems[1].dest = Common::Rect(0x201, 0x152, 0x24B, 0x18F);
+            onScreenItems[2].dest = Common::Rect(0x1B6, 0x18F, 0x200, 0x1CC);
+            onScreenItems[3].dest = Common::Rect(0x201, 0x18F, 0x24B, 0x1CC);
+        }
+
+        Item onScreenItems[4];
+        Common::Array<uint16> itemsOrder;
+        int16 blindsAnimFrame = 6;
+
+        Time nextFrameTime;
+    };
+
     GraphicsManager(NancyEngine *engine);
     virtual ~GraphicsManager();
 
@@ -98,16 +122,18 @@ public:
     void setupSecondaryVideo(uint channel, uint16 begin, uint16 end, bool loop);
     void playSecondaryVideo(uint channel);
     void stopSecondaryVideo(uint channel);
-
     void loadSecondaryMovie(Common::String &filename);
     bool playSecondaryMovie(uint16 &outFrameNr);
 
+    void updateInvBox();
+
     Graphics::Surface _background;
     Graphics::Surface _frameTextBox;
     Graphics::Surface _primaryFrameSurface;
     Graphics::Surface _object0Surface;
     Graphics::Surface _inventoryBoxIconsSurface;
     Graphics::Surface _inventoryCursorsSurface;
+    Graphics::Surface _inventoryBitmapSurface;
     Graphics::Surface _genericSurface;
 
     VideoChannel channels[2];
@@ -115,6 +141,7 @@ public:
     AVFDecoder _secMovieDecoder;
 
     View viewportDesc;
+    InventoryBox inventoryBoxDesc;
 
     static const Graphics::PixelFormat pixelFormat;
     static const uint transColor;
diff --git a/engines/nancy/input.cpp b/engines/nancy/input.cpp
index a86a47155f..1586b37b8c 100644
--- a/engines/nancy/input.cpp
+++ b/engines/nancy/input.cpp
@@ -185,6 +185,7 @@ void InputManager::setMousePosition(const Common::Point &newPos) {
 }
 
 // Passing -1 means keeping the previous value
+// Style 0 is regular, 1 is red highlight, 2 is blue highlight, 4 is red highlight again
 void InputManager::setPointerBitmap(int16 id, int16 style, int16 isHoldingItem) {
     ZRenderStruct &zr = _engine->graphics->getZRenderStruct("CUR IMAGE CURSOR");
     
@@ -203,7 +204,7 @@ void InputManager::setPointerBitmap(int16 id, int16 style, int16 isHoldingItem)
         zr.sourceSurface = &_engine->graphics->_object0Surface;
     }
 
-    zr.sourceRect = cursorsData.rects[pointerId*3 + pointerStyle];
+    zr.sourceRect = cursorsData.rects[pointerId * 4 + pointerStyle];
 }
 
 void InputManager::initKeymaps(Common::KeymapArray &keymaps) {
diff --git a/engines/nancy/logic.cpp b/engines/nancy/logic.cpp
index 6b0a9c6ee8..8374d8f411 100644
--- a/engines/nancy/logic.cpp
+++ b/engines/nancy/logic.cpp
@@ -109,8 +109,25 @@ void Logic::processActionRecords() {
                         switch (dep.type) {
                             case kNone:
                                 record->satisfiedDependencies[i] = true;
+                                break;
                             case kInventory:
-                                // TODO
+                                switch (dep.condition) {
+                                    case 1:
+                                        // Item not in possession or held
+                                        if (_engine->playState.inventory.items[dep.label] == PlayState::kFalse &&
+                                            dep.label != _engine->playState.inventory.heldItem) {
+                                            record->satisfiedDependencies[i] = true;
+                                        }
+                                        break;
+                                    case 2:
+                                        if (_engine->playState.inventory.items[dep.label] == PlayState::kTrue ||
+                                            dep.label == _engine->playState.inventory.heldItem) {
+                                            record->satisfiedDependencies[i] = true;
+                                        }
+                                        break;
+                                    default:
+                                        break;
+                                }
                                 break;
                             case kEventFlag:
                                 if (_engine->playState.eventFlags[dep.label] == dep.condition)
diff --git a/engines/nancy/playstate.h b/engines/nancy/playstate.h
index 7565a1c1cf..9a352757e5 100644
--- a/engines/nancy/playstate.h
+++ b/engines/nancy/playstate.h
@@ -33,13 +33,18 @@ struct PlayState {
     enum Flag { kFalse = 1, kTrue = 2 }; // Could be the other way around
     enum TimeOfDay { kDay, kNight, kDuskDawn };
 
+    struct Inventory {
+        Inventory() { for (uint i = 0; i < 11; ++i) items[i] = kFalse; }
+        Flag items[11];
+        int16 heldItem = -1;
+    };
+
     // These two are used to keep track of what options the player picked in the
     // current conversation. Since they don't get stored they probably shouldn't
     // be here
     Flag logicConditions[30];
     Time logicConditionsTimestamps[30]; // Stores when the condition got satisfied
-    
-    Flag inventory[11];
+    Inventory inventory;
     Flag eventFlags[168];
     byte sceneHitCount[1000];
     uint16 difficulty; // 0, 1, 2
diff --git a/engines/nancy/scene.cpp b/engines/nancy/scene.cpp
index 912143d0bd..607530ea5d 100644
--- a/engines/nancy/scene.cpp
+++ b/engines/nancy/scene.cpp
@@ -70,6 +70,46 @@ void SceneManager::changeScene(uint16 id, uint16 frame, uint16 verticalOffset, b
     _state = kLoadNew;
 }
 
+void SceneManager::addObjectToInventory(uint16 id) {
+    if (_engine->playState.inventory.heldItem == id) {
+        _engine->playState.inventory.heldItem = -1;
+    }
+
+    GraphicsManager::InventoryBox &box = _engine->graphics->inventoryBoxDesc;
+    _engine->playState.inventory.items[id] = PlayState::kTrue;
+    box.itemsOrder.push_back(id);
+
+    // Update the inventory box
+    _engine->graphics->updateInvBox();
+}
+
+void SceneManager::pickUpObject(uint16 id) {
+    Common::Array<uint16> &order = _engine->graphics->inventoryBoxDesc.itemsOrder;
+    Common::Array<uint16> temp;
+
+    _engine->playState.inventory.items[id] = PlayState::kFalse;
+
+    // Pop elements from the order array until we find the correct one
+    for (int i = order.size() - 1; i >= 0; --i) {
+        uint16 thisElem = order.back();
+        order.pop_back();
+        if (thisElem == id) {
+            _engine->playState.inventory.heldItem = id;
+            break;
+        } else {
+            temp.push_back(thisElem);
+        }
+    }
+
+    // Return all elements in the same order except the one we removed
+    for (int i = temp.size() - 1; i >= 0; --i) {
+        order.push_back(temp[i]);
+    }
+
+    // Update the inventory box
+    _engine->graphics->updateInvBox();
+}
+
 void SceneManager::init() {
     for (uint i = 0; i < 168; ++i) {
         _engine->playState.eventFlags[i] = PlayState::Flag::kFalse;
@@ -379,9 +419,16 @@ void SceneManager::run() {
         } else if (hovered == InputManager::textBoxID) {
             // TODO
         } else if (hovered == InputManager::inventoryItemTakeID) {
-            // TODO
+            GraphicsManager::InventoryBox &box = _engine->graphics->inventoryBoxDesc;
+            Common::Point mousePos = _engine->input->getMousePosition();
+            for (uint i = 0; i < 4; ++i) {
+                if (box.onScreenItems[i].dest.contains(mousePos)) {
+                    pickUpObject(box.onScreenItems[i].itemId);
+                    break;
+                }
+            }
         } else if (hovered == InputManager::inventoryItemReturnID) {
-            // TODO
+            addObjectToInventory(_engine->playState.inventory.heldItem);
         } else if (hovered == InputManager::textBoxScrollbarID) {
             handleScrollbar(0);
         } else if (hovered == InputManager::inventoryScrollbarID) {
@@ -543,6 +590,7 @@ void SceneManager::handleMouse() {
     zr.destRect.top = zr.destRect.bottom = mousePos.y;
     movementDirection = 0;
     _engine->input->hoveredElementID = -1;
+    bool returnCursorSelected = false;
 
     View &view = _engine->graphics->viewportDesc;
 
@@ -552,6 +600,7 @@ void SceneManager::handleMouse() {
     // Check if the mouse is within the viewport
     if (view.destination.contains(viewportMouse)){
         _engine->input->setPointerBitmap(0, 0, 0);
+
         // We can scroll left and right
         if (currentScene.horizontalEdgeSize > 0) {
             if (viewportMouse.x < view.destination.left + currentScene.horizontalEdgeSize) {
@@ -569,7 +618,7 @@ void SceneManager::handleMouse() {
         }
 
         if (movementDirection != 0) {
-            _engine->input->setPointerBitmap(0, 2, 0);
+            _engine->input->setPointerBitmap(-1, 2, 0);
         } else {
             // Go through all action records and find hotspots
             Common::Array<ActionRecord *> &records = _engine->logic->getActionRecords();
@@ -588,7 +637,8 @@ void SceneManager::handleMouse() {
                         if (r->type == 0xE || r->type == 0x3D || r->type == 0x3E) {
                             // Set the pointer to the U-shaped arrow if
                             // the type is Hot1FrExitSceneChange, MapCallHot1Fr, or MapCallHotMultiframe
-                            _engine->input->setPointerBitmap(1, 0, 0);
+                            _engine->input->setPointerBitmap(0, 3, 0);
+                            returnCursorSelected = true;
                         } else {
                             _engine->input->setPointerBitmap(-1, 1, 0);
                         }
@@ -597,6 +647,11 @@ void SceneManager::handleMouse() {
                 }
             }
         }
+
+        // If we're holding an item we need to show it, but only in the viewport
+        if (_engine->playState.inventory.heldItem != -1 && !returnCursorSelected) {
+            _engine->input->setPointerBitmap(_engine->playState.inventory.heldItem + 3, -1, 1);
+        }
     } else {
         // Check if we're hovering above a UI element
         ZRenderStruct *uizr = &_engine->graphics->getZRenderStruct("CUR TB BAT SLIDER");
@@ -608,11 +663,16 @@ void SceneManager::handleMouse() {
             _engine->input->hoveredElementID = InputManager::inventoryScrollbarID;
             _engine->input->setPointerBitmap(1, 2, -1);
             handleScrollbar(1);
+        } else if (_engine->sceneManager->inventoryDesc.shadesDst.contains(mousePos)) {
+            if (_engine->playState.inventory.heldItem != -1) {
+                _engine->input->hoveredElementID = InputManager::inventoryItemReturnID;
+            } else {
+                _engine->input->hoveredElementID = InputManager::inventoryItemTakeID;
+            }
         } else {
             _engine->input->setPointerBitmap(1, 1, 0);
         }
-    }
-        
+    }     
 }
 
 void SceneManager::clearSceneData() {
diff --git a/engines/nancy/scene.h b/engines/nancy/scene.h
index b52fcbc339..11801fb236 100644
--- a/engines/nancy/scene.h
+++ b/engines/nancy/scene.h
@@ -58,6 +58,8 @@ public:
     void process();
 
     void changeScene(uint16 id, uint16 frame, uint16 verticalOffset, bool noSound);
+    void addObjectToInventory(uint16 id);
+    void pickUpObject(uint16 id);
 
 private:
     void init();
@@ -87,19 +89,6 @@ public:
         kCredits = 1 << 5,
         kMap = 1 << 6
     };
-
-    struct InventoryBox {
-        struct Item {
-            Common::Rect source;
-            Common::Rect dest;
-            int16 itemId = -1;
-        };
-
-        Item onScreenItems[4];
-        uint16 blindsAnimFrame = 6;
-
-        Time nextFrameTime;
-    };
     
     byte stateChangeRequests; // GameStateChange
 
@@ -110,7 +99,6 @@ public:
     bool doNotStartSound = false;
 
     Inventory inventoryDesc;
-    InventoryBox inventoryBoxDesc;
 
 private:
     NancyEngine *_engine;


Commit: 188ee86d2ff3a0506d8b5d4f8b51fb20522ea433
    https://github.com/scummvm/scummvm/commit/188ee86d2ff3a0506d8b5d4f8b51fb20522ea433
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Implement item interaction

Items can now interact with the viewport, allowing the player to open the living room chest, fix the gas leak in the kitchen, etc.

Changed paths:
    engines/nancy/action/actionrecord.h
    engines/nancy/datatypes.cpp
    engines/nancy/datatypes.h
    engines/nancy/logic.cpp
    engines/nancy/scene.cpp


diff --git a/engines/nancy/action/actionrecord.h b/engines/nancy/action/actionrecord.h
index b46666ad0c..d871ec9b4f 100644
--- a/engines/nancy/action/actionrecord.h
+++ b/engines/nancy/action/actionrecord.h
@@ -43,7 +43,8 @@ enum DependencyType : byte {
     kPlayerTime         = 6,
     // ...
     kSceneCount         = 9,
-    // ...
+    kResetOnNewDay      = 10,
+    kUseItem            = 11,
     kTimeOfDay          = 12,
     kTimerNotDone       = 13,
     kTimerDone          = 14,
@@ -75,7 +76,9 @@ public:
         orFlags(nullptr),
         isDone(false),
         hasHotspot(false),
-        state(ExecutionState::kBegin) {}
+        state(ExecutionState::kBegin),
+        days(-1),
+        itemRequired(-1) {}
     virtual ~ActionRecord() { delete[] dependencies; delete rawData; delete[] satisfiedDependencies; delete[] timers; delete orFlags; }
 
     virtual uint16 readData(Common::SeekableReadStream &stream) =0;
@@ -105,6 +108,8 @@ public:
     bool hasHotspot;                // 0x85
     Common::Rect hotspot;           // 0x89
     ExecutionState state;           // 0x91
+    int16 days;                     // 0x95
+    int8 itemRequired;              // 0x97
 };
 
 } // End of namespace Nancy
diff --git a/engines/nancy/datatypes.cpp b/engines/nancy/datatypes.cpp
index a90afa32f6..3a7a3926ab 100644
--- a/engines/nancy/datatypes.cpp
+++ b/engines/nancy/datatypes.cpp
@@ -120,7 +120,7 @@ void Inventory::read(Common::SeekableReadStream &stream) {
     for (uint i = 0; i < 11; ++i) {
         stream.read(itemName, 0x14);
         items[i].name = Common::String(itemName);
-        items[i].unknown = stream.readUint16LE();
+        items[i].oneTimeUse = stream.readUint16LE();
         readRect(stream, items[i].sourceRect);
     }
 }
diff --git a/engines/nancy/datatypes.h b/engines/nancy/datatypes.h
index 5d86463163..2e0a1ea04a 100644
--- a/engines/nancy/datatypes.h
+++ b/engines/nancy/datatypes.h
@@ -81,7 +81,7 @@ struct Cursors {
 struct Inventory {
     struct ItemDesc {
         Common::String name; // 0x00
-        byte unknown = 0; // 0x14
+        byte oneTimeUse = 0; // 0x14
         Common::Rect sourceRect; // 0x16
     };
 
diff --git a/engines/nancy/logic.cpp b/engines/nancy/logic.cpp
index 8374d8f411..f571cd1790 100644
--- a/engines/nancy/logic.cpp
+++ b/engines/nancy/logic.cpp
@@ -179,12 +179,45 @@ void Logic::processActionRecords() {
                                         break;
                                 }
                                 break;
-                            /*case 10:
-                                // TODO
+                            case kResetOnNewDay:
+                                if (record->days == -1) {
+                                    record->days = _engine->playState.playerTime.getDays();
+                                    record->satisfiedDependencies[i] = true;
+                                    break;
+                                }
+
+                                if (record->days < _engine->playState.playerTime.getDays()) {
+                                    record->days = _engine->playState.playerTime.getDays();
+                                    for (uint j = 0; j < record->numDependencies; ++j) {
+                                        if (record->dependencies[j].type == kPlayerTime) {
+                                            record->satisfiedDependencies[j] = false;
+                                        }
+                                    }
+                                }
                                 break;
-                            case 11:
-                                // TODO
-                                break;*/
+                            case kUseItem: {
+                                bool hasUnsatisfiedDeps = false;
+                                if (record->numDependencies > 1) {
+                                    for (uint j = 0; j < record->numDependencies; ++j) {
+                                        if (j != i && record->satisfiedDependencies[j] == false) {
+                                            hasUnsatisfiedDeps = true;
+                                        }
+                                    }
+                                }
+
+                                if (hasUnsatisfiedDeps) {
+                                    break;
+                                }
+
+                                record->itemRequired = dep.label;
+
+                                if (dep.condition == 1) {
+                                    record->itemRequired += 100;
+                                }
+                                
+                                record->satisfiedDependencies[i] = true;
+                                break;
+                            }
                             case kTimeOfDay:
                                 if (dep.label == (byte)_engine->playState.timeOfDay)
                                     record->satisfiedDependencies[i] = true;
diff --git a/engines/nancy/scene.cpp b/engines/nancy/scene.cpp
index 607530ea5d..484e9013b9 100644
--- a/engines/nancy/scene.cpp
+++ b/engines/nancy/scene.cpp
@@ -451,11 +451,44 @@ void SceneManager::run() {
                     hovered == InputManager::passwordPuzzleEndID) {
             // TODO
         } else {
-            // ID must be an action record's
+            // Not a UI element, ID must be an action record's
             ActionRecord *rec = _engine->logic->getActionRecord(hovered);
             if (rec->isActive /*&& another condition !- 0*/) {
-                // TODO item holding logic
-                rec->state = ActionRecord::ExecutionState::kActionTrigger;
+                bool shouldTrigger = false;
+                int16 &heldItem = _engine->playState.inventory.heldItem;
+                if (rec->itemRequired != -1) {
+                    if (heldItem == -1 && rec->itemRequired == -2) {
+                        shouldTrigger = true;
+                    } else {
+                        if (rec->itemRequired <= 100) {
+                            if (heldItem == rec->itemRequired) {
+                                shouldTrigger = true;
+                            }
+                        } else if (rec->itemRequired <= 110 && rec->itemRequired - 100 != heldItem) {
+                            // IDs 100 - 110 mean the record will activate when the object is _not_ the specified one
+                            shouldTrigger = true;
+                        }
+                    }
+                } else {
+                    shouldTrigger = true;
+                }
+                if (shouldTrigger) {
+                    rec->state = ActionRecord::ExecutionState::kActionTrigger;
+                    
+                    if (rec->itemRequired > 100 && rec->itemRequired <= 110) {
+                        rec->itemRequired -= 100;
+                    }
+
+                    // Re-add the object to the inventory unless it's marked as a one-time use
+                    if (rec->itemRequired == heldItem && rec->itemRequired != -1) {
+                        if (inventoryDesc.items[heldItem].oneTimeUse != 0) {
+                            addObjectToInventory(heldItem);
+                        }
+
+                        heldItem = -1;
+                    }
+                }
+                
             }
         }
 


Commit: 673bfec2090444f6bb1a4ca966bf049462cb34a0
    https://github.com/scummvm/scummvm/commit/673bfec2090444f6bb1a4ca966bf049462cb34a0
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Initial dialogue implementation

Implemented most of the PlayPrimaryVideoChan0 action record, which is used in all dialogue and the intro cutscene. For now the audio and video are out of sync, the scrollbar doesn't work, and the mouse isn't constrained.

Changed paths:
  A engines/nancy/action/primaryvideo.cpp
  A engines/nancy/action/primaryvideo.h
  A engines/nancy/action/responses.cpp
  A engines/nancy/textbox.cpp
  A engines/nancy/textbox.h
    engines/nancy/action/arfactory_v1.cpp
    engines/nancy/action/recordtypes.cpp
    engines/nancy/action/recordtypes.h
    engines/nancy/audio.cpp
    engines/nancy/datatypes.cpp
    engines/nancy/datatypes.h
    engines/nancy/graphics.cpp
    engines/nancy/graphics.h
    engines/nancy/logic.cpp
    engines/nancy/logic.h
    engines/nancy/module.mk
    engines/nancy/nancy.cpp
    engines/nancy/nancy.h
    engines/nancy/scene.cpp
    engines/nancy/scene.h


diff --git a/engines/nancy/action/arfactory_v1.cpp b/engines/nancy/action/arfactory_v1.cpp
index c4423e9712..a7b4b5760a 100644
--- a/engines/nancy/action/arfactory_v1.cpp
+++ b/engines/nancy/action/arfactory_v1.cpp
@@ -23,6 +23,7 @@
 #include "engines/nancy/logic.h"
 #include "engines/nancy/action/recordtypes.h"
 #include "engines/nancy/action/actionrecord.h"
+#include "engines/nancy/action/primaryvideo.h"
 
 namespace Nancy {
 
diff --git a/engines/nancy/action/primaryvideo.cpp b/engines/nancy/action/primaryvideo.cpp
new file mode 100644
index 0000000000..e5adadb03a
--- /dev/null
+++ b/engines/nancy/action/primaryvideo.cpp
@@ -0,0 +1,332 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/nancy/action/primaryvideo.h"
+
+#include "engines/nancy/action/responses.cpp"
+
+#include "engines/nancy/nancy.h"
+#include "engines/nancy/scene.h"
+#include "engines/nancy/logic.h"
+#include "engines/nancy/nancy.h"
+#include "engines/nancy/graphics.h"
+#include "engines/nancy/audio.h"
+
+#include "common/file.h"
+#include "common/random.h"
+
+namespace Nancy {
+
+// Simple helper function to read rectangles
+static void readRect(Common::SeekableReadStream &stream, Common::Rect &inRect) {
+    inRect.left = stream.readUint32LE();
+    inRect.top = stream.readUint32LE();
+    inRect.right = stream.readUint32LE();
+    inRect.bottom = stream.readUint32LE();
+}
+
+uint16 PlayPrimaryVideoChan0::readData(Common::SeekableReadStream &stream) {
+    uint16 bytesRead = stream.pos();
+
+    char name[10];
+    stream.read(name, 10);
+    videoName = Common::String(name);
+
+    stream.skip(0x13);
+
+    readRect(stream, src);
+    readRect(stream, dest);
+
+    char *rawText = new char[1500]();
+    stream.read(rawText, 1500);
+    assembleText(rawText, text, 1500);
+    delete[] rawText;
+
+    stream.read(name, 10);
+    soundName = Common::String(name);
+    soundChannelID = stream.readUint16LE();
+
+    stream.skip(8);
+
+    numRepeats = stream.readUint16LE();
+
+    stream.skip(4);
+
+    volume = stream.readUint16LE();
+
+    stream.skip(0x29);
+    conditionalResponseCharacterID = stream.readByte();
+    goodbyeResponseCharacterID = stream.readByte();
+    numSceneChanges = stream.readByte();
+    shouldPopScene = stream.readByte() == 1;
+    SceneChange::readData(stream);
+
+    stream.seek(bytesRead + 0x69C);
+
+    uint16 numResponses = stream.readUint16LE();
+    if (numResponses > 0) {
+        for (uint i = 0; i < numResponses; ++i) {
+            uint16 numConditionFlags = stream.readUint16LE();
+            responses.push_back(ResponseStruct());
+            ResponseStruct &response = responses[i];
+
+            if (numConditionFlags > 0) {
+                for (uint16 j = 0; j < numConditionFlags; ++j) {
+                    response.conditionFlags.push_back(ConditionFlags());
+                    ConditionFlags &flags = response.conditionFlags[j];
+                    stream.read(flags.unknown, 5);
+                }
+            }
+            rawText = new char[400];
+            stream.read(rawText, 400);
+            assembleText(rawText, response.text, 400);
+            delete[] rawText;
+
+            stream.read(name, 10);
+            response.soundName = name;
+            stream.skip(1);
+            response.sceneChange.readData(stream);
+            response.flagDesc.label = stream.readSint16LE();
+            response.flagDesc.flag = (PlayState::Flag)stream.readByte();
+
+            stream.skip(0x32);
+        }
+    }
+
+    uint16 numSceneBranchStructs = stream.readUint16LE();
+    if (numSceneBranchStructs > 0) {
+        // TODO
+    }
+
+    uint16 numFlagsStructs = stream.readUint16LE();
+    if (numFlagsStructs > 0)  {
+        for (uint16 i = 0; i < numFlagsStructs; ++i) {
+            uint16 numConditionFlags = stream.readUint16LE();
+            flagsStructs.push_back(FlagsStruct());
+            FlagsStruct &flagsStruct = flagsStructs[flagsStructs.size()-1];
+
+            if (numConditionFlags > 0) {
+                // Not sure about this
+                if (numConditionFlags > 0) {
+                    for (uint16 j = 0; j < numConditionFlags; ++j) {
+                        flagsStruct.conditionFlags.push_back(ConditionFlags());
+                        ConditionFlags &flags = flagsStruct.conditionFlags[flagsStruct.conditionFlags.size()-1];
+                        stream.read(flags.unknown, 5);
+                    }
+                }
+            }
+
+            flagsStruct.type = (FlagsStruct::ConditionType)stream.readByte();
+            flagsStruct.label = stream.readSint16LE();
+            flagsStruct.flag = (PlayState::Flag)stream.readByte();
+        }
+    }
+
+    bytesRead = stream.pos() - bytesRead;
+    return bytesRead;
+}
+
+void PlayPrimaryVideoChan0::execute(NancyEngine *engine) {
+    ZRenderStruct &zr = engine->graphics->getZRenderStruct("PRIMARY VIDEO");
+    AVFDecoder &decoder = engine->graphics->_primaryVideoDecoder;
+    View &viewportDesc = engine->graphics->viewportDesc;
+    switch (state) {
+        case kBegin:
+            zr.sourceRect = src;
+            zr.destRect = dest;
+            zr.destRect.left += viewportDesc.destination.left;
+            zr.destRect.top += viewportDesc.destination.top;
+            zr.destRect.right += viewportDesc.destination.left;
+            zr.destRect.bottom += viewportDesc.destination.top;
+            zr.isActive = true;
+            if (decoder.isVideoLoaded()) {
+                decoder.close();
+            }
+            decoder.loadFile(videoName + ".avf");
+            engine->sound->loadSound(soundName, soundChannelID, numRepeats, volume);
+            engine->sound->pauseSound(soundChannelID, false);
+            state = kRun;
+            // fall through
+        case kRun:
+            if (!hasDrawnTextbox) {
+                hasDrawnTextbox = true;
+                engine->graphics->_textbox.clear();
+                engine->graphics->_textbox.processTextLine(text, 1);
+
+                // Add responses when conditions have been satisfied
+                if (conditionalResponseCharacterID != 10) {
+                    addConditionalResponses(engine);
+                }
+
+                if (goodbyeResponseCharacterID != 10) {
+                    addGoodbye(engine);
+                }
+
+                for (uint i = 0; i < responses.size(); ++i) {
+                    auto &res = responses[i];
+                    engine->graphics->_textbox.processResponse(res.text, 1, i, res.soundName);
+                }
+
+                ZRenderStruct &fr = engine->graphics->getZRenderStruct("FRAME TB SURF");
+                fr.isActive = true;
+                fr.sourceRect = Common::Rect(fr.destRect.width(), fr.destRect.height());
+            }
+            if (!engine->sound->isSoundPlaying(soundChannelID)) {
+                if (responses.size() == 0) {
+                    state = kActionTrigger;
+                } else {
+                    for (uint i = 0; i < 30; ++i) {
+                        if (engine->playState.logicConditions[i] == PlayState::kTrue) {
+                            pickedResponse = i;
+                            break;
+                        }
+                    }
+
+                    if (pickedResponse != -1) {
+                        sceneChange = responses[pickedResponse].sceneChange;
+                        engine->sound->loadSound(responses[pickedResponse].soundName, soundChannelID, numRepeats, volume);
+                        engine->sound->pauseSound(soundChannelID, false);
+                        state = kActionTrigger;
+                    }
+                }
+            }
+            break;
+        case kActionTrigger:
+            // process flags structs
+            for (auto flags : flagsStructs) {
+                bool conditionsSatisfied = true;
+                for (auto cond : flags.conditionFlags) {
+                    // TODO
+                    error("Condition flags not evaluated");
+                }
+
+                if (conditionsSatisfied) {
+                    switch (flags.type) {
+                        case FlagsStruct::kEventFlags:
+                            engine->playState.eventFlags[flags.label] = flags.flag;
+                            break;
+                        case FlagsStruct::kInventory:
+                            if (flags.flag == PlayState::kTrue) {
+                                engine->sceneManager->addObjectToInventory(flags.label);
+                            } else {
+                                engine->sceneManager->removeObjectFromInventory(flags.label);
+                            }
+                            break;
+                        default:
+                            break;
+                    }
+                }
+            }
+
+            if (pickedResponse != -1) {
+                int16 label = responses[pickedResponse].flagDesc.label;
+                if (label != -1) {
+                    engine->playState.eventFlags[label] = responses[pickedResponse].flagDesc.flag;
+                }
+            }
+
+            if (!engine->sound->isSoundPlaying(soundChannelID)) {
+                if (shouldPopScene) {
+                    engine->sceneManager->popScene();
+                } else {
+                    SceneChange::execute(engine);
+                }
+            }
+
+            // awful hack
+            engine->logic->ignorePrimaryVideo = true;
+
+            break;
+    }
+}
+
+void PlayPrimaryVideoChan0::addConditionalResponses(NancyEngine *engine) {
+    for (auto &res : nancy1ConditionalResponses) {
+        if (res.characterID == conditionalResponseCharacterID) {
+            bool isSatisfied = true;
+            for (auto & cond : res.conditions) {
+                if (cond.label == -1) {
+                    break;
+                }
+
+                if (engine->playState.eventFlags[cond.label] != cond.flag) {
+                    isSatisfied = false;
+                    break;
+                }
+            }
+
+            if (isSatisfied) {
+                Common::File file;
+                char snd[10];
+
+                file.open("game.exe");
+                file.seek(nancy1ResponseBaseFileOffset + res.fileOffset);
+                file.read(snd, 8);
+
+                responses.push_back(ResponseStruct());
+                ResponseStruct &newResponse = responses.back();
+                newResponse.soundName = snd;
+                newResponse.text = file.readString();
+                newResponse.sceneChange.sceneID = res.sceneID;
+                newResponse.sceneChange.doNotStartSound = true;
+
+                file.close();
+            }
+        }
+    }
+}
+
+void PlayPrimaryVideoChan0::addGoodbye(NancyEngine *engine) {
+    for (auto res : nancy1Goodbyes) {
+        if (res.characterID == goodbyeResponseCharacterID) {
+            Common::File file;
+            char snd[10];
+
+            file.open("game.exe");
+            file.seek(nancy1ResponseBaseFileOffset + res.fileOffset);
+            file.read(snd, 8);
+
+            responses.push_back(ResponseStruct());
+            ResponseStruct &newResponse = responses.back();
+            newResponse.soundName = snd;
+            newResponse.text = file.readString();
+            // response is picked randomly
+            newResponse.sceneChange.sceneID = res.sceneIDs[engine->_rnd->getRandomNumber(3)];
+
+            file.close();
+        }
+    }
+}
+
+void PlayPrimaryVideoChan0::assembleText(char *rawCaption, Common::String &output, uint size) {
+    for (uint i = 0; i < size; ++i) {
+        // A single line can be broken up into bits, look for them and
+        // concatenate them when we're done
+        if (rawCaption[i] != 0) {
+            Common::String newBit(rawCaption + i);
+            output += newBit;
+            i += newBit.size();
+        }
+    }
+}
+
+} // End of namespace Nancy
\ No newline at end of file
diff --git a/engines/nancy/action/primaryvideo.h b/engines/nancy/action/primaryvideo.h
new file mode 100644
index 0000000000..75f5bb9be3
--- /dev/null
+++ b/engines/nancy/action/primaryvideo.h
@@ -0,0 +1,92 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef NANCY_ACTION_PRIMARYVIDEO_H
+#define NANCY_ACTION_PRIMARYVIDEO_H
+
+#include "engines/nancy/action/recordtypes.h"
+
+namespace Nancy {
+
+class NancyEngine;
+
+class PlayPrimaryVideoChan0 : public SceneChange {
+
+struct ConditionFlags {
+    byte unknown[5];
+};
+
+struct ResponseStruct {
+    Common::Array<ConditionFlags> conditionFlags; // 0x02
+    Common::String text; // 0x06
+    Common::String soundName; // 0x196
+    SceneChangeDesc sceneChange; // 0x1A0
+    FlagDesc flagDesc; // 0x1A8
+};
+
+struct FlagsStruct {
+    enum ConditionType : byte { kNone = 0, kEventFlags = 1, kInventory = 2 };
+    Common::Array<ConditionFlags> conditionFlags;
+
+    ConditionType type;
+    int16 label;
+    PlayState::Flag flag;
+};
+
+public:
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void execute(NancyEngine *engine) override;
+    
+    // Functions for handling the built-in dialogue responses found in the executable
+    void addConditionalResponses(NancyEngine *engine);
+    void addGoodbye(NancyEngine *engine);
+
+    Common::String videoName; // 0x00
+    Common::Rect src; // 0x1D
+    Common::Rect dest; // 0x2D
+    Common::String text; // 0x3D
+
+    Common::String soundName; // 0x619, TODO make a proper soundDesc struct
+    uint16 soundChannelID; // 0x623
+    uint16 numRepeats; // 0x62D
+    uint16 volume; // 0x633
+
+    byte conditionalResponseCharacterID; // 0x65E
+    byte goodbyeResponseCharacterID; // 0x65F
+    byte numSceneChanges; // 0x660, not sure
+    bool shouldPopScene; // 0x661
+    // SceneChange data is at 0x662
+
+    Common::Array<ResponseStruct> responses;
+    Common::Array<FlagsStruct> flagsStructs;
+
+    bool hasDrawnTextbox = false;
+    int16 pickedResponse = -1;
+
+private:
+    void assembleText(char *rawCaption, Common::String &output, uint size);
+
+};
+
+}
+
+#endif // NANCY_ACTION_PRIMARYVIDEO_H
\ No newline at end of file
diff --git a/engines/nancy/action/recordtypes.cpp b/engines/nancy/action/recordtypes.cpp
index 7f8681fadc..a4b5fc9249 100644
--- a/engines/nancy/action/recordtypes.cpp
+++ b/engines/nancy/action/recordtypes.cpp
@@ -75,16 +75,20 @@ void EventFlagsDesc::execute(NancyEngine *engine) {
     }
 }
 
-uint16 SceneChange::readData(Common::SeekableReadStream &stream) {
+void SceneChangeDesc::readData(Common::SeekableReadStream &stream) {
     sceneID = stream.readUint16LE();
     frameID = stream.readUint16LE();
     verticalOffset = stream.readUint16LE();
     doNotStartSound = (bool)(stream.readUint16LE());
+}
+
+uint16 SceneChange::readData(Common::SeekableReadStream &stream) {
+    sceneChange.readData(stream);
     return 8;
 }
 
 void SceneChange::execute(NancyEngine *engine) {
-    engine->sceneManager->changeScene(sceneID, frameID, verticalOffset, doNotStartSound);
+    engine->sceneManager->changeScene(sceneChange.sceneID, sceneChange.frameID, sceneChange.verticalOffset, sceneChange.doNotStartSound);
     isDone = true;
 }
 
@@ -164,60 +168,6 @@ uint16 StartStopPlayerScrolling::readData(Common::SeekableReadStream &stream) {
     return 1;
 }
 
-uint16 PlayPrimaryVideoChan0::readData(Common::SeekableReadStream &stream) {
-    uint16 bytesRead = stream.pos();
-    stream.read(videoData, 0x69C);
-
-    uint16 numResponses = stream.readUint16LE();
-    if (numResponses > 0) {
-        for (uint i = 0; i < numResponses; ++i) {
-            uint16 numConditionFlags = stream.readUint32LE();
-            responses.push_back(ResponseStruct());
-            ResponseStruct &response = responses[responses.size()-1];
-
-            if (numConditionFlags > 0) {
-                for (uint16 j = 0; j < numConditionFlags; ++j) {
-                    response.conditionFlags.push_back(ConditionFlags());
-                    ConditionFlags &flags = response.conditionFlags[response.conditionFlags.size()-1];
-                    stream.read(flags.unknown, 5);
-                }
-            }
-
-            stream.read(response.unknown, 0x1D8);
-        }
-    }
-
-    uint16 numSceneBranchStructs = stream.readUint16LE();
-    if (numSceneBranchStructs > 0) {
-        // TODO
-    }
-
-    uint16 numFlagsStructs = stream.readUint16LE();
-    if (numFlagsStructs > 0)  {
-        for (uint16 i = 0; i < numFlagsStructs; ++i) {
-            uint16 numConditionFlags = stream.readUint16LE();
-            flagsStructs.push_back(FlagsStruct());
-            FlagsStruct &flagsStruct = flagsStructs[flagsStructs.size()-1];
-
-            if (numConditionFlags > 0) {
-                // Not sure about this
-                if (numConditionFlags > 0) {
-                for (uint16 j = 0; j < numConditionFlags; ++j) {
-                    flagsStruct.conditionFlags.push_back(ConditionFlags());
-                    ConditionFlags &flags = flagsStruct.conditionFlags[flagsStruct.conditionFlags.size()-1];
-                    stream.read(flags.unknown, 5);
-                }
-            }
-            }
-
-            flagsStruct.unknown = stream.readUint32LE();
-        }
-    }
-
-    bytesRead = stream.pos() - bytesRead;
-    return bytesRead;
-}
-
 uint16 PlaySecondaryVideo::readData(Common::SeekableReadStream &stream) {
     char buf[10];
     stream.read(buf, 10);
@@ -311,6 +261,7 @@ void PlaySecondaryVideo::execute(NancyEngine *engine) {
             break;
         }
         case kActionTrigger:
+            engine->sceneManager->pushScene();
             SceneChange::execute(engine);
             break;
     }
@@ -895,7 +846,7 @@ void PlayDigiSoundAndDie::execute(NancyEngine *engine) {
             }
             break;
         case kActionTrigger:
-            if (sceneID != 9999) {
+            if (sceneChange.sceneID != 9999) {
                 SceneChange::execute(engine);
             }
             break;
diff --git a/engines/nancy/action/recordtypes.h b/engines/nancy/action/recordtypes.h
index 05f5b56bd8..38b8951546 100644
--- a/engines/nancy/action/recordtypes.h
+++ b/engines/nancy/action/recordtypes.h
@@ -74,15 +74,22 @@ struct EventFlagsDesc {
     void execute(NancyEngine *engine);
 };
 
+// Describes a scene transition
+struct SceneChangeDesc {
+    uint16 sceneID = 0;
+    uint16 frameID = 0;
+    uint16 verticalOffset = 0;
+    bool doNotStartSound = false;
+
+    void readData(Common::SeekableReadStream &stream);
+};
+
 class SceneChange : public ActionRecord {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
     virtual void execute(NancyEngine *engine) override;
 
-    uint16 sceneID = 0;
-    uint16 frameID = 0;
-    uint16 verticalOffset = 0;
-    bool doNotStartSound = false;
+    SceneChangeDesc sceneChange;
 };
 
 class HotMultiframeSceneChange : public SceneChange {
@@ -120,30 +127,6 @@ public:
     byte type = 0;
 };
 
-class PlayPrimaryVideoChan0 : public ActionRecord {
-
-struct ConditionFlags {
-    byte unknown[5];
-};
-
-struct ResponseStruct {
-    Common::Array<ConditionFlags> conditionFlags;
-    byte unknown[0x1d8]; // TODO
-};
-
-struct FlagsStruct {
-    Common::Array<ConditionFlags> conditionFlags;
-    uint32 unknown; // TODO
-};
-
-public:
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
-
-    byte videoData[0x69C]; // TODO this is its own class
-    Common::Array<ResponseStruct> responses;
-    Common::Array<FlagsStruct> flagsStructs;
-};
-
 // Base class for PlaySecondaryVideoChan0 and PlaySecondaryVideoChan1
 class PlaySecondaryVideo : public SceneChange {
 public:
diff --git a/engines/nancy/action/responses.cpp b/engines/nancy/action/responses.cpp
new file mode 100644
index 0000000000..1a8b7e179b
--- /dev/null
+++ b/engines/nancy/action/responses.cpp
@@ -0,0 +1,626 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/nancy/action/recordtypes.h"
+#include "common/array.h"
+
+namespace Nancy {
+
+struct ConditionalResponseDesc {
+    byte characterID; // 0: Daryl, 1: Connie, 2: Hal, 3: Hulk
+    uint fileOffset;
+    uint16 sceneID;
+    FlagDesc conditions[7];
+};
+
+struct GoodbyeDesc {
+    byte characterID;
+    uint fileOffset;
+    uint16 sceneIDs[4];
+};
+
+static const uint nancy1ResponseBaseFileOffset = 0xB1FE0; // TODO there could be more than one version of the exe
+
+#define EMPTY_DESC {-1, PlayState::kFalse }
+
+static const GoodbyeDesc nancy1Goodbyes[] = {
+    // Daryl
+    {
+        0,
+        0x11B0,
+        { 0xC94, 0xC95, 0xC96, 0xC97}
+    },
+
+    // Connie
+    {
+        1,
+        0x11D8,
+        { 0xFC, 0x9D8, 0x9D9, 0x9DB }
+    },
+
+    // Hal
+    {
+        2,
+        0x11FC,
+        { 0x1C3, 0x1C4, 0x1C5, 0x1C6}
+    },
+
+    // Hulk
+    {
+        3,
+        0x1228,
+        { 0xCE2, 0xCE0, 0xCE2, 0xCE0 } // only two responses
+    }
+};
+
+static const ConditionalResponseDesc nancy1ConditionalResponses[] = {
+    // Daryl
+    {
+        0, 
+        0x840,
+        0x7C,
+        {
+            { 0x1D, PlayState::kTrue },
+            { 0x39, PlayState::kFalse },
+            EMPTY_DESC
+        }
+    },
+
+    {
+        0,
+        0x804,
+        0x7F,
+        {
+            { 0x13, PlayState::kTrue },
+            { 0x37, PlayState::kFalse },
+            EMPTY_DESC
+        }
+    },
+
+    {
+        0,
+        0x7BC,
+        0x81,
+        {
+            { 0xB, PlayState::kTrue },
+            { 0x38, PlayState::kFalse },
+            EMPTY_DESC
+        }
+    },
+
+    {
+        0,
+        0x750,
+        0x83,
+        {
+            { 0, PlayState::kTrue },
+            { 1, PlayState::kFalse },
+            { 0x6B, PlayState::kFalse },
+            EMPTY_DESC
+        }
+    },
+
+    {
+        0,
+        0x6F4,
+        0x84,
+        {
+            { 0x64, PlayState::kTrue },
+            { 0x1E, PlayState::kFalse },
+            { 0x14, PlayState::kFalse },
+            { 0xC, PlayState::kFalse },
+            { 0x6C, PlayState::kFalse },
+            EMPTY_DESC
+        }
+    },
+
+    {
+        0,
+        0x5EC,
+        0x86,
+        {
+            { 0x6D, PlayState::kFalse },
+            { 0x6, PlayState::kTrue },
+            { 0x8, PlayState::kTrue },
+            { 0x5E, PlayState::kTrue },
+            { 0x17, PlayState::kTrue },
+            { 0x24, PlayState::kTrue },
+            { 0x9, PlayState::kTrue }
+        }
+    },
+
+    {
+        0,
+        0x554,
+        0x8B,
+        {
+            { 0x6E, PlayState::kFalse },
+            { 0x24, PlayState::kTrue },
+            { 0x9, PlayState::kTrue },
+            { 0x5E, PlayState::kFalse },
+            { 0x8, PlayState::kFalse },
+            EMPTY_DESC
+        }
+    },
+
+    {
+        0,
+        0x4F0,
+        0x8D,
+        {
+            { 0x6F, PlayState::kFalse },
+            { 0x5E, PlayState::kTrue },
+            { 0x24, PlayState::kTrue },
+            { 0x9, PlayState::kTrue },
+            { 0x8, PlayState::kFalse },
+            EMPTY_DESC
+        }
+    },
+
+    {
+        0,
+        0x458,
+        0x8F,
+        {
+            { 0x70, PlayState::kFalse },
+            { 0x24, PlayState::kTrue },
+            { 0x9, PlayState::kTrue },
+            { 0x6, PlayState::kTrue },
+            { 0x8, PlayState::kTrue },
+            { 0x5E, PlayState::kFalse },
+            EMPTY_DESC
+        }
+    },
+
+    {
+        0,
+        0x3BC,
+        0x90,
+        {
+            { 0x71, PlayState::kFalse },
+            { 0x5E, PlayState::kTrue },
+            { 0x24, PlayState::kFalse },
+            { 0x8, PlayState::kFalse },
+            EMPTY_DESC
+        }
+    },
+
+    {
+        0,
+        0x320,
+        0x91,
+        {
+            { 0x72, PlayState::kFalse },
+            { 0x5E, PlayState::kTrue },
+            { 0x8, PlayState::kTrue },
+            { 0x6, PlayState::kTrue },
+            { 0x24, PlayState::kFalse },
+            EMPTY_DESC
+        }
+    },
+
+    {
+        0,
+        0x2AC,
+        0x92,
+        {
+            { 0x73, PlayState::kFalse },
+            { 0x8, PlayState::kTrue },
+            { 0x6, PlayState::kTrue },
+            { 0x5E, PlayState::kFalse },
+            { 0x24, PlayState::kFalse },
+            EMPTY_DESC
+        }
+    },
+
+    {
+        0,
+        0x1F0,
+        0x96,
+        {
+            { 0x74, PlayState::kFalse },
+            { 0x1D, PlayState::kTrue },
+            { 0x13, PlayState::kTrue },
+            { 0xB, PlayState::kTrue },
+            { 0x5E, PlayState::kFalse },
+            { 0x24, PlayState::kFalse },
+            { 0x8, PlayState::kFalse }
+        }
+    },
+
+    {
+        0,
+        0x190,
+        0x97,
+        {
+            { 0x27, PlayState::kFalse },
+            { 0x5, PlayState::kTrue },
+            EMPTY_DESC
+        }
+    },
+
+    {
+        0,
+        0xF0,
+        0x9C,
+        {
+            { 0x28, PlayState::kTrue },
+            { 0x75, PlayState::kFalse },
+            EMPTY_DESC
+        }
+    },
+
+    {
+        0,
+        0x94,
+        0x93,
+        {
+            { 0xC, PlayState::kFalse },
+            { 0x6, PlayState::kTrue },
+            { 0x76, PlayState::kFalse },
+            EMPTY_DESC
+        }
+    },
+
+    {
+        0,
+        0x58,
+        0x94,
+        {
+            { 0x14, PlayState::kFalse },
+            { 0x4, PlayState::kTrue },
+            { 0x77, PlayState::kFalse },
+            EMPTY_DESC
+        }
+    },
+
+    {
+        0,
+        0,
+        0x95,
+        {
+            { 0x1E, PlayState::kFalse },
+            { 0x63, PlayState::kTrue },
+            { 0x78, PlayState::kFalse },
+            EMPTY_DESC
+        }
+    },
+
+    // Connie
+    {
+        1,
+        0xBE4,
+        0xE9,
+        {
+            { 0x1D, PlayState::kTrue },
+            { 0x18, PlayState::kFalse },
+            EMPTY_DESC
+        }
+    },
+
+    {
+        1,
+        0xB8C,
+        0xEA,
+        {
+            { 0x1F, PlayState::kTrue },
+            { 0x19, PlayState::kFalse },
+            EMPTY_DESC
+        }
+    },
+
+    {
+        1,
+        0xB54,
+        0xEB,
+        {
+            { 0xB, PlayState::kTrue },
+            { 0x1A, PlayState::kFalse },
+            EMPTY_DESC
+        }
+    },
+
+    {
+        1,
+        0xB14,
+        0xEC,
+        {
+            { 0x26, PlayState::kTrue },
+            { 0x1C, PlayState::kFalse },
+            EMPTY_DESC
+        }
+    },
+
+    {
+        1,
+        0xABC,
+        0xED,
+        {
+            { 0, PlayState::kTrue },
+            { 1, PlayState::kFalse },
+            { 0x79, PlayState::kFalse },
+            EMPTY_DESC
+        }
+    },
+
+    {
+        1,
+        0xA00,
+        0xEE,
+        {
+            { 2, PlayState::kTrue },
+            { 3, PlayState::kTrue },
+            { 0x17, PlayState::kFalse },
+            EMPTY_DESC
+        }
+    },
+
+    {
+        1,
+        0x6F4,
+        0xEF,
+        {
+            { 0x64, PlayState::kTrue },
+            { 0x16, PlayState::kFalse },
+            EMPTY_DESC
+        }
+    },
+
+    {
+        1,
+        0x968,
+        0xF0,
+        {
+            { 0x5, PlayState::kTrue },
+            { 0x14, PlayState::kFalse },
+            EMPTY_DESC
+        }
+    },
+
+    {
+        1,
+        0x8C8,
+        0xF5,
+        {
+            { 0x28, PlayState::kTrue },
+            EMPTY_DESC
+        }
+    },
+
+    {
+        1,
+        0x884,
+        0xE7,
+        {
+            { 0xD, PlayState::kTrue },
+            { 0x5E, PlayState::kFalse },
+            EMPTY_DESC
+        }
+    },
+
+    // Hal
+    {
+        2,
+        0xED0,
+        0x1B3,
+        {
+            { 0x1D, PlayState::kTrue },
+            { 0x11, PlayState::kFalse },
+            EMPTY_DESC
+        }
+    },
+
+    {
+        2,
+        0x804,
+        0x1B5,
+        {
+            { 0x13, PlayState::kTrue },
+            { 0xE, PlayState::kFalse },
+            EMPTY_DESC
+        }
+    },
+
+    {
+        2,
+        0xE74,
+        0x1B6,
+        {
+            { 0x1B, PlayState::kTrue },
+            { 0xF, PlayState::kFalse },
+            EMPTY_DESC
+        }
+    },
+
+    {
+        2,
+        0xE2C,
+        0x1B7,
+        {
+            { 0x26, PlayState::kTrue },
+            { 0x10, PlayState::kFalse },
+            EMPTY_DESC
+        }
+    },
+
+    {
+        2,
+        0xDD4,
+        0x1B9,
+        {
+            { 0, PlayState::kTrue },
+            { 1, PlayState::kFalse },
+            { 0x68, PlayState::kFalse },
+            EMPTY_DESC
+        }
+    },
+
+    {
+        2,
+        0xD48,
+        0x1BA,
+        {
+            { 0, PlayState::kTrue },
+            { 1, PlayState::kFalse },
+            { 0x20, PlayState::kTrue },
+            { 0x69, PlayState::kFalse },
+            EMPTY_DESC
+        }
+    },
+
+    {
+        2,
+        0x6F4,
+        0x1BB,
+        {
+            { 0x6A, PlayState::kFalse },
+            { 0x64, PlayState::kTrue },
+            { 0x5, PlayState::kFalse },
+            EMPTY_DESC
+        }
+    },
+
+    {
+        2,
+        0xCC8,
+        0x1BC,
+        {
+            { 0x8, PlayState::kTrue },
+            { 0x6, PlayState::kTrue },
+            { 0xC, PlayState::kFalse },
+            EMPTY_DESC
+        }
+    },
+
+    {
+        2,
+        0xC2C,
+        0x1BE,
+        {
+            { 0x28, PlayState::kTrue },
+            EMPTY_DESC
+        }
+    },
+
+    // Hulk
+    {
+        3,
+        0x1164,
+        0x14D,
+        {
+            { 0x13, PlayState::kTrue },
+            { 0x3A, PlayState::kFalse },
+            EMPTY_DESC
+        }
+    },
+
+    {
+        3,
+        0xB54,
+        0x150,
+        {
+            { 0xB, PlayState::kTrue },
+            { 0x25, PlayState::kFalse },
+            EMPTY_DESC
+        }
+    },
+
+    {
+        3,
+        0x10D8,
+        0x153,
+        {
+            { 0x12, PlayState::kTrue },
+            { 0x21, PlayState::kFalse },
+            EMPTY_DESC
+        }
+    },
+
+    {
+
+        3,
+        0xE2C,
+        0x154,
+        {
+            { 0x26, PlayState::kTrue },
+            { 0x22, PlayState::kFalse },
+            EMPTY_DESC
+        }
+    },
+
+    {
+        3,
+        0x108C,
+        0x155,
+        {
+            { 0, PlayState::kTrue },
+            { 1, PlayState::kFalse },
+            { 0x66, PlayState::kFalse },
+            EMPTY_DESC
+        }
+    },
+
+    {
+        3,
+        0x6F4,
+        0x156,
+        {
+            { 0x67, PlayState::kFalse },
+            { 0x64, PlayState::kTrue },
+            EMPTY_DESC
+        }
+    },
+
+    {
+        3,
+        0x1028,
+        0x157,
+        {
+            { 0x63, PlayState::kTrue },
+            { 0x24, PlayState::kFalse },
+            EMPTY_DESC
+        }
+    },
+
+    {
+        3,
+        0xFB0,
+        0x158,
+        {
+            { 0x5, PlayState::kTrue },
+            { 0x1E, PlayState::kFalse },
+            EMPTY_DESC
+        }
+    },
+
+    {
+        3,
+        0xF10,
+        0x159,
+        {
+            { 0x28, PlayState::kTrue },
+            EMPTY_DESC
+        }
+    }
+};
+
+} // End of namespace Nancy
\ No newline at end of file
diff --git a/engines/nancy/audio.cpp b/engines/nancy/audio.cpp
index c9c6ecc3f7..d701e2bb50 100644
--- a/engines/nancy/audio.cpp
+++ b/engines/nancy/audio.cpp
@@ -191,7 +191,7 @@ void SoundManager::loadSound(Common::String &name, int16 id, uint16 numLoops, ui
 		Audio::RewindableAudioStream *aStr = makeHISStream(mSnd, DisposeAfterUse::YES);
 		if (aStr) {
 			Audio::AudioStream *aStrLoop = Audio::makeLoopingAudioStream(aStr, numLoops);
-			_engine->_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &handles[id], aStrLoop, -1, volume * 255 / 64);
+			_engine->_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &handles[id], aStrLoop, -1, volume * 255 / 100);
 			_engine->_system->getMixer()->pauseHandle(handles[id], true);
 			names[id] = name;
 		}
diff --git a/engines/nancy/datatypes.cpp b/engines/nancy/datatypes.cpp
index 3a7a3926ab..c90af2b012 100644
--- a/engines/nancy/datatypes.cpp
+++ b/engines/nancy/datatypes.cpp
@@ -81,15 +81,12 @@ void View::read(Common::SeekableReadStream &stream) {
 // Takes a CURS chunk as input
 void Cursors::read(Common::SeekableReadStream &stream) {
     stream.seek(0);
-    for (uint i = 0; i < 85; ++i) {
-        Common::Rect &rect = rects[i];
-        rect.left = stream.readUint32LE();
-        rect.top = stream.readUint32LE();
-        rect.right = stream.readUint32LE();
-        rect.bottom = stream.readUint32LE();
+    for (uint i = 0; i < 84; ++i) {
+        readRect(stream, rects[i]);
     }
-    primaryVideoCursorX = stream.readUint16LE();
-    primaryVideoCursorY = stream.readUint16LE();
+    readRect(stream, primaryVideoInactiveZone);
+    primaryVideoInitialPos.x = stream.readUint16LE();
+    primaryVideoInitialPos.y = stream.readUint16LE();
 }
 
 void Inventory::read(Common::SeekableReadStream &stream) {
@@ -125,4 +122,45 @@ void Inventory::read(Common::SeekableReadStream &stream) {
     }
 }
 
+void Font::read(Common::SeekableReadStream &stream) {
+    char name[10];
+    stream.read(name, 10);
+    imageName = name;
+
+    char desc[0x20];
+    stream.read(desc, 0x20);
+    description = desc;
+    stream.skip(8);
+    colorCoordsOffset.x = stream.readUint16LE();
+    colorCoordsOffset.y = stream.readUint16LE();
+
+    stream.skip(2);
+    spaceWidth = stream.readUint16LE();
+    stream.skip(2);
+    uppercaseOffset = stream.readUint16LE();
+    lowercaseOffset = stream.readUint16LE();
+    digitOffset = stream.readUint16LE();
+    periodOffset = stream.readUint16LE();
+    commaOffset = stream.readUint16LE();
+    equalitySignOffset = stream.readUint16LE();
+    colonOffset = stream.readUint16LE();
+    dashOffset = stream.readUint16LE();
+    questionMarkOffset = stream.readUint16LE();
+    exclamationMarkOffset = stream.readUint16LE();
+    percentOffset = stream.readUint16LE();
+    ampersandOffset = stream.readUint16LE();
+    asteriskOffset = stream.readUint16LE();
+    leftBracketOffset = stream.readUint16LE();
+    rightBracketOffset = stream.readUint16LE();
+    plusOffset = stream.readUint16LE();
+    apostropheOffset = stream.readUint16LE();
+    semicolonOffset = stream.readUint16LE();
+    slashOffset = stream.readUint16LE();
+
+    for (uint i = 0; i < 78; ++i) {
+        symbolRects.push_back(Common::Rect());
+        readRect(stream, symbolRects[i]);
+    }
+}
+
 } // End of namespace Nancy
\ No newline at end of file
diff --git a/engines/nancy/datatypes.h b/engines/nancy/datatypes.h
index 2e0a1ea04a..7606821161 100644
--- a/engines/nancy/datatypes.h
+++ b/engines/nancy/datatypes.h
@@ -25,6 +25,9 @@
 
 #include "common/str.h"
 #include "common/rect.h"
+#include "common/array.h"
+
+#include "graphics/surface.h"
 
 namespace Common {
 class SeekableReadStream;
@@ -72,10 +75,9 @@ struct View {
 struct Cursors {
     void read(Common::SeekableReadStream &stream);
 
-    Common::Rect rects[85];
-    // The cursor gets set to this location at some point during PrimaryVideoSequence 
-    uint16 primaryVideoCursorX;
-    uint16 primaryVideoCursorY;
+    Common::Rect rects[84];
+    Common::Rect primaryVideoInactiveZone;
+    Common::Point primaryVideoInitialPos;
 };
 
 struct Inventory {
@@ -100,6 +102,41 @@ struct Inventory {
     ItemDesc items[11]; // 0x1F4
 };
 
+struct Font {
+    void read(Common::SeekableReadStream &stream);
+
+    Common::String imageName; // 0x00
+    Common::String description; // 0xA
+    //
+    Common::Point colorCoordsOffset; // y is def at 0x34, x is just a guess
+
+    uint16 spaceWidth = 0;              // 0x38
+    
+    uint16 uppercaseOffset = 0;         // 0x3C
+    uint16 lowercaseOffset = 0;         // 0x3E
+    uint16 digitOffset = 0;             // 0x40
+    uint16 periodOffset = 0;            // 0x42
+    uint16 commaOffset = 0;             // 0x44
+    uint16 equalitySignOffset = 0;      // 0x46
+    uint16 colonOffset = 0;             // 0x48
+    uint16 dashOffset = 0;              // 0x4A
+    uint16 questionMarkOffset = 0;      // 0x4C
+    uint16 exclamationMarkOffset = 0;   // 0x4E
+    uint16 percentOffset = 0;           // 0x50
+    uint16 ampersandOffset = 0;         // 0x52
+    uint16 asteriskOffset = 0;          // 0x54
+    uint16 leftBracketOffset = 0;       // 0x56
+    uint16 rightBracketOffset = 0;      // 0x58
+    uint16 plusOffset = 0;              // 0x5A
+    uint16 apostropheOffset = 0;        // 0x5C
+    uint16 semicolonOffset = 0;         // 0x5E
+    uint16 slashOffset = 0;             // 0x60
+
+    Common::Array<Common::Rect> symbolRects; // 0x62
+    
+    Graphics::Surface image;
+};
+
 } // End of namespace Nancy
 
 #endif // NANCY_DATATYPES_H
\ No newline at end of file
diff --git a/engines/nancy/graphics.cpp b/engines/nancy/graphics.cpp
index e9b100e88f..2e6f664049 100644
--- a/engines/nancy/graphics.cpp
+++ b/engines/nancy/graphics.cpp
@@ -43,7 +43,8 @@ const uint GraphicsManager::transColor = 0x3E0;
 
 
 GraphicsManager::GraphicsManager(NancyEngine *engine) :
-        _engine(engine) {
+        _engine(engine),
+        _textbox(engine) {
     _screen.create(640, 480, pixelFormat);
 }
 
@@ -54,20 +55,11 @@ void GraphicsManager::init() {
     uint32 height = viewportDesc.destination.bottom - viewportDesc.destination.top;
     _background.create(width, height, pixelFormat);
 
-    // TODO make a TBOX struct
-    chunk = _engine->getBootChunkStream("TBOX");
-    chunk->seek(0x28);
-    width = chunk->readUint32LE();
-    height = chunk->readUint32LE();
-    chunk->seek(0x20);
-    width -= chunk->readUint32LE();
-    height -= chunk->readUint32LE();
-    _frameTextBox.create(width, height, pixelFormat);
+    _textbox.init();
 }
 
 GraphicsManager::~GraphicsManager() {
     _background.free();
-    _frameTextBox.free();
     _screen.free();
     _primaryFrameSurface.free();
     _object0Surface.free();
@@ -78,7 +70,7 @@ GraphicsManager::~GraphicsManager() {
     _genericSurface.free();
 
     _secMovieSurface.free();
-    _secMovieDecoder.close();
+    _primaryVideoSurface.free();
 
 
     for (auto st : _ZRender) {
@@ -125,6 +117,7 @@ Common::String &GraphicsManager::initZRenderStruct(char const *name,
 
     return st.name;
 }
+
 #define READ_RECT(where, x) chunk->seek(x); \
                             where->left = chunk->readUint32LE(); \
                             where->top = chunk->readUint32LE(); \
@@ -158,7 +151,8 @@ void GraphicsManager::initSceneZRenderStructs(Common::Array<Common::String> &out
     chunk = _engine->getBootChunkStream("BSUM");
     READ_RECT(dest, 356)
     outNames.push_back(initZRenderStruct(  "FRAME TB SURF", 6, false, ZRenderStruct::kNoTrans,
-                        &_frameTextBox, nullptr, nullptr, dest));
+                        nullptr, nullptr, nullptr, dest));
+    getZRenderStruct("FRAME TB SURF").managedSource = &_textbox._surface;
 
     READ_RECT(source, 388)
     READ_RECT(dest, 420)
@@ -179,7 +173,7 @@ void GraphicsManager::initSceneZRenderStructs(Common::Array<Common::String> &out
                         new RenderFunction(this, &GraphicsManager::renderFrameInvBox)));
     
     outNames.push_back(initZRenderStruct(  "INV BITMAP", 9, false, ZRenderStruct::kNoTrans, &_inventoryBitmapSurface));
-    outNames.push_back(initZRenderStruct(  "PRIMARY VIDEO", 8, false, ZRenderStruct::kNoTrans, nullptr,
+    outNames.push_back(initZRenderStruct(  "PRIMARY VIDEO", 8, false, ZRenderStruct::kNoTrans, &_primaryVideoSurface,
                         new RenderFunction(this, &GraphicsManager::renderPrimaryVideo)));
     outNames.push_back(initZRenderStruct(  "SEC VIDEO 0", 8, false, ZRenderStruct::kTrans, &channels[0].surf));
     outNames.push_back(initZRenderStruct(  "SEC VIDEO 1", 8, false, ZRenderStruct::kTrans, &channels[1].surf));
@@ -258,12 +252,20 @@ void GraphicsManager::renderDisplay(Common::Array<Common::String> ids) {
                         // making some assumptions here
                         case ZRenderStruct::kNoTrans: {
                             Common::Point dest(current.destRect.left, current.destRect.top);
-                            _screen.blitFrom(*current.sourceSurface, current.sourceRect, dest);
+                            if (current.sourceSurface) {
+                                _screen.blitFrom(*current.sourceSurface, current.sourceRect, dest);
+                            } else {
+                                _screen.blitFrom(*current.managedSource, current.sourceRect, dest);
+                            }
                             break;
                         }
                         case ZRenderStruct::kTrans: {
                             Common::Point dest(current.destRect.left, current.destRect.top);
-                            _screen.transBlitFrom(*current.sourceSurface, current.sourceRect, dest, transColor);
+                            if (current.sourceSurface) {
+                                _screen.transBlitFrom(*current.sourceSurface, current.sourceRect, dest, transColor);
+                            } else {
+                                _screen.transBlitFrom(*current.managedSource, current.sourceRect, dest, transColor);
+                            }
                             break;
                         }
                         default:
@@ -471,7 +473,21 @@ void GraphicsManager::renderFrameInvBox() {
 }
 
 void GraphicsManager::renderPrimaryVideo() {
-    // TODO
+    ZRenderStruct &zr = getZRenderStruct("PRIMARY VIDEO");
+    if (!_primaryVideoDecoder.isPlaying()) {
+        _primaryVideoDecoder.start();
+        
+        _primaryVideoSurface.w = _secMovieDecoder.getWidth();
+        _primaryVideoSurface.h = _secMovieDecoder.getHeight();
+        _primaryVideoSurface.format = _secMovieDecoder.getPixelFormat();
+    }
+
+    if (_primaryVideoDecoder.needsUpdate()) {
+        _primaryVideoSurface = *_primaryVideoDecoder.decodeNextFrame();
+    }
+
+    Common::Point dest(zr.destRect.left, zr.destRect.top);
+     _screen.blitFrom(_primaryVideoSurface, zr.sourceRect, dest);
 }
 
 void GraphicsManager::renderSecVideo0() {
diff --git a/engines/nancy/graphics.h b/engines/nancy/graphics.h
index 43e7fcc69f..144ad69070 100644
--- a/engines/nancy/graphics.h
+++ b/engines/nancy/graphics.h
@@ -26,6 +26,7 @@
 #include "engines/nancy/nancy.h"
 #include "engines/nancy/video.h"
 #include "engines/nancy/datatypes.h"
+#include "engines/nancy/textbox.h"
 
 #include "common/func.h"
 #include "common/stack.h"
@@ -45,6 +46,7 @@ public:
     uint32 z = 0;
     RenderFunction *renderFunction = nullptr;
     Graphics::Surface *sourceSurface = nullptr;
+    Graphics::ManagedSurface *managedSource = nullptr;
     Common::Rect sourceRect;
     Common::Rect destRect;
     bool isActive = false;
@@ -128,7 +130,6 @@ public:
     void updateInvBox();
 
     Graphics::Surface _background;
-    Graphics::Surface _frameTextBox;
     Graphics::Surface _primaryFrameSurface;
     Graphics::Surface _object0Surface;
     Graphics::Surface _inventoryBoxIconsSurface;
@@ -137,8 +138,11 @@ public:
     Graphics::Surface _genericSurface;
 
     VideoChannel channels[2];
+    Graphics::Surface _primaryVideoSurface;
+    AVFDecoder _primaryVideoDecoder;
     Graphics::Surface _secMovieSurface;
     AVFDecoder _secMovieDecoder;
+    Textbox _textbox;
 
     View viewportDesc;
     InventoryBox inventoryBoxDesc;
diff --git a/engines/nancy/logic.cpp b/engines/nancy/logic.cpp
index f571cd1790..e04f49c7ef 100644
--- a/engines/nancy/logic.cpp
+++ b/engines/nancy/logic.cpp
@@ -96,6 +96,8 @@ bool Logic::addNewActionRecord(Common::SeekableReadStream &inputData) {
 }
 
 void Logic::processActionRecords() {
+    ignorePrimaryVideo = false;
+    
     for (auto record : _records) {
         if (record->isDone) {
             continue;
@@ -264,7 +266,9 @@ void Logic::processActionRecords() {
         }
 
         if (record->isActive) {
-            record->execute(_engine);
+            if (record->type != 0x32 || !ignorePrimaryVideo) { 
+                record->execute(_engine);
+            }
         }
     }
 }
diff --git a/engines/nancy/logic.h b/engines/nancy/logic.h
index 045748409a..b47437ed9c 100644
--- a/engines/nancy/logic.h
+++ b/engines/nancy/logic.h
@@ -39,7 +39,7 @@ class Logic {
     friend class SceneManager;
 
 public:
-    Logic(NancyEngine* engine): _engine(engine) {}
+    Logic(NancyEngine* engine): _engine(engine), ignorePrimaryVideo(false) {}
     virtual ~Logic() {}
 
     bool addNewActionRecord(Common::SeekableReadStream &inputData);
@@ -48,6 +48,8 @@ public:
     ActionRecord * getActionRecord(uint id) { if (id < _records.size()) return _records[id]; else return nullptr;}
     void clearActionRecords();
 
+    bool ignorePrimaryVideo; // hack
+
 protected:
     virtual ActionRecord *createActionRecord(uint16 type);
     NancyEngine *_engine;
diff --git a/engines/nancy/module.mk b/engines/nancy/module.mk
index b68aafe547..c7ecfabf0c 100644
--- a/engines/nancy/module.mk
+++ b/engines/nancy/module.mk
@@ -3,6 +3,7 @@ MODULE := engines/nancy
 MODULE_OBJS = \
   action/recordtypes.o \
   action/arfactory_v1.o \
+  action/primaryvideo.o \
   audio.o \
   console.o \
   datatypes.o \
@@ -17,6 +18,7 @@ MODULE_OBJS = \
   nancy.o \
   resource.o \
   scene.o \
+  textbox.o \
   video.o
 
 # This module can be built as a plugin
diff --git a/engines/nancy/nancy.cpp b/engines/nancy/nancy.cpp
index db4b095b32..2f43eb3164 100644
--- a/engines/nancy/nancy.cpp
+++ b/engines/nancy/nancy.cpp
@@ -72,9 +72,10 @@ NancyEngine::NancyEngine(OSystem *syst, const NancyGameDescription *gd) :
 	DebugMan.addDebugChannel(kDebugMusic, "Music", "Music debug level");
 
 	_console = new NancyConsole(this);
-	_logoSequence = new LogoSequence(this);
-	_rnd = nullptr;
+	_rnd = new Common::RandomSource("Nancy");
+	_rnd->setSeed(_rnd->getSeed());
 
+	_logoSequence = new LogoSequence(this);
 	logic = new Logic(this);
 	sceneManager = new SceneManager(this);
 	map = new Map(this);
@@ -224,8 +225,7 @@ void NancyEngine::bootGameEngine() {
 	if (_fontSize != font->size()) {
 		error("Mismatch NumFonts and FONT memory... %i, %i", _fontSize, font->size());
 	}
-	// TODO a loop that uses FONT
-	// TODO another loop that does the same thing but with CURS
+	
 	// TODO reset some vars
 
 	graphics->clearZRenderStructs();
@@ -256,13 +256,6 @@ void NancyEngine::clearBootChunks() {
 	_bootChunks.clear();
 }
 
-void NancyEngine::initialize() {
-	debugC(1, kDebugEngine, "initialize");
-
-	_rnd = new Common::RandomSource("nancy");
-	_rnd->setSeed(42);                              // Kick random number generator
-}
-
 void NancyEngine::preloadCals(const IFF &boot) {
 	const byte *buf;
 	uint size;
diff --git a/engines/nancy/nancy.h b/engines/nancy/nancy.h
index 4db298e1f5..5c89143429 100644
--- a/engines/nancy/nancy.h
+++ b/engines/nancy/nancy.h
@@ -192,8 +192,6 @@ private:
 
 	LogoSequence *_logoSequence;
 	Common::HashMap<Common::String, Common::SeekableReadStream *> _bootChunks;
-
-	void initialize();
 };
 
 } // End of namespace Nancy
diff --git a/engines/nancy/scene.cpp b/engines/nancy/scene.cpp
index 484e9013b9..1c8228d6ef 100644
--- a/engines/nancy/scene.cpp
+++ b/engines/nancy/scene.cpp
@@ -70,6 +70,17 @@ void SceneManager::changeScene(uint16 id, uint16 frame, uint16 verticalOffset, b
     _state = kLoadNew;
 }
 
+void SceneManager::pushScene() {
+    _pushedSceneID = _sceneID;
+    _pushedFrameID = _engine->playState.currentViewFrame;
+    _pushedVerticalScroll = _engine->playState.verticalScroll;
+}
+
+void SceneManager::popScene() {
+    changeScene(_pushedSceneID, _pushedFrameID, _pushedVerticalScroll, true);
+    _pushedSceneID = 10000;
+}
+
 void SceneManager::addObjectToInventory(uint16 id) {
     if (_engine->playState.inventory.heldItem == id) {
         _engine->playState.inventory.heldItem = -1;
@@ -83,7 +94,7 @@ void SceneManager::addObjectToInventory(uint16 id) {
     _engine->graphics->updateInvBox();
 }
 
-void SceneManager::pickUpObject(uint16 id) {
+void SceneManager::removeObjectFromInventory(uint16 id, bool pickUp) {
     Common::Array<uint16> &order = _engine->graphics->inventoryBoxDesc.itemsOrder;
     Common::Array<uint16> temp;
 
@@ -94,7 +105,9 @@ void SceneManager::pickUpObject(uint16 id) {
         uint16 thisElem = order.back();
         order.pop_back();
         if (thisElem == id) {
-            _engine->playState.inventory.heldItem = id;
+            if (pickUp) {
+                _engine->playState.inventory.heldItem = id;
+            }
             break;
         } else {
             temp.push_back(thisElem);
@@ -417,13 +430,23 @@ void SceneManager::run() {
             stateChangeRequests |= kMap;
             return;
         } else if (hovered == InputManager::textBoxID) {
-            // TODO
+            int16 picked = _engine->graphics->_textbox.getHovered(_engine->input->getMousePosition());
+            if (picked != -1) {
+                // clear logic conditions and timestamps
+                for (uint i = 0; i < 30; ++i) {
+                    _engine->playState.logicConditions[i] = PlayState::kFalse;
+                    _engine->playState.logicConditionsTimestamps[i] = 0;
+                }
+
+                _engine->playState.logicConditions[picked] = PlayState::kTrue;
+                _engine->playState.logicConditionsTimestamps[picked] = playTimeThisFrame;
+            }
         } else if (hovered == InputManager::inventoryItemTakeID) {
             GraphicsManager::InventoryBox &box = _engine->graphics->inventoryBoxDesc;
             Common::Point mousePos = _engine->input->getMousePosition();
             for (uint i = 0; i < 4; ++i) {
                 if (box.onScreenItems[i].dest.contains(mousePos)) {
-                    pickUpObject(box.onScreenItems[i].itemId);
+                    removeObjectFromInventory(box.onScreenItems[i].itemId, true);
                     break;
                 }
             }
@@ -696,6 +719,13 @@ void SceneManager::handleMouse() {
             _engine->input->hoveredElementID = InputManager::inventoryScrollbarID;
             _engine->input->setPointerBitmap(1, 2, -1);
             handleScrollbar(1);
+        } else if (uizr = &_engine->graphics->getZRenderStruct("FRAME TB SURF"), uizr->destRect.contains(mousePos)) {
+            _engine->input->hoveredElementID = InputManager::textBoxID;
+            if (_engine->graphics->_textbox.getHovered(mousePos) != -1) {
+                _engine->input->setPointerBitmap(1, 2, -1);
+            } else {
+                _engine->input->setPointerBitmap(1, 0, -1);
+            }
         } else if (_engine->sceneManager->inventoryDesc.shadesDst.contains(mousePos)) {
             if (_engine->playState.inventory.heldItem != -1) {
                 _engine->input->hoveredElementID = InputManager::inventoryItemReturnID;
@@ -703,7 +733,7 @@ void SceneManager::handleMouse() {
                 _engine->input->hoveredElementID = InputManager::inventoryItemTakeID;
             }
         } else {
-            _engine->input->setPointerBitmap(1, 1, 0);
+            _engine->input->setPointerBitmap(1, 0, 0);
         }
     }     
 }
@@ -720,11 +750,19 @@ void SceneManager::clearSceneData() {
         _engine->playState.eventFlags[i] = PlayState::kFalse;
     }
 
+    // clear all logic flags and timestamps
+    for (uint i = 0; i < 30; ++i) {
+        _engine->playState.logicConditions[i] = PlayState::kFalse;
+        _engine->playState.logicConditionsTimestamps[i] = 0;
+    }
+
     _engine->logic->clearActionRecords();
 
+    _engine->graphics->getZRenderStruct("PRIMARY VIDEO").isActive = false;
     _engine->graphics->getZRenderStruct("SEC VIDEO 0").isActive = false;
     _engine->graphics->getZRenderStruct("SEC VIDEO 1").isActive = false;
     _engine->graphics->getZRenderStruct("SEC MOVIE").isActive = false;
+    _engine->graphics->getZRenderStruct("FRAME TB SURF").isActive = false;
     _engine->graphics->getZRenderStruct("STATIC BITMAP ANIMATION").isActive = false;
 }
 
diff --git a/engines/nancy/scene.h b/engines/nancy/scene.h
index 11801fb236..d699899d13 100644
--- a/engines/nancy/scene.h
+++ b/engines/nancy/scene.h
@@ -58,8 +58,10 @@ public:
     void process();
 
     void changeScene(uint16 id, uint16 frame, uint16 verticalOffset, bool noSound);
+    void pushScene();
+    void popScene();
     void addObjectToInventory(uint16 id);
-    void pickUpObject(uint16 id);
+    void removeObjectFromInventory(uint16 id, bool pickUp = false);
 
 private:
     void init();
@@ -108,6 +110,10 @@ private:
     uint32 _stashedTickCount;
     Time _nextBackgroundMovement;
 
+    uint16 _pushedSceneID = 10000;
+    uint16 _pushedFrameID = 0;
+    uint16 _pushedVerticalScroll = 0;
+
     Common::Point scrollbarMouse;
 
     bool isComingFromMenu = true;
diff --git a/engines/nancy/textbox.cpp b/engines/nancy/textbox.cpp
new file mode 100644
index 0000000000..cd3667c4f2
--- /dev/null
+++ b/engines/nancy/textbox.cpp
@@ -0,0 +1,297 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/nancy/textbox.h"
+
+#include "engines/nancy/nancy.h"
+#include "engines/nancy/resource.h"
+#include "engines/nancy/graphics.h"
+
+#include "common/error.h"
+#include "common/util.h"
+
+namespace Nancy {
+
+const Common::String Textbox::beginToken = Common::String("<i>");
+const Common::String Textbox::endToken = Common::String("<o>");
+const Common::String Textbox::colorBeginToken = Common::String("<c1>");
+const Common::String Textbox::colorEndToken = Common::String("<c0>");
+const Common::String Textbox::hotspotToken = Common::String("<h>");
+const Common::String Textbox::newLineToken = Common::String("<n>");
+
+void Textbox::init() {
+    Common::SeekableReadStream *chunk = _engine->getBootChunkStream("FONT");
+    
+    chunk->seek(0);
+    while (chunk->pos() < chunk->size() - 1) {
+        _fonts.push_back(Font());
+        _fonts.back().read(*chunk);
+
+        _engine->_res->loadImage("ciftree", _fonts.back().imageName, _fonts.back().image);
+    }
+    
+    chunk = _engine->getBootChunkStream("TBOX");
+    chunk->seek(0x28);
+    uint width = chunk->readUint32LE();
+    uint height = chunk->readUint32LE();
+    chunk->seek(0x20);
+    width -= chunk->readUint32LE();
+    height -= chunk->readUint32LE();
+    _surface.create(width, height, GraphicsManager::pixelFormat);
+
+    chunk->seek(0x36);
+    _firstLineOffset = chunk->readUint16LE();
+    _lineHeight = chunk->readUint16LE();
+    _borderWidth = chunk->readUint16LE();
+}
+
+void Textbox::clear() {
+    _surface.clear();
+    lineNr = 0;
+    _responses.clear();
+}
+
+Common::Rect Textbox::processTextLine(const Common::String &text, uint16 fontID) {
+    ZRenderStruct &zr = _engine->graphics->getZRenderStruct("FRAME TB SURF");
+    uint textWidth = 0;
+    Common::String line;
+    uint maxWidth = zr.destRect.width() - _borderWidth;
+    uint lineDist = _lineHeight + _lineHeight / 4;
+    Common::Rect ret;
+    ret.left = _borderWidth;
+    ret.top = _firstLineOffset - _lineHeight + lineNr * lineDist;
+    ret.right = ret.left;
+    ret.bottom = ret.top + _lineHeight;
+    
+    bool colorTokenIsOpen = false;
+    uint16 colorOffset = 0;
+
+    for (uint i = 0; i < text.size(); ++i) {
+        if (text[i] == '<') {
+            Common::String token = text.substr(i, 4);
+            if (token.hasPrefix(beginToken)) {
+                // Ignore and continue
+                i += beginToken.size() - 1;
+            } else if (token.hasPrefix(endToken)) {
+                // Caption has ended, return
+                return ret;
+            } else if (token.hasPrefix(colorBeginToken)) {
+                // Assumes color is always at beginning of line
+                colorTokenIsOpen = true;
+                i += colorBeginToken.size() - 1;
+                colorOffset = 0;
+            } else if (token.hasPrefix(colorEndToken)) {
+                // Draw color line (always a single letter?)
+                colorTokenIsOpen = false;
+                drawText(line, 1, _borderWidth, _firstLineOffset + lineNr * lineDist, true);
+                line.clear();
+                i += colorEndToken.size() - 1;
+            } else if (token.hasPrefix(hotspotToken)) {
+                // TODO ADD HOTSPOT
+                i += hotspotToken.size() - 1;
+            } else if (token.hasPrefix(newLineToken)) {
+                // Draw finished line
+                drawText(line, fontID, colorOffset + _borderWidth, _firstLineOffset + lineNr * lineDist);
+                line.clear();
+                ret.setWidth(MAX<int16>(ret.width(), textWidth));
+                textWidth = 0;
+                ++lineNr;
+                i += newLineToken.size() - 1;
+            }
+        } else {
+            if (colorTokenIsOpen) {
+                colorOffset += getCharacterWidth(text[i], fontID) + 1;
+            }
+            line += text[i];
+            textWidth += getCharacterWidth(text[i], fontID) + 1;
+
+            // Wrap text
+            if (textWidth > maxWidth) {
+                while (line.size() > 0) {
+                    if (line.lastChar() != ' ') {
+                        line.deleteLastChar();
+                        --i;
+                    } else {
+                        drawText(line, fontID, colorOffset + _borderWidth, _firstLineOffset + lineNr * lineDist);
+                        line.clear();
+                        ret.setWidth(MAX<int16>(ret.width(), textWidth));
+                        ret.bottom += lineDist;
+                        textWidth = 0;
+                        colorOffset = 0;
+                        ++lineNr;
+                        break;
+                    }
+        }
+            }
+        }
+    }
+    
+    if (line.size()) {
+        drawText(line, fontID, colorOffset + _borderWidth, _firstLineOffset + lineNr * lineDist);
+        ret.setWidth(MAX<int16>(ret.width(), textWidth));
+    }
+
+    return ret;
+}
+
+
+void Textbox::processResponse(const Common::String &text, uint16 fontID, uint16 id, Common::String soundName) {
+    ++lineNr;
+    _responses.push_back(Response());
+    Response &res = _responses.back();
+
+    res.hotspot = processTextLine(text, fontID);
+    res.soundName = soundName;
+}
+
+int16 Textbox::getHovered(Common::Point mousePos) {
+    ZRenderStruct &zr = _engine->graphics->getZRenderStruct("FRAME TB SURF");
+    int16 pickedResponse = -1;
+
+    // Adjust the mouse to local coordinates
+    mousePos.x -= zr.destRect.left;
+    mousePos.y -= zr.destRect.top;
+
+    // Adjust for scroll
+    mousePos.y += zr.sourceRect.top;
+
+    for (uint i = 0; i < _responses.size(); ++i) {
+        if (_responses[i].hotspot.contains(mousePos)) {
+            pickedResponse = i;
+        }
+    }
+
+    return pickedResponse;
+}
+
+uint16 Textbox::getInnerHeight() {
+    uint lineDist = _lineHeight + _lineHeight / 4;
+    return lineNr * lineDist + 2 * _firstLineOffset;
+}
+
+Common::Rect Textbox::getCharacterSourceRect(char c, uint16 fontID) {
+    using namespace Common;
+    Font &font = _fonts[fontID];
+    uint offset = 0;
+    Common::Rect ret;
+
+    if (isUpper(c)) {
+        offset = c + font.uppercaseOffset - 0x41;
+    } else if (isLower(c)) {
+        offset = c + font.lowercaseOffset - 0x61;
+    } else if (isDigit(c)) {
+        offset = c + font.digitOffset - 0x30;
+    } else if (isSpace(c)) {
+        ret.setWidth(font.spaceWidth);
+        return ret;
+    } else if (isPunct(c)) {
+        switch (c) {
+            case '.':
+                offset = font.periodOffset;
+                break;
+            case ',':
+                offset = font.commaOffset;
+                break;
+            case '=':
+                offset = font.equalitySignOffset;
+                break;
+            case ':':
+                offset = font.colonOffset;
+                break;
+            case '-':
+                offset = font.dashOffset;
+                break;
+            case '?':
+                offset = font.questionMarkOffset;
+                break;
+            case '!':
+                offset = font.exclamationMarkOffset;
+                break;
+            case '%':
+                offset = font.percentOffset;
+                break;
+            case '&':
+                offset = font.ampersandOffset;
+                break;
+            case '*':
+                offset = font.asteriskOffset;
+                break;
+            case '(':
+                offset = font.leftBracketOffset;
+                break;
+            case ')':
+                offset = font.rightBracketOffset;
+                break;
+            case '+':
+                offset = font.plusOffset;
+                break;
+            case '\'':
+                offset = font.apostropheOffset;
+                break;
+            case ';':
+                offset = font.semicolonOffset;
+                break;
+            case '/':
+                offset = font.slashOffset;
+                break;
+            default:
+                error("Unsupported FONT character: %c", c);
+        }
+    }
+    ret = font.symbolRects[offset];
+    ret.right += 1;
+    return ret;
+}
+
+uint16 Textbox::getCharacterWidth(char c, uint16 fontID) {
+    return getCharacterSourceRect(c, fontID).width();
+}
+
+void Textbox::drawText(const Common::String &text, uint16 fontID, uint16 left, uint16 bottom, bool color) {
+    Common::Rect source;
+    Common::Rect dest;
+    dest.left = left;
+    dest.bottom = bottom;
+
+    for (char c : text) {
+        source = getCharacterSourceRect(c, fontID);
+        if (color) {
+            source.left += _fonts[fontID].colorCoordsOffset.x;
+            source.right += _fonts[fontID].colorCoordsOffset.x;
+            source.top += _fonts[fontID].colorCoordsOffset.y;
+            source.bottom += _fonts[fontID].colorCoordsOffset.y;
+        }
+
+        if (Common::isSpace(c)) {
+            dest.left += source.width();
+            continue;
+        }
+
+        dest.right = dest.left + source.width();
+        dest.top = dest.bottom - source.height();
+
+        _surface.transBlitFrom(_fonts[fontID].image, source, dest, GraphicsManager::transColor);
+        dest.left = dest.right + 1;
+    }
+}
+
+} // End of namespace Nancy
\ No newline at end of file
diff --git a/engines/nancy/textbox.h b/engines/nancy/textbox.h
new file mode 100644
index 0000000000..6d0f769e11
--- /dev/null
+++ b/engines/nancy/textbox.h
@@ -0,0 +1,95 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef NANCY_TEXTBOX_H
+#define NANCY_TEXTBOX_H
+
+#include "engines/nancy/datatypes.h"
+
+#include "common/str.h"
+#include "common/array.h"
+
+#include "graphics/managed_surface.h"
+
+namespace Nancy {
+
+class NancyEngine;
+
+class Textbox {
+    friend class GraphicsManager;
+    friend class SceneManager;
+
+public:
+    Textbox(NancyEngine *engine) :
+        _engine(engine),
+        _needsUpdate(false),
+        lineNr(0),
+        _firstLineOffset(0),
+        _lineHeight(0),
+        _borderWidth(0) {}
+    
+    void init();
+    void clear();
+
+    Common::Rect processTextLine(const Common::String &text, uint16 fontID);
+    void processResponse(const Common::String &text, uint16 fontID, uint16 id, Common::String soundName);
+
+    int16 getHovered(Common::Point mousePos);
+    uint16 getInnerHeight();
+
+    const Graphics::Surface *getSurface();
+
+private:
+    Common::Rect getCharacterSourceRect(char c, uint16 fontID);
+    uint16 getCharacterWidth(char c, uint16 fontID);
+    void drawText(const Common::String &text, uint16 fontID, uint16 left, uint16 bottom, bool color = false);
+    
+private:
+    struct Response {
+        Common::String soundName;
+        Common::Rect hotspot;
+    };
+    
+    NancyEngine *_engine;
+    Common::String _formattedText;
+    bool _needsUpdate;
+    Common::Array<Font> _fonts;
+    Graphics::ManagedSurface _surface;
+    uint16 lineNr;
+    Common::Array<Response> _responses;
+    
+    uint16 _firstLineOffset;
+    uint16 _lineHeight;
+    uint16 _borderWidth;
+
+    static const Common::String beginToken;
+    static const Common::String endToken;
+    static const Common::String colorBeginToken;
+    static const Common::String colorEndToken;
+    static const Common::String hotspotToken;
+    static const Common::String newLineToken;
+
+};
+
+} // End of namespace Nancy
+
+#endif // NANCY_TEXTBOX_H
\ No newline at end of file


Commit: cd36df63fcb1c4af8c7d177bde3d2c862c334ba0
    https://github.com/scummvm/scummvm/commit/cd36df63fcb1c4af8c7d177bde3d2c862c334ba0
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add textbox scrolling

Added code for scrolling the textbox during dialogue scenes.

Changed paths:
    engines/nancy/input.h
    engines/nancy/scene.cpp
    engines/nancy/scene.h
    engines/nancy/textbox.cpp
    engines/nancy/textbox.h


diff --git a/engines/nancy/input.h b/engines/nancy/input.h
index 6e25e14f50..dbce570e20 100644
--- a/engines/nancy/input.h
+++ b/engines/nancy/input.h
@@ -78,7 +78,7 @@ enum InputType : uint16 {
     byte getInput() { return _inputs; }
     void clearInput();
     bool isClickValidLMB() { return hoveredElementID != -1 && _inputs & kLeftMouseButtonUp; }
-    bool isClickValidRMB() { return hoveredElementID != -1 && _inputs & kLeftMouseButtonDown; }
+    bool isClickValidRMB() { return hoveredElementID != -1 && _inputs & kRightMouseButtonUp; }
 
     Common::Point getMousePosition();
     void setMousePosition(const Common::Point &newPos);
diff --git a/engines/nancy/scene.cpp b/engines/nancy/scene.cpp
index 1c8228d6ef..8ed4b87708 100644
--- a/engines/nancy/scene.cpp
+++ b/engines/nancy/scene.cpp
@@ -518,7 +518,7 @@ void SceneManager::run() {
 
     } else if (_engine->input->isClickValidRMB()) {
         if (_engine->input->hoveredElementID == InputManager::textBoxScrollbarID) {
-            // TODO, moves scrollbar one line up
+            _engine->graphics->_textbox.setPosition(handleScrollbar(0, true));
         } else if (_engine->input->hoveredElementID == InputManager::inventoryScrollbarID) {
             // TODO, moves scrollbar one line up
         } else if (_engine->input->hoveredElementID == InputManager::textBoxID) {
@@ -714,7 +714,7 @@ void SceneManager::handleMouse() {
         if (uizr->destRect.contains(mousePos)) {
             _engine->input->hoveredElementID = InputManager::textBoxScrollbarID;
             _engine->input->setPointerBitmap(1, 2, -1);
-            handleScrollbar(0);
+            _engine->graphics->_textbox.setPosition(handleScrollbar(0));
         } else if (uizr = &_engine->graphics->getZRenderStruct("CUR INV SLIDER"), uizr->destRect.contains(mousePos)) {
             _engine->input->hoveredElementID = InputManager::inventoryScrollbarID;
             _engine->input->setPointerBitmap(1, 2, -1);
@@ -767,7 +767,7 @@ void SceneManager::clearSceneData() {
 }
 
 // 0 is textbox, 1 is inventory, returns -1 when movement is stopped/disabled
-float SceneManager::handleScrollbar(uint id) {
+float SceneManager::handleScrollbar(uint id, bool reset) {
     Common::SeekableReadStream *chunk;
     ZRenderStruct *zr;
     if (id == 0) {
@@ -787,6 +787,11 @@ float SceneManager::handleScrollbar(uint id) {
     Common::Rect &scrollRect = zr->destRect;
     Common::Point newMousePos = _engine->input->getMousePosition();
 
+    if (reset) {
+        scrollRect.moveTo(origDest);
+        return 0;
+    }
+
     if (_engine->input->getInput() & InputManager::kLeftMouseButtonDown) {
         if (scrollbarMouse.x == -1 && scrollbarMouse.y == -1) {
             scrollbarMouse = newMousePos - Common::Point(scrollRect.left, scrollRect.top);
@@ -795,11 +800,11 @@ float SceneManager::handleScrollbar(uint id) {
 
         uint16 minY = origDest.y;
         uint16 maxY = tboxRect.bottom - scrollRect.height() + tboxRect.top - origDest.y; // TODO goes a little out of bounds
-        uint16 newTop = CLIP((uint16)(scrollRect.top + newMousePos.y - scrollbarMouse.y), minY, maxY);
+        uint16 newTop = CLIP<uint16>((scrollRect.top + newMousePos.y - scrollbarMouse.y), minY, maxY);
         scrollRect.bottom += newTop - scrollRect.top;
         scrollRect.top = newTop;
 
-        return (float)(maxY - minY) / newTop;
+        return newTop == minY ? 0 : 1 - (float(maxY - newTop) / float(maxY - minY));
     } else {
         scrollbarMouse.x = -1;
         scrollbarMouse.y = -1;
diff --git a/engines/nancy/scene.h b/engines/nancy/scene.h
index d699899d13..633b6da90b 100644
--- a/engines/nancy/scene.h
+++ b/engines/nancy/scene.h
@@ -62,6 +62,7 @@ public:
     void popScene();
     void addObjectToInventory(uint16 id);
     void removeObjectFromInventory(uint16 id, bool pickUp = false);
+    float handleScrollbar(uint id, bool reset = false);
 
 private:
     void init();
@@ -71,7 +72,6 @@ private:
     void handleMouse();
     void clearSceneData();
 
-    float handleScrollbar(uint id);
 
 public:
     enum State {
diff --git a/engines/nancy/textbox.cpp b/engines/nancy/textbox.cpp
index cd3667c4f2..7e185c15ad 100644
--- a/engines/nancy/textbox.cpp
+++ b/engines/nancy/textbox.cpp
@@ -25,6 +25,7 @@
 #include "engines/nancy/nancy.h"
 #include "engines/nancy/resource.h"
 #include "engines/nancy/graphics.h"
+#include "engines/nancy/scene.h"
 
 #include "common/error.h"
 #include "common/util.h"
@@ -68,6 +69,7 @@ void Textbox::clear() {
     _surface.clear();
     lineNr = 0;
     _responses.clear();
+    setPosition(_engine->sceneManager->handleScrollbar(0, true));
 }
 
 Common::Rect Textbox::processTextLine(const Common::String &text, uint16 fontID) {
@@ -183,6 +185,21 @@ int16 Textbox::getHovered(Common::Point mousePos) {
     return pickedResponse;
 }
 
+void Textbox::setPosition(float pos) {
+    if (pos == -1)
+        return;
+
+    pos = CLIP<float>(pos, 0, 1);
+    ZRenderStruct &zr = _engine->graphics->getZRenderStruct("FRAME TB SURF");
+
+    uint16 inner = getInnerHeight();
+    uint16 outer = zr.destRect.height();
+
+    if (inner > outer) {
+        zr.sourceRect.moveTo(zr.sourceRect.left, (inner - outer) * pos);
+    }
+}
+
 uint16 Textbox::getInnerHeight() {
     uint lineDist = _lineHeight + _lineHeight / 4;
     return lineNr * lineDist + 2 * _firstLineOffset;
diff --git a/engines/nancy/textbox.h b/engines/nancy/textbox.h
index 6d0f769e11..7987ee5f87 100644
--- a/engines/nancy/textbox.h
+++ b/engines/nancy/textbox.h
@@ -54,11 +54,12 @@ public:
     void processResponse(const Common::String &text, uint16 fontID, uint16 id, Common::String soundName);
 
     int16 getHovered(Common::Point mousePos);
-    uint16 getInnerHeight();
+    void setPosition(float pos);
 
     const Graphics::Surface *getSurface();
 
 private:
+    uint16 getInnerHeight();
     Common::Rect getCharacterSourceRect(char c, uint16 fontID);
     uint16 getCharacterWidth(char c, uint16 fontID);
     void drawText(const Common::String &text, uint16 fontID, uint16 left, uint16 bottom, bool color = false);


Commit: 3aaf2a9bea05f545c0ea9ac80b88d6811573f3e9
    https://github.com/scummvm/scummvm/commit/3aaf2a9bea05f545c0ea9ac80b88d6811573f3e9
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Major engine rewrite

Rewrote most of the engine using a much more object-oriented approach and using more of ScummVM's common classes. This design deviates quite a lot from the original engine's, but should be more maintainable and extensible in the future.

Changed paths:
  A engines/nancy/action/actionmanager.cpp
  A engines/nancy/action/actionmanager.h
  A engines/nancy/action/secondaryvideo.cpp
  A engines/nancy/action/secondaryvideo.h
  A engines/nancy/action/staticbitmapanim.cpp
  A engines/nancy/action/staticbitmapanim.h
  A engines/nancy/commontypes.h
  A engines/nancy/cursor.cpp
  A engines/nancy/cursor.h
  A engines/nancy/font.cpp
  A engines/nancy/font.h
  A engines/nancy/renderobject.cpp
  A engines/nancy/renderobject.h
  A engines/nancy/state/logo.cpp
  A engines/nancy/state/logo.h
  A engines/nancy/state/map.cpp
  A engines/nancy/state/map.h
  A engines/nancy/state/scene.cpp
  A engines/nancy/state/scene.h
  A engines/nancy/ui/frame.cpp
  A engines/nancy/ui/frame.h
  A engines/nancy/ui/inventorybox.cpp
  A engines/nancy/ui/inventorybox.h
  A engines/nancy/ui/scrollbar.cpp
  A engines/nancy/ui/scrollbar.h
  A engines/nancy/ui/textbox.cpp
  A engines/nancy/ui/textbox.h
  A engines/nancy/ui/viewport.cpp
  A engines/nancy/ui/viewport.h
  A engines/nancy/util.h
  R engines/nancy/datatypes.cpp
  R engines/nancy/datatypes.h
  R engines/nancy/logic.cpp
  R engines/nancy/logic.h
  R engines/nancy/logo.cpp
  R engines/nancy/logo.h
  R engines/nancy/map.cpp
  R engines/nancy/map.h
  R engines/nancy/menu.cpp
  R engines/nancy/menu.h
  R engines/nancy/playstate.h
  R engines/nancy/scene.cpp
  R engines/nancy/scene.h
  R engines/nancy/textbox.cpp
  R engines/nancy/textbox.h
    engines/nancy/action/actionrecord.h
    engines/nancy/action/arfactory_v1.cpp
    engines/nancy/action/primaryvideo.cpp
    engines/nancy/action/primaryvideo.h
    engines/nancy/action/recordtypes.cpp
    engines/nancy/action/recordtypes.h
    engines/nancy/action/responses.cpp
    engines/nancy/audio.cpp
    engines/nancy/audio.h
    engines/nancy/console.cpp
    engines/nancy/console.h
    engines/nancy/decompress.cpp
    engines/nancy/decompress.h
    engines/nancy/detection.cpp
    engines/nancy/detection.h
    engines/nancy/graphics.cpp
    engines/nancy/graphics.h
    engines/nancy/iff.cpp
    engines/nancy/iff.h
    engines/nancy/input.cpp
    engines/nancy/input.h
    engines/nancy/metaengine.cpp
    engines/nancy/module.mk
    engines/nancy/nancy.cpp
    engines/nancy/nancy.h
    engines/nancy/resource.cpp
    engines/nancy/resource.h
    engines/nancy/time.h
    engines/nancy/video.cpp
    engines/nancy/video.h


diff --git a/engines/nancy/action/actionmanager.cpp b/engines/nancy/action/actionmanager.cpp
new file mode 100644
index 0000000000..39e4ef4815
--- /dev/null
+++ b/engines/nancy/action/actionmanager.cpp
@@ -0,0 +1,329 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/nancy/commontypes.h"
+#include "engines/nancy/action/actionmanager.h"
+
+#include "engines/nancy/state/scene.h"
+
+#include "engines/nancy/nancy.h"
+#include "engines/nancy/cursor.h"
+#include "engines/nancy/input.h"
+#include "engines/nancy/state/scene.h"
+#include "engines/nancy/ui/viewport.h"
+
+#include "common/memstream.h"
+#include "common/events.h"
+#include "common/str.h"
+
+namespace Nancy {
+namespace Action {
+
+void ActionManager::handleInput(NancyInput &input) {
+    for (auto &rec : _records) {
+        if (rec->isActive) {
+            // Send input to all active records
+            rec->handleInput(input);
+        }
+
+        if (rec->isActive && rec->hasHotspot && _engine->scene->getViewport().convertViewportToScreen(rec->hotspot).contains(input.mousePos)) {
+            _engine->cursorManager->setCursorType(rec->getHoverCursor());
+
+            if (input.input & NancyInput::kLeftMouseButtonUp) {
+                input.input &= ~NancyInput::kLeftMouseButtonUp;
+
+                bool shouldTrigger = false;
+                int16 heldItem = _engine->scene->getHeldItem();
+                if (rec->itemRequired != -1) {
+                    if (heldItem == -1 && rec->itemRequired == -2) {
+                        shouldTrigger = true;
+                    } else {
+                        if (rec->itemRequired <= 100) {
+                            if (heldItem == rec->itemRequired) {
+                                shouldTrigger = true;
+                            }
+                        } else if (rec->itemRequired <= 110 && rec->itemRequired - 100 != heldItem) {
+                            // IDs 100 - 110 mean the record will activate when the object is _not_ the specified one
+                            shouldTrigger = true;
+                        }
+                    }
+                } else {
+                    shouldTrigger = true;
+                }
+                if (shouldTrigger) {
+                    rec->state = ActionRecord::ExecutionState::kActionTrigger;
+                    
+                    if (rec->itemRequired > 100 && rec->itemRequired <= 110) {
+                        rec->itemRequired -= 100;
+                    }
+
+                    // Re-add the object to the inventory unless it's marked as a one-time use
+                    if (rec->itemRequired == heldItem && rec->itemRequired != -1) {
+                        if (_engine->scene->getInventoryBox().getItemDescription(heldItem).oneTimeUse != 0) {
+                            _engine->scene->getInventoryBox().addItem(heldItem);
+                        }
+
+                        _engine->scene->setHeldItem(-1);
+                    }
+                }
+
+                break;
+            }
+        }
+    }
+}
+
+bool ActionManager::addNewActionRecord(Common::SeekableReadStream &inputData) {
+    inputData.seek(0x30);
+    byte ARType = inputData.readByte();
+    ActionRecord *newRecord = createActionRecord(ARType);
+
+    inputData.seek(0);
+    char *descBuf = new char[0x30];
+    inputData.read(descBuf, 0x30);
+    newRecord->description = Common::String(descBuf);
+    delete[] descBuf;
+
+    newRecord->type = inputData.readByte(); // redundant
+    newRecord->execType = inputData.readByte();
+
+    uint16 localChunkSize = newRecord->readData(inputData);
+    localChunkSize += 0x32;
+
+    // If the localChunkSize is less than the total data, there must be dependencies at the end of the chunk
+    uint16 depsDataSize = (uint16)inputData.size() - localChunkSize;
+    if (depsDataSize > 0) {
+        // Each dependency is 0x0C bytes long (in v1)
+        uint numDependencies = depsDataSize / 0xC;
+        if (depsDataSize % 0xC) {
+            error("Invalid dependency data size!");
+        }
+
+        // Initialize the dependencies data
+        inputData.seek(localChunkSize);
+        for (uint16 i = 0; i < numDependencies; ++i) {
+            newRecord->dependencies.push_back(DependencyRecord());
+            DependencyRecord &dep = newRecord->dependencies.back();
+
+            dep.type = (DependencyType)inputData.readByte();
+            dep.label = inputData.readByte();
+            dep.condition = inputData.readByte();
+            dep.orFlag = inputData.readByte();
+            dep.hours = inputData.readSint16LE();
+            dep.minutes = inputData.readSint16LE();
+            dep.seconds = inputData.readSint16LE();
+            dep.milliseconds = inputData.readSint16LE();
+
+            if (dep.type != kSceneCount || dep.hours != -1 || dep.minutes != -1 || dep.seconds != -1) {
+                dep.timeData = ((dep.hours * 60 + dep.minutes) * 60 + dep.seconds) * 1000 + dep.milliseconds;
+            }
+        }
+    } else {
+        // Set new record to active if it doesn't depend on anything
+        newRecord->isActive = true;
+    }
+
+    _records.push_back(newRecord);
+    return true;
+}
+
+void ActionManager::processActionRecords() {    
+    for (auto record : _records) {
+        if (record->isDone) {
+            continue;
+        }
+
+        if (!record->isActive) {
+            for (uint i = 0; i < record->dependencies.size(); ++i) {
+                DependencyRecord &dep = record->dependencies[i];
+                if (!dep.satisfied) {
+                    switch (dep.type) {
+                        case kNone:
+                            dep.satisfied = true;
+                            break;
+                        case kInventory:
+                            switch (dep.condition) {
+                                case kFalse:
+                                    // Item not in possession or held
+                                    if (_engine->scene->_flags.items[dep.label] == kFalse &&
+                                        dep.label != _engine->scene->_flags.heldItem) {
+                                        dep.satisfied = true;
+                                    }
+                                    break;
+                                case kTrue:
+                                    if (_engine->scene->_flags.items[dep.label] == kTrue ||
+                                        dep.label == _engine->scene->_flags.heldItem) {
+                                        dep.satisfied = true;
+                                    }
+                                    break;
+                                default:
+                                    break;
+                            }
+                            break;
+                        case kEventFlag:
+                            if (_engine->scene->getEventFlag(dep.label, (NancyFlag)dep.condition))
+                                // nancy1 has code for some timer array that never gets used
+                                // and is discarded from nancy2 onward
+                                dep.satisfied = true;
+                            break;
+                        case kLogicCondition:
+                            if (_engine->scene->_flags.logicConditions[dep.label].flag == dep.condition) {
+                                // Wait for specified time before satisfying dependency condition
+                                Time elapsed = _engine->scene->_timers.totalTime - _engine->scene->_flags.logicConditions[dep.label].timestamp;
+                                if (elapsed >= dep.timeData)
+                                    dep.satisfied = true;
+                            }
+                            break;
+                        case kTotalTime:
+                            if (_engine->scene->_timers.totalTime >= dep.timeData)
+                                dep.satisfied = true;
+                            break;
+                        case kSceneTime:
+                            if (_engine->scene->_timers.sceneTime >= dep.timeData)
+                                dep.satisfied = true;
+                            break;
+                        case kPlayerTime:
+                            // TODO almost definitely wrong, as the original engine treats player time differently
+                            if (_engine->scene->_timers.playerTime >= dep.timeData)
+                                dep.satisfied = true;
+                            break;
+                        /*case 7:
+                            // TODO
+                            break;
+                        case 8:
+                            // TODO
+                            break;*/
+                        case kSceneCount:
+                            // This dependency type keeps its data in the time variables
+                            // Also, I'm pretty sure it never gets used
+                            switch (dep.milliseconds) {
+                                case 1:
+                                    if (dep.seconds < _engine->scene->_sceneState.sceneHitCount[dep.hours])
+                                        dep.satisfied = true;
+                                    break;
+                                case 2:
+                                    if (dep.seconds > _engine->scene->_sceneState.sceneHitCount[dep.hours])
+                                        dep.satisfied = true;
+                                    break;
+                                case 3:
+                                    if (dep.seconds == _engine->scene->_sceneState.sceneHitCount[dep.hours])
+                                        dep.satisfied = true;
+                                    break;
+                            }
+                            break;
+                        case kResetOnNewDay:
+                            if (record->days == -1) {
+                                record->days = _engine->scene->_timers.playerTime.getDays();
+                                dep.satisfied = true;
+                                break;
+                            }
+
+                            if (record->days < _engine->scene->_timers.playerTime.getDays()) {
+                                record->days = _engine->scene->_timers.playerTime.getDays();
+                                for (uint j = 0; j < record->dependencies.size(); ++j) {
+                                    if (record->dependencies[j].type == kPlayerTime) {
+                                        record->dependencies[j].satisfied = false;
+                                    }
+                                }
+                            }
+                            break;
+                        case kUseItem: {
+                            bool hasUnsatisfiedDeps = false;
+                            for (uint j = 0; j < record->dependencies.size(); ++j) {
+                                if (j != i && record->dependencies[j].satisfied == false) {
+                                    hasUnsatisfiedDeps = true;
+                                }
+                            }
+
+                            if (hasUnsatisfiedDeps) {
+                                break;
+                            }
+
+                            record->itemRequired = dep.label;
+
+                            if (dep.condition == 1) {
+                                record->itemRequired += 100;
+                            }
+                            
+                            dep.satisfied = true;
+                            break;
+                        }
+                        case kTimeOfDay:
+                            if (dep.label == (byte)_engine->scene->_timers.timeOfDay)
+                                dep.satisfied = true;
+                            break;
+                        case kTimerNotDone:
+                            if (_engine->scene->_timers.timerTime <= dep.timeData)
+                                dep.satisfied = true;
+                            break;
+                        case kTimerDone:
+                            if (_engine->scene->_timers.timerTime > dep.timeData)
+                                dep.satisfied = true;
+                            break;
+                        case kDifficultyLevel:
+                            if (dep.condition == _engine->scene->_difficulty)
+                                dep.satisfied = true;
+                            break;
+                        default:
+                            break;
+                    }
+                }
+            }
+
+            // An orFlag marks that its corresponding dependency and the one after it
+            // mutually satisfy each other; if one is satisfied, so is the other
+            for (uint i = 1; i < record->dependencies.size(); ++i) {
+                if (record->dependencies[i-1].orFlag) {
+                    if (record->dependencies[i-1].satisfied)
+                        record->dependencies[i].satisfied = true;
+                    if (record->dependencies[i].satisfied)
+                        record->dependencies[i-1].satisfied = true;
+                }
+            }
+
+            // Check if all dependencies have been satisfied, and activate the record if they have
+            uint satisfied = 0;
+            for (uint i = 0; i < record->dependencies.size(); ++i) {
+                if (record->dependencies[i].satisfied)
+                    ++satisfied;
+            }
+
+            if (satisfied == record->dependencies.size())
+                record->isActive = true;
+        
+        }
+
+        if (record->isActive) {
+            record->execute(_engine);
+        }
+    }
+}
+
+void ActionManager::clearActionRecords() {
+    for (auto &r : _records) {
+        delete r;
+    }
+    _records.clear();
+}
+
+} // End of namespace Action
+} // End of namespace Nancy
diff --git a/engines/nancy/logic.h b/engines/nancy/action/actionmanager.h
similarity index 70%
rename from engines/nancy/logic.h
rename to engines/nancy/action/actionmanager.h
index b47437ed9c..15f1205bac 100644
--- a/engines/nancy/logic.h
+++ b/engines/nancy/action/actionmanager.h
@@ -20,8 +20,8 @@
  *
  */
 
-#ifndef NANCY_LOGIC_H
-#define NANCY_LOGIC_H
+#ifndef NANCY_ACTION_ACTIONMANAGER_H
+#define NANCY_ACTION_ACTIONMANAGER_H
 
 #include "engines/nancy/action/actionrecord.h"
 
@@ -33,29 +33,39 @@
 namespace Nancy {
 
 class NancyEngine;
-class SceneManager;
+struct NancyInput;
 
-class Logic {
-    friend class SceneManager;
+namespace State {
+class Scene;
+}
+
+namespace Action {
+
+// The class that handles ActionRecords and their execution
+class ActionManager {
+    friend class Nancy::State::Scene;
 
 public:
-    Logic(NancyEngine* engine): _engine(engine), ignorePrimaryVideo(false) {}
-    virtual ~Logic() {}
+    ActionManager(Nancy::NancyEngine* engine) :
+        _engine(engine) {}
+    virtual ~ActionManager() {}
+
+    void handleInput(NancyInput &input);
 
-    bool addNewActionRecord(Common::SeekableReadStream &inputData);
     void processActionRecords();
+    bool addNewActionRecord(Common::SeekableReadStream &inputData);
     Common::Array<ActionRecord *> &getActionRecords() { return _records; }
-    ActionRecord * getActionRecord(uint id) { if (id < _records.size()) return _records[id]; else return nullptr;}
+    ActionRecord *getActionRecord(uint id) { if (id < _records.size()) return _records[id]; else return nullptr;}
     void clearActionRecords();
 
-    bool ignorePrimaryVideo; // hack
-
 protected:
     virtual ActionRecord *createActionRecord(uint16 type);
-    NancyEngine *_engine;
+
+    Nancy::NancyEngine *_engine;
     Common::Array<ActionRecord *> _records;
 };
 
+} // End of namespace Action
 } // End of namespace Nancy
 
-#endif // NANCY_LOGIC_H
\ No newline at end of file
+#endif // NANCY_ACTION_ACTIONMANAGER_H
diff --git a/engines/nancy/action/actionrecord.h b/engines/nancy/action/actionrecord.h
index d871ec9b4f..528185fff8 100644
--- a/engines/nancy/action/actionrecord.h
+++ b/engines/nancy/action/actionrecord.h
@@ -23,7 +23,10 @@
 #ifndef NANCY_ACTION_ACTIONRECORD_H
 #define NANCY_ACTION_ACTIONRECORD_H
 
+#include "engines/nancy/input.h"
+
 #include "engines/nancy/time.h"
+#include "engines/nancy/cursor.h"
 
 #include "common/str.h"
 #include "common/stream.h"
@@ -32,6 +35,8 @@
 namespace Nancy {
 
 class NancyEngine;
+
+namespace Action {
     
 enum DependencyType : byte {
     kNone               = 0,
@@ -51,6 +56,8 @@ enum DependencyType : byte {
     kDifficultyLevel    = 15
 };
 
+// Describes a condition that needs to be fulfilled before the
+// action record can be executed
 struct DependencyRecord {
     DependencyType type;    // 0x00
     byte label;             // 0x01
@@ -60,58 +67,64 @@ struct DependencyRecord {
     int16 minutes;          // 0x06
     int16 seconds;          // 0x08
     int16 milliseconds;     // 0x0A
+
+    bool satisfied;
+    Time timeData;
 };
 
+// Describes a single action that will be performed on every update.
+// Supports conditional execution (via dependencies) and can have
+// clickable hotspots on screen.
+// Does _not_ support drawing to screen, records that need this functionality
+// will have to also subclass RenderObject.
 class ActionRecord {
+    friend class ActionManager;
 public:
     enum ExecutionState { kBegin, kRun, kActionTrigger };
     ActionRecord() :
         type(0),
         execType(0),
-        dependencies(nullptr),
-        numDependencies(0),
         isActive(0),
-        satisfiedDependencies(nullptr),
-        timers(nullptr),
-        orFlags(nullptr),
         isDone(false),
         hasHotspot(false),
         state(ExecutionState::kBegin),
         days(-1),
         itemRequired(-1) {}
-    virtual ~ActionRecord() { delete[] dependencies; delete rawData; delete[] satisfiedDependencies; delete[] timers; delete orFlags; }
+    virtual ~ActionRecord() {}
 
     virtual uint16 readData(Common::SeekableReadStream &stream) =0;
     virtual void execute(NancyEngine *engine) {};
 
+    virtual CursorManager::CursorType getHoverCursor() const { return CursorManager::kHotspot; }
+    virtual void handleInput(NancyInput &input) {}
+
 protected:
-    // TODO these are temporary until every data class is figured out
+    // TODO this is temporary until every record type is figured out
     uint16 readRaw(Common::SeekableReadStream &stream, uint16 bytes) {
-        rawData = new byte[bytes];
-        stream.read(rawData, bytes);
+        stream.skip(bytes);
         return bytes;
     }
-    byte *rawData = nullptr;
 
 public:
-    Common::String description;     // 0x00
-    byte type;                      // 0x30
-    byte execType;                  // 0x31
+    Common::String description;                     // 0x00
+    byte type;                                      // 0x30
+    byte execType;                                  // 0x31
     // 0x32 data
-    DependencyRecord *dependencies; // 0x36
-    byte numDependencies;           // 0x3A
-    bool isActive;                  // 0x3B
-    bool *satisfiedDependencies;    // 0x3C
-    Time *timers;                   // 0x48
-    bool *orFlags;                  // 0x78
-    bool isDone;                    // 0x84
-    bool hasHotspot;                // 0x85
-    Common::Rect hotspot;           // 0x89
-    ExecutionState state;           // 0x91
-    int16 days;                     // 0x95
-    int8 itemRequired;              // 0x97
+    Common::Array<DependencyRecord> dependencies;   // 0x36
+    // 0x3A numDependencies
+    bool isActive;                                  // 0x3B
+    // 0x3C satisfiedDependencies[] 
+    // 0x48 timers[]
+    // 0x78 orFlags[]
+    bool isDone;                                    // 0x84
+    bool hasHotspot;                                // 0x85
+    Common::Rect hotspot;                           // 0x89
+    ExecutionState state;                           // 0x91
+    int16 days;                                     // 0x95
+    int8 itemRequired;                              // 0x97
 };
 
+} // End of namespace Action
 } // End of namespace Nancy
 
-#endif // NANCY_ACTION_ACTIONRECORD_H
\ No newline at end of file
+#endif // NANCY_ACTION_ACTIONRECORD_H
diff --git a/engines/nancy/action/arfactory_v1.cpp b/engines/nancy/action/arfactory_v1.cpp
index a7b4b5760a..cef5602325 100644
--- a/engines/nancy/action/arfactory_v1.cpp
+++ b/engines/nancy/action/arfactory_v1.cpp
@@ -20,15 +20,22 @@
  *
  */
 
-#include "engines/nancy/logic.h"
+#include "engines/nancy/action/actionmanager.h"
 #include "engines/nancy/action/recordtypes.h"
 #include "engines/nancy/action/actionrecord.h"
 #include "engines/nancy/action/primaryvideo.h"
+#include "engines/nancy/action/secondaryvideo.h"
+#include "engines/nancy/action/staticbitmapanim.h"
+
+#include "engines/nancy/state/scene.h"
+
+#include "engines/nancy/nancy.h"
 
 namespace Nancy {
+namespace Action {
 
 // TODO put this function in a subclass
-ActionRecord *Logic::createActionRecord(uint16 type) {
+ActionRecord *ActionManager::createActionRecord(uint16 type) {
     type -= 0xA;
     switch (type) {
         case 0x00:
@@ -40,7 +47,7 @@ ActionRecord *Logic::createActionRecord(uint16 type) {
         case 0x03:
             return new HotMultiframeMultisceneChange();
         case 0x04:
-            return new Hot1FrSceneChange();
+            return new Hot1FrExitSceneChange();
         case 0x0C:
             return new StartFrameNextScene();
         case 0x14:
@@ -48,17 +55,17 @@ ActionRecord *Logic::createActionRecord(uint16 type) {
         case 0x15:
             return new StartStopPlayerScrolling(); // TODO
         case 0x28:
-            return new PlayPrimaryVideoChan0();
+            return new PlayPrimaryVideoChan0(_engine->scene->getViewport());
         case 0x29:
-            return new PlaySecondaryVideoChan0();
+            return new PlaySecondaryVideo(_engine->scene->getViewport());
         case 0x2A:
-            return new PlaySecondaryVideoChan1();
+            return new PlaySecondaryVideo(_engine->scene->getViewport());
         case 0x2B:
-            return new PlaySecondaryMovie();
+            return new PlaySecondaryMovie(_engine->scene->getViewport());
         case 0x2C:
             return new PlayStaticBitmapAnimation();
         case 0x2D:
-            return new PlayIntStaticBitmapAnimation();
+            return new PlayIntStaticBitmapAnimation(_engine->scene->getViewport());
         case 0x32:
             return new MapCall();
         case 0x33:
@@ -120,7 +127,7 @@ ActionRecord *Logic::createActionRecord(uint16 type) {
         case 0x6F:
             return new RemoveInventoryNoHS();
         case 0x70:
-            return new ShowInventoryItem();
+            return new ShowInventoryItem(_engine->scene->getViewport());
         case 0x8C:
             return new PlayDigiSoundAndDie(); // TODO
         case 0x8D:
@@ -137,4 +144,5 @@ ActionRecord *Logic::createActionRecord(uint16 type) {
     }
 }
 
-} // End of namespace Nancy
\ No newline at end of file
+} // End of namespace Action
+} // End of namespace Nancy
diff --git a/engines/nancy/action/primaryvideo.cpp b/engines/nancy/action/primaryvideo.cpp
index e5adadb03a..1a96b5752b 100644
--- a/engines/nancy/action/primaryvideo.cpp
+++ b/engines/nancy/action/primaryvideo.cpp
@@ -23,25 +23,49 @@
 #include "engines/nancy/action/primaryvideo.h"
 
 #include "engines/nancy/action/responses.cpp"
+#include "engines/nancy/action/actionmanager.h"
 
 #include "engines/nancy/nancy.h"
-#include "engines/nancy/scene.h"
-#include "engines/nancy/logic.h"
+#include "engines/nancy/state/scene.h"
 #include "engines/nancy/nancy.h"
-#include "engines/nancy/graphics.h"
 #include "engines/nancy/audio.h"
+#include "engines/nancy/util.h"
 
 #include "common/file.h"
 #include "common/random.h"
 
 namespace Nancy {
+namespace Action {
 
-// Simple helper function to read rectangles
-static void readRect(Common::SeekableReadStream &stream, Common::Rect &inRect) {
-    inRect.left = stream.readUint32LE();
-    inRect.top = stream.readUint32LE();
-    inRect.right = stream.readUint32LE();
-    inRect.bottom = stream.readUint32LE();
+bool PlayPrimaryVideoChan0::isExitingScene = false;
+
+PlayPrimaryVideoChan0::~PlayPrimaryVideoChan0() {
+    _decoder.close();
+    _engine->scene->getTextbox().setVisible(false);
+}
+
+void PlayPrimaryVideoChan0::init() {
+    _decoder.loadFile(videoName + ".avf");
+    _drawSurface.create(src.width(), src.height(), _decoder.getPixelFormat());
+
+    RenderObject::init();
+}
+
+void PlayPrimaryVideoChan0::updateGraphics() {
+    if (!_decoder.isVideoLoaded()) {
+        return;
+    }
+
+    if (!_decoder.isPlaying()) {
+        _decoder.start();
+    }
+
+    if (_decoder.needsUpdate()) {
+        _drawSurface.blitFrom(*_decoder.decodeNextFrame(), src, Common::Point());
+        _needsRedraw = true;
+    }
+
+    RenderObject::updateGraphics();
 }
 
 uint16 PlayPrimaryVideoChan0::readData(Common::SeekableReadStream &stream) {
@@ -54,11 +78,11 @@ uint16 PlayPrimaryVideoChan0::readData(Common::SeekableReadStream &stream) {
     stream.skip(0x13);
 
     readRect(stream, src);
-    readRect(stream, dest);
+    readRect(stream, _screenPosition);
 
     char *rawText = new char[1500]();
     stream.read(rawText, 1500);
-    assembleText(rawText, text, 1500);
+    UI::Textbox::assembleTextLine(rawText, text, 1500);
     delete[] rawText;
 
     stream.read(name, 10);
@@ -98,7 +122,7 @@ uint16 PlayPrimaryVideoChan0::readData(Common::SeekableReadStream &stream) {
             }
             rawText = new char[400];
             stream.read(rawText, 400);
-            assembleText(rawText, response.text, 400);
+            UI::Textbox::assembleTextLine(rawText, response.text, 400);
             delete[] rawText;
 
             stream.read(name, 10);
@@ -106,7 +130,7 @@ uint16 PlayPrimaryVideoChan0::readData(Common::SeekableReadStream &stream) {
             stream.skip(1);
             response.sceneChange.readData(stream);
             response.flagDesc.label = stream.readSint16LE();
-            response.flagDesc.flag = (PlayState::Flag)stream.readByte();
+            response.flagDesc.flag = (NancyFlag)stream.readByte();
 
             stream.skip(0x32);
         }
@@ -137,31 +161,25 @@ uint16 PlayPrimaryVideoChan0::readData(Common::SeekableReadStream &stream) {
 
             flagsStruct.type = (FlagsStruct::ConditionType)stream.readByte();
             flagsStruct.label = stream.readSint16LE();
-            flagsStruct.flag = (PlayState::Flag)stream.readByte();
+            flagsStruct.flag = (NancyFlag)stream.readByte();
         }
     }
 
+    isExitingScene = false;
+
     bytesRead = stream.pos() - bytesRead;
     return bytesRead;
 }
 
 void PlayPrimaryVideoChan0::execute(NancyEngine *engine) {
-    ZRenderStruct &zr = engine->graphics->getZRenderStruct("PRIMARY VIDEO");
-    AVFDecoder &decoder = engine->graphics->_primaryVideoDecoder;
-    View &viewportDesc = engine->graphics->viewportDesc;
+    if (isExitingScene) {
+        return;
+    }
+
     switch (state) {
         case kBegin:
-            zr.sourceRect = src;
-            zr.destRect = dest;
-            zr.destRect.left += viewportDesc.destination.left;
-            zr.destRect.top += viewportDesc.destination.top;
-            zr.destRect.right += viewportDesc.destination.left;
-            zr.destRect.bottom += viewportDesc.destination.top;
-            zr.isActive = true;
-            if (decoder.isVideoLoaded()) {
-                decoder.close();
-            }
-            decoder.loadFile(videoName + ".avf");
+            init();
+            registerGraphics();
             engine->sound->loadSound(soundName, soundChannelID, numRepeats, volume);
             engine->sound->pauseSound(soundChannelID, false);
             state = kRun;
@@ -169,8 +187,8 @@ void PlayPrimaryVideoChan0::execute(NancyEngine *engine) {
         case kRun:
             if (!hasDrawnTextbox) {
                 hasDrawnTextbox = true;
-                engine->graphics->_textbox.clear();
-                engine->graphics->_textbox.processTextLine(text, 1);
+                engine->scene->getTextbox().clear();
+                engine->scene->getTextbox().addTextLine(text);
 
                 // Add responses when conditions have been satisfied
                 if (conditionalResponseCharacterID != 10) {
@@ -183,25 +201,25 @@ void PlayPrimaryVideoChan0::execute(NancyEngine *engine) {
 
                 for (uint i = 0; i < responses.size(); ++i) {
                     auto &res = responses[i];
-                    engine->graphics->_textbox.processResponse(res.text, 1, i, res.soundName);
+                    engine->scene->getTextbox().addTextLine(res.text);
                 }
-
-                ZRenderStruct &fr = engine->graphics->getZRenderStruct("FRAME TB SURF");
-                fr.isActive = true;
-                fr.sourceRect = Common::Rect(fr.destRect.width(), fr.destRect.height());
             }
+
             if (!engine->sound->isSoundPlaying(soundChannelID)) {
                 if (responses.size() == 0) {
+                    // NPC has finished talking with no responses available, auto-advance to next scene
                     state = kActionTrigger;
                 } else {
+                    // NPC has finished talking, we have responses
                     for (uint i = 0; i < 30; ++i) {
-                        if (engine->playState.logicConditions[i] == PlayState::kTrue) {
+                        if (engine->scene->getLogicCondition(i, kTrue)) {
                             pickedResponse = i;
                             break;
                         }
                     }
 
                     if (pickedResponse != -1) {
+                        // Player has picked response, play sound file and change state
                         sceneChange = responses[pickedResponse].sceneChange;
                         engine->sound->loadSound(responses[pickedResponse].soundName, soundChannelID, numRepeats, volume);
                         engine->sound->pauseSound(soundChannelID, false);
@@ -214,21 +232,20 @@ void PlayPrimaryVideoChan0::execute(NancyEngine *engine) {
             // process flags structs
             for (auto flags : flagsStructs) {
                 bool conditionsSatisfied = true;
-                for (auto cond : flags.conditionFlags) {
-                    // TODO
-                    error("Condition flags not evaluated");
+                if (flags.conditionFlags.size()) {
+                    error("Condition flags not evaluated, please fix");
                 }
 
                 if (conditionsSatisfied) {
                     switch (flags.type) {
                         case FlagsStruct::kEventFlags:
-                            engine->playState.eventFlags[flags.label] = flags.flag;
+                            engine->scene->setEventFlag(flags.label, flags.flag);
                             break;
                         case FlagsStruct::kInventory:
-                            if (flags.flag == PlayState::kTrue) {
-                                engine->sceneManager->addObjectToInventory(flags.label);
+                            if (flags.flag == kTrue) {
+                                engine->scene->addItemToInventory(flags.label);
                             } else {
-                                engine->sceneManager->removeObjectFromInventory(flags.label);
+                                engine->scene->removeItemFromInventory(flags.label);
                             }
                             break;
                         default:
@@ -236,25 +253,23 @@ void PlayPrimaryVideoChan0::execute(NancyEngine *engine) {
                     }
                 }
             }
-
+            
             if (pickedResponse != -1) {
-                int16 label = responses[pickedResponse].flagDesc.label;
-                if (label != -1) {
-                    engine->playState.eventFlags[label] = responses[pickedResponse].flagDesc.flag;
-                }
+                // Set response's event flag, if any
+                engine->scene->setEventFlag(responses[pickedResponse].flagDesc.label, responses[pickedResponse].flagDesc.flag);
             }
 
             if (!engine->sound->isSoundPlaying(soundChannelID)) {
                 if (shouldPopScene) {
-                    engine->sceneManager->popScene();
+                    // Exit dialogue
+                    engine->scene->popScene();
                 } else {
+                    // Continue to next dialogue scene
                     SceneChange::execute(engine);
                 }
+                isExitingScene = true;
             }
 
-            // awful hack
-            engine->logic->ignorePrimaryVideo = true;
-
             break;
     }
 }
@@ -268,7 +283,7 @@ void PlayPrimaryVideoChan0::addConditionalResponses(NancyEngine *engine) {
                     break;
                 }
 
-                if (engine->playState.eventFlags[cond.label] != cond.flag) {
+                if (!engine->scene->getEventFlag(cond.label, cond.flag)) {
                     isSatisfied = false;
                     break;
                 }
@@ -317,16 +332,5 @@ void PlayPrimaryVideoChan0::addGoodbye(NancyEngine *engine) {
     }
 }
 
-void PlayPrimaryVideoChan0::assembleText(char *rawCaption, Common::String &output, uint size) {
-    for (uint i = 0; i < size; ++i) {
-        // A single line can be broken up into bits, look for them and
-        // concatenate them when we're done
-        if (rawCaption[i] != 0) {
-            Common::String newBit(rawCaption + i);
-            output += newBit;
-            i += newBit.size();
-        }
-    }
-}
-
-} // End of namespace Nancy
\ No newline at end of file
+} // End of namespace Action
+} // End of namespace Nancy
diff --git a/engines/nancy/action/primaryvideo.h b/engines/nancy/action/primaryvideo.h
index 75f5bb9be3..8ea257cbb6 100644
--- a/engines/nancy/action/primaryvideo.h
+++ b/engines/nancy/action/primaryvideo.h
@@ -24,12 +24,21 @@
 #define NANCY_ACTION_PRIMARYVIDEO_H
 
 #include "engines/nancy/action/recordtypes.h"
+#include "engines/nancy/renderobject.h"
+
+#include "engines/nancy/video.h"
+
+#include "common/str.h"
+#include "common/array.h"
 
 namespace Nancy {
 
 class NancyEngine;
 
-class PlayPrimaryVideoChan0 : public SceneChange {
+namespace Action {
+
+// ActionRecord subclass that handles all NPC dialog and nancy1's intro video
+class PlayPrimaryVideoChan0 : public SceneChange, public RenderObject {
 
 struct ConditionFlags {
     byte unknown[5];
@@ -49,10 +58,16 @@ struct FlagsStruct {
 
     ConditionType type;
     int16 label;
-    PlayState::Flag flag;
+    NancyFlag flag;
 };
 
 public:
+    PlayPrimaryVideoChan0(RenderObject &redrawFrom) : RenderObject(redrawFrom) {}
+    virtual ~PlayPrimaryVideoChan0();
+
+    virtual void init()override;
+    virtual void updateGraphics() override;
+
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
     virtual void execute(NancyEngine *engine) override;
     
@@ -62,7 +77,7 @@ public:
 
     Common::String videoName; // 0x00
     Common::Rect src; // 0x1D
-    Common::Rect dest; // 0x2D
+    // _screenPosition 0x2D
     Common::String text; // 0x3D
 
     Common::String soundName; // 0x619, TODO make a proper soundDesc struct
@@ -79,14 +94,21 @@ public:
     Common::Array<ResponseStruct> responses;
     Common::Array<FlagsStruct> flagsStructs;
 
+    AVFDecoder _decoder;
+
     bool hasDrawnTextbox = false;
     int16 pickedResponse = -1;
 
-private:
-    void assembleText(char *rawCaption, Common::String &output, uint size);
+    // Used to avoid showing first frame of unrelated primary video between scenes
+    static bool isExitingScene;
 
+protected:
+    virtual uint16 getZOrder() const override { return 8; }
+    virtual BlitType getBlitType() const override { return kNoTrans; }
+    virtual bool isViewportRelative() const override { return true; }
 };
 
-}
+} // End of namespace Action
+} // End of namespace Nancy
 
-#endif // NANCY_ACTION_PRIMARYVIDEO_H
\ No newline at end of file
+#endif // NANCY_ACTION_PRIMARYVIDEO_H
diff --git a/engines/nancy/action/recordtypes.cpp b/engines/nancy/action/recordtypes.cpp
index a4b5fc9249..341aec4331 100644
--- a/engines/nancy/action/recordtypes.cpp
+++ b/engines/nancy/action/recordtypes.cpp
@@ -21,26 +21,22 @@
  */
 
 #include "engines/nancy/action/recordtypes.h"
+#include "engines/nancy/action/actionmanager.h"
+
+#include "engines/nancy/state/scene.h"
+
 #include "engines/nancy/nancy.h"
-#include "engines/nancy/scene.h"
-#include "engines/nancy/logic.h"
 #include "engines/nancy/nancy.h"
 #include "engines/nancy/graphics.h"
 #include "engines/nancy/audio.h"
 #include "engines/nancy/input.h"
 #include "engines/nancy/resource.h"
+#include "engines/nancy/util.h"
 
 #include "common/str.h"
 
 namespace Nancy {
-
-// Simple helper function to read rectangles
-static void readRect(Common::SeekableReadStream &stream, Common::Rect &inRect) {
-    inRect.left = stream.readUint32LE();
-    inRect.top = stream.readUint32LE();
-    inRect.right = stream.readUint32LE();
-    inRect.bottom = stream.readUint32LE();
-}
+namespace Action {
 
 void HotspotDesc::readData(Common::SeekableReadStream &stream) {
     frameID = stream.readUint16LE();
@@ -53,25 +49,16 @@ void BitmapDesc::readData(Common::SeekableReadStream &stream) {
     readRect(stream, dest);
 }
 
-void SecondaryVideoDesc::readData(Common::SeekableReadStream &stream) {
-    frameID = stream.readUint16LE();
-    readRect(stream, srcRect);
-    readRect(stream, destRect);
-    stream.skip(0x20);
-}
-
 void EventFlagsDesc::readData(Common::SeekableReadStream &stream) {
     for (uint i = 0; i < 10; ++i) {
         descs[i].label = stream.readSint16LE();
-        descs[i].flag = (PlayState::Flag)stream.readUint16LE();
+        descs[i].flag = (NancyFlag)stream.readUint16LE();
     }
 }
 
 void EventFlagsDesc::execute(NancyEngine *engine) {
     for (uint i = 0; i < 10; ++i) {
-        if (descs[i].label != -1) {
-            engine->playState.eventFlags[descs[i].label] = descs[i].flag;
-        }
+        engine->scene->setEventFlag(descs[i].label, descs[i].flag);
     }
 }
 
@@ -88,7 +75,7 @@ uint16 SceneChange::readData(Common::SeekableReadStream &stream) {
 }
 
 void SceneChange::execute(NancyEngine *engine) {
-    engine->sceneManager->changeScene(sceneChange.sceneID, sceneChange.frameID, sceneChange.verticalOffset, sceneChange.doNotStartSound);
+    engine->scene->changeScene(sceneChange.sceneID, sceneChange.frameID, sceneChange.verticalOffset, sceneChange.doNotStartSound);
     isDone = true;
 }
 
@@ -114,7 +101,7 @@ void HotMultiframeSceneChange::execute(NancyEngine *engine) {
         case kRun:
             hasHotspot = false;
             for (uint i = 0; i < hotspots.size(); ++i) {
-                if (hotspots[i].frameID == engine->playState.currentViewFrame) {
+                if (hotspots[i].frameID == engine->scene->getSceneInfo().frameID) {
                     hasHotspot = true;
                     hotspot = hotspots[i].coords;
                 }
@@ -139,7 +126,7 @@ void Hot1FrSceneChange::execute(NancyEngine *engine) {
             state = kRun;
             // fall through
         case kRun:
-            if (hotspotDesc.frameID == engine->playState.currentViewFrame) {
+            if (hotspotDesc.frameID == engine->scene->getSceneInfo().frameID) {
                 hasHotspot = true;
             } else {
                 hasHotspot = false;
@@ -168,196 +155,6 @@ uint16 StartStopPlayerScrolling::readData(Common::SeekableReadStream &stream) {
     return 1;
 }
 
-uint16 PlaySecondaryVideo::readData(Common::SeekableReadStream &stream) {
-    char buf[10];
-    stream.read(buf, 10);
-    filename = Common::String(buf);
-    stream.skip(0x14);
-    loopFirstFrame = stream.readUint16LE();
-    loopLastFrame = stream.readUint16LE();
-    onHoverFirstFrame = stream.readUint16LE();
-    onHoverLastFrame = stream.readUint16LE();
-    onHoverEndFirstFrame = stream.readUint16LE();
-    onHoverEndLastFrame = stream.readUint16LE();
-    SceneChange::readData(stream);
-    stream.skip(1);
-
-    uint16 numVideoDescs = stream.readUint16LE();
-    for (uint i = 0; i < numVideoDescs; ++i) {
-        videoDescs.push_back(SecondaryVideoDesc());
-        videoDescs[i].readData(stream);
-    }
-
-    return 0x35 + (numVideoDescs * 0x42);
-}
-
-void PlaySecondaryVideo::execute(NancyEngine *engine) {
-    switch (state) {
-        case kBegin:
-            engine->graphics->loadSecondaryVideo(channelID(), filename, this);
-            engine->graphics->setupSecondaryVideo(channelID(), loopFirstFrame, loopLastFrame, true);
-            state = kRun;
-            // fall through
-        case kRun: {
-            ZRenderStruct &zr = engine->graphics->getZRenderStruct("SEC VIDEO 0");
-            zr.isActive = false;
-            hasHotspot = false;
-
-            int activeFrame = -1;
-
-            for (uint i = 0; i < videoDescs.size(); ++i) {
-                if (videoDescs[i].frameID == engine->playState.currentViewFrame) {
-                    activeFrame = i;
-                }
-            }
-
-            if (activeFrame != -1) {
-                // Activate the ZRenderStruct
-                zr.sourceRect = videoDescs[activeFrame].srcRect;
-                zr.destRect = videoDescs[activeFrame].destRect;
-                zr.destRect.left += engine->graphics->viewportDesc.destination.left;
-                zr.destRect.top += engine->graphics->viewportDesc.destination.top;
-                zr.destRect.top -= engine->playState.verticalScroll;
-                zr.destRect.right += engine->graphics->viewportDesc.destination.left;
-                zr.destRect.bottom += engine->graphics->viewportDesc.destination.top;
-                zr.destRect.bottom -= engine->playState.verticalScroll;
-                zr.isActive = true;
-
-                // Activate the hotspot
-                hotspot = videoDescs[activeFrame].destRect;
-                hasHotspot = true;
-
-                // check if we're hovered this frame
-                bool isHovered = engine->logic->getActionRecord(engine->input->hoveredElementID) == this;
-
-                switch (hoverState) {
-                    case kEndHoverDone:
-                        hoverState = kNoHover;
-                        engine->graphics->setupSecondaryVideo(channelID(), loopFirstFrame, loopLastFrame, true);
-                        break;
-                    case kNoHover:
-                        if (isHovered) {
-                            // Player has just hovered over, play the hover animation once
-                            hoverState = kHover;
-                            engine->graphics->setupSecondaryVideo(channelID(), onHoverFirstFrame, onHoverLastFrame, false);
-                        }
-                        break;
-                    case kHover:
-                        if (!isHovered) {
-                            // Player has just stopped hovering, reverse the playback and go back to frame 0
-                            hoverState = kEndHover;
-                            engine->graphics->setupSecondaryVideo(channelID(), onHoverEndLastFrame, onHoverEndFirstFrame, false);
-                        }
-                        break;
-                    case kEndHover:
-                        break;
-                }
-
-                engine->graphics->playSecondaryVideo(channelID());
-            } else {
-                engine->graphics->stopSecondaryVideo(channelID());
-            }
-
-            break;
-        }
-        case kActionTrigger:
-            engine->sceneManager->pushScene();
-            SceneChange::execute(engine);
-            break;
-    }
-}
-
-uint16 PlaySecondaryMovie::readData(Common::SeekableReadStream &stream) {  
-    char name[10];
-    stream.read(name, 10);
-    videoName = Common::String(name);
-
-    stream.skip(0x1C);
-    for (uint i = 0; i < 15; ++i) {
-        frameFlags[i].frameID = stream.readSint16LE();
-        frameFlags[i].flagDesc.label = stream.readSint16LE();
-        frameFlags[i].flagDesc.flag = (PlayState::Flag)stream.readUint16LE();
-    }
-
-    triggerFlags.readData(stream);
-    stream.read(name, 10);
-    soundName = Common::String(name);
-    soundChannel = stream.readUint16LE();
-    stream.skip(0xE);
-    soundVolume = stream.readUint16LE();
-    
-    stream.skip(6);
-    SceneChange::readData(stream);
-
-    uint16 numVideoDescs = stream.readUint16LE();
-    for (uint i = 0; i < numVideoDescs; ++i) {
-        videoDescs.push_back(SecondaryVideoDesc());
-        videoDescs[i].readData(stream);
-    }
-
-    return 0xD4 + numVideoDescs * 0x42; // TODO
-}
-
-void PlaySecondaryMovie::execute(NancyEngine *engine) {
-    ZRenderStruct &zr = engine->graphics->getZRenderStruct("SEC MOVIE");
-
-    switch (state) {
-        case kBegin:
-            engine->graphics->loadSecondaryMovie(videoName);
-            if (soundName != "NO SOUND") {
-                engine->sound->loadSound(soundName, soundChannel, 1, soundVolume);
-            }
-            state = kRun;
-            // fall through
-        case kRun: {
-            zr.isActive = false;
-            engine->graphics->getZRenderStruct("CUR IMAGE CURSOR").isActive = false;
-            
-            int activeFrame = -1;
-
-            for (uint i = 0; i < videoDescs.size(); ++i) {
-                if (videoDescs[i].frameID == engine->playState.currentViewFrame) {
-                    activeFrame = i;
-                }
-            }
-
-            if (activeFrame != -1) {
-                // Activate the ZRenderStruct
-                zr.sourceRect = videoDescs[activeFrame].srcRect;
-                zr.destRect = videoDescs[activeFrame].destRect;
-                zr.destRect.left += engine->graphics->viewportDesc.destination.left;
-                zr.destRect.top += engine->graphics->viewportDesc.destination.top;
-                zr.destRect.top -= engine->playState.verticalScroll;
-                zr.destRect.right += engine->graphics->viewportDesc.destination.left;
-                zr.destRect.bottom += engine->graphics->viewportDesc.destination.top;
-                zr.destRect.bottom -= engine->playState.verticalScroll;
-                zr.isActive = true;
-
-                // Start sound if any
-                if (soundName != "NO SOUND") {
-                    engine->sound->pauseSound(soundChannel, false);
-                }
-            }
-
-            uint16 frame = 0;
-            if (!engine->graphics->playSecondaryMovie(frame)) {
-                // No new frame, check for flags
-                for (uint i = 0; i < 16; ++i) {
-                    if (frameFlags[i].frameID == frame) {
-                        engine->playState.eventFlags[frameFlags[i].flagDesc.label] = frameFlags[i].flagDesc.flag;
-                    }
-                }
-            }
-
-            break;
-        }
-        case kActionTrigger:
-            triggerFlags.execute(engine);
-            SceneChange::execute(engine);
-            break;
-    }
-}
-
 uint16 PlayStaticBitmapAnimation::readData(Common::SeekableReadStream &stream) {
     // TODO
     uint16 bytesRead = stream.pos();
@@ -381,134 +178,6 @@ uint16 PlayStaticBitmapAnimation::readData(Common::SeekableReadStream &stream) {
     return bytesRead;
 }
 
-uint16 PlayIntStaticBitmapAnimation::readData(Common::SeekableReadStream &stream) {
-    uint beginOffset = stream.pos();
-    char name[10];
-    stream.read(name, 10);
-    imageName = Common::String(name);
-
-    stream.skip(0xA);
-    firstFrame = stream.readUint16LE();
-    stream.skip(2);
-    lastFrame = stream.readUint16LE();
-    frameTime = Common::Rational(1000, stream.readUint16LE()).toInt();
-    stream.skip(2);
-    soundFlagDesc.label = stream.readSint16LE();
-    soundFlagDesc.flag = (PlayState::Flag)stream.readUint16LE();
-
-    SceneChange::readData(stream);
-
-    triggerFlags.readData(stream);
-
-    stream.read(name, 10);
-    soundName = Common::String(name);
-    channelID = stream.readUint16LE();
-
-    stream.seek(beginOffset + 0x74, SEEK_SET);
-    uint numFrames = stream.readUint16LE();
-
-    for (uint i = firstFrame; i <= lastFrame; ++i) {
-        frameRects.push_back(Common::Rect());
-        readRect(stream, frameRects[i]);
-    }
-
-    for (uint i = 0; i < numFrames; ++i) {
-        bitmaps.push_back(BitmapDesc());
-        BitmapDesc &rects = bitmaps[i];
-        rects.frameID = stream.readUint16LE();
-        readRect(stream, rects.src);
-        readRect(stream, rects.dest);
-    }
-
-    return 0x76 + numFrames * 0x22 + (lastFrame - firstFrame + 1) * 16;
-}
-
-void PlayIntStaticBitmapAnimation::execute(NancyEngine *engine) {
-    // TODO handle sound, event flags
-    ZRenderStruct &zr = engine->graphics->getZRenderStruct("STATIC BITMAP ANIMATION");
-    uint32 currentFrameTime = engine->getTotalPlayTime();
-    switch (state) {
-        case kBegin:
-
-            // find the correct source and destination for the current viewport frame
-            lastViewFrame = engine->playState.currentViewFrame;
-            for (uint i = 0; i < bitmaps.size(); ++i) {
-                if (lastViewFrame == bitmaps[i].frameID) {
-                    currentViewFrameID = i;
-                    break;
-                }
-            }
-
-            currentFrame = firstFrame;
-            nextFrameTime = currentFrameTime + frameTime;
-
-            // Load the image directly into the generic surface
-            // instead of inside the record; this also means we skip
-            // using the source rect in srcDestRect.
-            // This _may_ lead to problems (but probably won't)
-            zr.sourceSurface->free();
-            engine->_res->loadImage("ciftree", imageName, *zr.sourceSurface);
-
-            // Set up the ZRenderStruct and its surface
-            zr.sourceRect = frameRects[currentFrame - firstFrame];
-            if (currentViewFrameID != -1) {
-                zr.isActive = true;
-                zr.destRect = bitmaps[currentViewFrameID].dest;
-                zr.destRect.left += engine->graphics->viewportDesc.destination.left;
-                zr.destRect.top += engine->graphics->viewportDesc.destination.top;
-                zr.destRect.top -= engine->playState.verticalScroll;
-                zr.destRect.right += engine->graphics->viewportDesc.destination.left;
-                zr.destRect.bottom += engine->graphics->viewportDesc.destination.top;
-                zr.destRect.bottom -= engine->playState.verticalScroll;
-            }
-
-            if (soundName != "NO SOUND") {
-                warning("PlayIntStaticBitmapAnimation has a sound, please implement it!");
-            }
-            state = kRun;
-            // fall through
-        case kRun:
-            // Check if we've moved the viewport
-            if (lastViewFrame != engine->playState.currentViewFrame) {
-                lastViewFrame = engine->playState.currentViewFrame;
-                currentViewFrameID = -1;
-                for (uint i = 0; i < bitmaps.size(); ++i) {
-                    if (lastViewFrame == bitmaps[i].frameID) {
-                        currentViewFrameID = i;
-                        zr.isActive = true;
-                        zr.destRect = bitmaps[currentViewFrameID].dest;
-                        zr.destRect.left += engine->graphics->viewportDesc.destination.left;
-                        zr.destRect.top += engine->graphics->viewportDesc.destination.top;
-                        zr.destRect.top -= engine->playState.verticalScroll;
-                        zr.destRect.right += engine->graphics->viewportDesc.destination.left;
-                        zr.destRect.bottom += engine->graphics->viewportDesc.destination.top;
-                        zr.destRect.bottom -= engine->playState.verticalScroll;
-                        break;
-                    }
-                }
-            }
-
-            if (currentViewFrameID == -1) {
-                break;
-            }
-
-            // Check the timer to see if we need to draw the next animation frame
-            if (nextFrameTime <= currentFrameTime) {
-                nextFrameTime = currentFrameTime + frameTime;
-
-                currentFrame = ++currentFrame > lastFrame ? firstFrame : currentFrame;
-
-                zr.sourceRect = frameRects[currentFrame - firstFrame];
-            }
-            break;
-        case kActionTrigger:
-            triggerFlags.execute(engine);
-
-            SceneChange::execute(engine);
-            break;
-    }
-}
-
 uint16 MapCall::readData(Common::SeekableReadStream &stream) {
     stream.skip(1);
     return 1;
@@ -516,7 +185,7 @@ uint16 MapCall::readData(Common::SeekableReadStream &stream) {
 
 void MapCall::execute(NancyEngine *engine) {
     execType = 2;
-    engine->sceneManager->stateChangeRequests |= SceneManager::kMap;
+    engine->scene->requestStateChange(NancyEngine::kMap);
     // call base, depends on execType
     state = kBegin;
 }
@@ -533,7 +202,7 @@ void MapCallHot1Fr::execute(NancyEngine *engine) {
             state = kRun;
             // fall through
         case kRun:
-            if (engine->playState.currentViewFrame == hotspotDesc.frameID) {
+            if (hotspotDesc.frameID == engine->scene->getSceneInfo().frameID) {
                 hasHotspot = true;
             }
             break;
@@ -561,7 +230,7 @@ void MapCallHotMultiframe::execute(NancyEngine *engine) {
         case kRun:
             hasHotspot = false;
             for (uint i = 0; i < hotspots.size(); ++i) {
-                if (hotspots[i].frameID == engine->playState.currentViewFrame) {
+                if (hotspots[i].frameID == engine->scene->getSceneInfo().frameID) {
                     hasHotspot = true;
                     hotspot = hotspots[i].coords;
                 }
@@ -631,8 +300,7 @@ uint16 ResetAndStartTimer::readData(Common::SeekableReadStream &stream) {
 }
 
 void ResetAndStartTimer::execute(NancyEngine *engine) {
-    engine->playState.timerIsActive = true;
-    engine->playState.timerTime = 0;
+    engine->scene->resetAndStartTimer();
     isDone = true;
 }
 
@@ -642,8 +310,7 @@ uint16 StopTimer::readData(Common::SeekableReadStream &stream) {
 }
 
 void StopTimer::execute(NancyEngine *engine) {
-    engine->playState.timerIsActive = false;
-    engine->playState.timerTime = 0;
+    engine->scene->stopTimer();
     isDone = true;
 }
 
@@ -679,7 +346,7 @@ void EventFlagsMultiHS::execute(NancyEngine *engine) {
         case kRun:
             hasHotspot = false;
             for (uint i = 0; i < hotspots.size(); ++i) {
-                if (hotspots[i].frameID == engine->playState.currentViewFrame) {
+                if (hotspots[i].frameID == engine->scene->getSceneInfo().frameID) {
                     hasHotspot = true;
                     hotspot = hotspots[i].coords;
                 }
@@ -723,11 +390,12 @@ uint16 LeverPuzzle::readData(Common::SeekableReadStream &stream) {
 }
 
 uint16 Telephone::readData(Common::SeekableReadStream &stream) {
-    rawData = new byte[0x2016];
+    byte *rawData = new byte[0x2016];
     stream.read(rawData, 0x48C);
 
     int32 sizeNext = (int16)(rawData[0x48A]) * 235;
     stream.read(rawData + 0x48C, sizeNext);
+    delete[] rawData;
     return sizeNext + 0x48C;
 }
 
@@ -749,16 +417,14 @@ uint16 RemoveInventoryNoHS::readData(Common::SeekableReadStream &stream) {
 
 uint16 DifficultyLevel::readData(Common::SeekableReadStream &stream) {
     difficulty = stream.readUint16LE();
-    flagLabel = stream.readSint16LE();
-    flagCondition = stream.readUint16LE();
+    flag.label = stream.readSint16LE();
+    flag.flag = (NancyFlag)stream.readUint16LE();
     return 6;
 }
 
 void DifficultyLevel::execute(NancyEngine *engine) {
-    engine->playState.difficulty = difficulty;
-    if (flagLabel != -1) {
-        engine->playState.eventFlags[flagLabel] = (PlayState::Flag)flagCondition;
-    }
+    engine->scene->setDifficulty(difficulty);
+    engine->scene->setEventFlag(flag.label, flag.flag);
     isDone = true;
 }
 
@@ -766,6 +432,17 @@ uint16 RotatingLockPuzzle::readData(Common::SeekableReadStream &stream) {
     return readRaw(stream, 0x2A4); // TODO
 }
 
+void ShowInventoryItem::init() {
+    Graphics::Surface srcSurf;
+    _engine->_res->loadImage("ciftree", imageName, srcSurf);
+    _fullSurface.create(srcSurf.w, srcSurf.h, srcSurf.format);
+    _fullSurface.blitFrom(srcSurf);
+
+    _drawSurface.create(_fullSurface, bitmaps[0].src);
+
+    RenderObject::init();
+}
+
 uint16 ShowInventoryItem::readData(Common::SeekableReadStream &stream) {
     objectID = stream.readUint16LE();
     char name[10];
@@ -783,39 +460,45 @@ uint16 ShowInventoryItem::readData(Common::SeekableReadStream &stream) {
 }
 
 void ShowInventoryItem::execute(NancyEngine *engine) {
-    ZRenderStruct &zr = engine->graphics->getZRenderStruct("INV BITMAP");
     switch (state) {
         case kBegin:
-            zr.sourceSurface->free();
-            engine->_res->loadImage("ciftree", imageName, *zr.sourceSurface);
+            init();
+            registerGraphics();
             state = kRun;
             // fall through
-        case kRun:
-            zr.isActive = false;
-            hasHotspot = false;
+        case kRun: {
+            int newFrame = -1;
 
             for (uint i = 0; i < bitmaps.size(); ++i) {
-                if (bitmaps[i].frameID == engine->playState.currentViewFrame) {
+                if (bitmaps[i].frameID == engine->scene->getSceneInfo().frameID) {
+                    newFrame = i;
+                    break;
+                }
+            }
+
+            if (newFrame != drawnFrameID) {
+                drawnFrameID = newFrame;
+                if (newFrame != -1) {
                     hasHotspot = true;
-                    hotspot = bitmaps[i].dest;
-                    
-                    zr.isActive = true;
-                    zr.sourceRect = bitmaps[i].src;
-                    zr.destRect = bitmaps[i].dest;
-                    View &viewport = engine->graphics->viewportDesc;
-                    zr.destRect.left += viewport.destination.left;
-                    zr.destRect.right += viewport.destination.left;
-                    zr.destRect.top += viewport.destination.top - engine->playState.verticalScroll;
-                    zr.destRect.bottom += viewport.destination.top - engine->playState.verticalScroll;
+                    hotspot = bitmaps[newFrame].dest;
+                    _drawSurface.create(_fullSurface, bitmaps[newFrame].src);
+                    _screenPosition = bitmaps[newFrame].dest;
+                    setVisible(true);
+                } else {
+                    hasHotspot = false;
+                    setVisible(false);
                 }
             }
+                   
             break;
+        }
         case kActionTrigger:
             // TODO play sound
-            engine->sceneManager->addObjectToInventory(objectID);
-            zr.isActive = false;
+            engine->scene->addItemToInventory(objectID);
+            setVisible(false);
             hasHotspot = false;
             isDone = true;
+            break;
     }
 }
 
@@ -869,4 +552,5 @@ uint16 HintSystem::readData(Common::SeekableReadStream &stream) {
     return readRaw(stream, 0x23); // TODO
 }
 
-} // End of namespace Nancy
\ No newline at end of file
+}
+} // End of namespace Nancy
diff --git a/engines/nancy/action/recordtypes.h b/engines/nancy/action/recordtypes.h
index 38b8951546..df94bf503a 100644
--- a/engines/nancy/action/recordtypes.h
+++ b/engines/nancy/action/recordtypes.h
@@ -24,15 +24,19 @@
 #define NANCY_ACTION_RECORDTYPES_H
 
 #include "engines/nancy/action/actionrecord.h"
-#include "engines/nancy/playstate.h"
+#include "engines/nancy/commontypes.h"
+#include "engines/nancy/renderobject.h"
 
 #include "common/stream.h"
 #include "common/array.h"
+#include "common/str.h"
 
 namespace Nancy {
 
 class NancyEngine;
 
+namespace Action {
+
 // Describes a hotspot
 struct HotspotDesc {
     uint16 frameID = 0;
@@ -50,20 +54,10 @@ struct BitmapDesc {
     void readData(Common::SeekableReadStream &stream);
 };
 
-// Describes a secondary video/movie's source and destination
-struct SecondaryVideoDesc {
-    int16 frameID;
-    Common::Rect srcRect;
-    Common::Rect destRect;
-    // 2 unknown/empty rects
-
-    void readData(Common::SeekableReadStream &stream);
-};
-
 // Describes a single event flag change or comparison
 struct FlagDesc {
     int16 label;
-    PlayState::Flag flag;
+    NancyFlag flag;
 };
 
 // Describes 10 event flag changes to be executed when an action is triggered
@@ -71,7 +65,7 @@ struct EventFlagsDesc {
     FlagDesc descs[10];
 
     void readData(Common::SeekableReadStream &stream);
-    void execute(NancyEngine *engine);
+    void execute(Nancy::NancyEngine *engine);
 };
 
 // Describes a scene transition
@@ -87,7 +81,7 @@ struct SceneChangeDesc {
 class SceneChange : public ActionRecord {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
-    virtual void execute(NancyEngine *engine) override;
+    virtual void execute(Nancy::NancyEngine *engine) override;
 
     SceneChangeDesc sceneChange;
 };
@@ -95,20 +89,23 @@ public:
 class HotMultiframeSceneChange : public SceneChange {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
-    virtual void execute(NancyEngine *engine) override;
+    virtual void execute(Nancy::NancyEngine *engine) override;
 
     Common::Array<HotspotDesc> hotspots;
 };
 
-// The exact same logic as Hot1FrExitSceneChange
 class Hot1FrSceneChange : public SceneChange {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
-    virtual void execute(NancyEngine *engine) override;
+    virtual void execute(Nancy::NancyEngine *engine) override;
 
     HotspotDesc hotspotDesc;
 };
 
+class Hot1FrExitSceneChange : public Hot1FrSceneChange {
+    virtual CursorManager::CursorType getHoverCursor() const override { return CursorManager::kExitArrow; }
+};
+
 class HotMultiframeMultisceneChange : public ActionRecord {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
@@ -127,66 +124,6 @@ public:
     byte type = 0;
 };
 
-// Base class for PlaySecondaryVideoChan0 and PlaySecondaryVideoChan1
-class PlaySecondaryVideo : public SceneChange {
-public:
-
-    enum HoverState { kNoHover, kHover, kEndHover, kEndHoverDone };
-
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
-    virtual void execute(NancyEngine *engine) override;
-
-    virtual uint channelID() =0;
-
-    Common::String filename;
-    //...
-    uint16 loopFirstFrame = 0; // 0x1E
-    uint16 loopLastFrame = 0; // 0x20
-    uint16 onHoverFirstFrame = 0; // 0x22
-    uint16 onHoverLastFrame = 0; // 0x24
-    uint16 onHoverEndFirstFrame = 0; // 0x26
-    uint16 onHoverEndLastFrame = 0; // 0x28
-    // SceneChange data is at 0x2A
-    // unknown byte
-    Common::Array<SecondaryVideoDesc> videoDescs; // 0x35
-
-    // not present in original data
-    HoverState hoverState = kNoHover;
-};
-
-class PlaySecondaryVideoChan0 : public PlaySecondaryVideo {
-    virtual uint channelID() override { return 0; }
-};
-
-class PlaySecondaryVideoChan1 : public PlaySecondaryVideo {
-    virtual uint channelID() override { return 1; }
-};
-
-class PlaySecondaryMovie : public SceneChange {
-public:
-    struct FlagAtFrame {
-        int16 frameID;
-        FlagDesc flagDesc;
-    };
-
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
-    virtual void execute(NancyEngine *engine) override;
-
-    Common::String videoName; // 0x00
-
-    FlagAtFrame frameFlags[15]; // 0x26
-    EventFlagsDesc triggerFlags; // 0x80
-
-    Common::String soundName; // 0xA8
-    uint16 soundChannel = 0; // 0xB2
-    uint16 soundVolume = 0; // 0xC2
-
-    // SceneChange data at 0xCA
-    Common::Array<SecondaryVideoDesc> videoDescs; // 0xD4
-
-
-};
-
 class PlayStaticBitmapAnimation : public ActionRecord {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
@@ -194,47 +131,18 @@ public:
     byte bitmapData[0xA88];
 };
 
-class PlayIntStaticBitmapAnimation : public SceneChange {
-// TODO this effectively also contains an EventFlags, consider multiple inheritance
-// or maybe splitting EventFlags into a separate struct
-public:
-
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
-    virtual void execute(NancyEngine *engine) override;
-
-    Common::String imageName;
-    uint16 firstFrame;
-    uint16 lastFrame;
-    FlagDesc soundFlagDesc;
-    EventFlagsDesc triggerFlags;
-    Time frameTime;
-
-    // Todo
-    Common::String soundName;
-    uint16 channelID;
-
-    // Describes a single frame in this animation
-    Common::Array<Common::Rect> frameRects;
-    // Describes how the animation will be displayed on a single
-    // frame of the viewport
-    Common::Array<BitmapDesc> bitmaps;
-
-    uint16 currentFrame = 0;
-    uint16 lastViewFrame = 0;
-    int16 currentViewFrameID = -1;
-    Time nextFrameTime;
-};
-
 class MapCall : public ActionRecord {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
-    virtual void execute(NancyEngine *engine) override;
+    virtual void execute(Nancy::NancyEngine *engine) override;
+
+    virtual CursorManager::CursorType getHoverCursor() const override { return CursorManager::kExitArrow; }
 };
 
 class MapCallHot1Fr : public MapCall {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
-    virtual void execute(NancyEngine *engine) override;
+    virtual void execute(Nancy::NancyEngine *engine) override;
 
     HotspotDesc hotspotDesc;
 };
@@ -242,7 +150,7 @@ public:
 class MapCallHotMultiframe : public MapCall {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
-    virtual void execute(NancyEngine *engine) override;
+    virtual void execute(Nancy::NancyEngine *engine) override;
 
     Common::Array<HotspotDesc> hotspots;
 };
@@ -310,19 +218,19 @@ public:
 class ResetAndStartTimer : public ActionRecord {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
-    virtual void execute(NancyEngine *engine) override;
+    virtual void execute(Nancy::NancyEngine *engine) override;
 };
 
 class StopTimer : public ActionRecord {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
-    virtual void execute(NancyEngine *engine) override;
+    virtual void execute(Nancy::NancyEngine *engine) override;
 };
 
 class EventFlags : public ActionRecord {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
-    virtual void execute(NancyEngine *engine) override;
+    virtual void execute(Nancy::NancyEngine *engine) override;
 
     EventFlagsDesc flags;
 };
@@ -330,7 +238,7 @@ public:
 class EventFlagsMultiHS : public EventFlags {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
-    virtual void execute(NancyEngine *engine) override;
+    virtual void execute(Nancy::NancyEngine *engine) override;
 
     Common::Array<HotspotDesc> hotspots;
 };
@@ -401,11 +309,10 @@ public:
 class DifficultyLevel : public ActionRecord {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
-    virtual void execute(NancyEngine *engine) override;
+    virtual void execute(Nancy::NancyEngine *engine) override;
 
     uint16 difficulty = 0;
-    int16 flagLabel = 0;
-    uint16 flagCondition = 0;
+    FlagDesc flag;
 };
 
 class RotatingLockPuzzle : public ActionRecord {
@@ -413,20 +320,33 @@ public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
 };
 
-class ShowInventoryItem : public ActionRecord {
+class ShowInventoryItem : public ActionRecord, public RenderObject {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
-    virtual void execute(NancyEngine *engine) override;
+    virtual void execute(Nancy::NancyEngine *engine) override;
+
+    ShowInventoryItem(RenderObject &redrawFrom) : RenderObject(redrawFrom) {}
+    virtual ~ShowInventoryItem() { _fullSurface.free(); }
+
+    virtual void init()override;
  
     uint16 objectID = 0;
     Common::String imageName;
     Common::Array<BitmapDesc> bitmaps;
+
+    int16 drawnFrameID = -1;
+    Graphics::ManagedSurface _fullSurface;
+    
+protected:
+    virtual uint16 getZOrder() const override { return 9; }
+    virtual BlitType getBlitType() const override { return kNoTrans; }
+    virtual bool isViewportRelative() const override { return true; }
 };
 
 class PlayDigiSoundAndDie : public SceneChange {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
-    virtual void execute(NancyEngine *engine) override;
+    virtual void execute(Nancy::NancyEngine *engine) override;
     // TODO subclass into Play and Stop (?)
 
     Common::String filename;
@@ -452,6 +372,7 @@ public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
 };
 
+} // End of namespace Action
 } // End of namespace Nancy
 
-#endif // NANCY_ACTION_RECORDTYPES_H
\ No newline at end of file
+#endif // NANCY_ACTION_RECORDTYPES_H
diff --git a/engines/nancy/action/responses.cpp b/engines/nancy/action/responses.cpp
index 1a8b7e179b..3ac113a918 100644
--- a/engines/nancy/action/responses.cpp
+++ b/engines/nancy/action/responses.cpp
@@ -24,6 +24,14 @@
 #include "common/array.h"
 
 namespace Nancy {
+namespace Action {
+
+// For whatever reason the games don't use the conditional dialogue code
+// inside primary video, and instead have a bunch of functions that manually
+// check hardcoded event flag IDs and extract strings embedded inside the exe.
+// These tables contain the extracted data and offsets for the string data, but if
+// there happen to be many versions of the same game it might be a better idea to just
+// directly copy the dialogue strings in here.
 
 struct ConditionalResponseDesc {
     byte characterID; // 0: Daryl, 1: Connie, 2: Hal, 3: Hulk
@@ -40,7 +48,7 @@ struct GoodbyeDesc {
 
 static const uint nancy1ResponseBaseFileOffset = 0xB1FE0; // TODO there could be more than one version of the exe
 
-#define EMPTY_DESC {-1, PlayState::kFalse }
+#define EMPTY_DESC {-1, kFalse }
 
 static const GoodbyeDesc nancy1Goodbyes[] = {
     // Daryl
@@ -79,8 +87,8 @@ static const ConditionalResponseDesc nancy1ConditionalResponses[] = {
         0x840,
         0x7C,
         {
-            { 0x1D, PlayState::kTrue },
-            { 0x39, PlayState::kFalse },
+            { 0x1D, kTrue },
+            { 0x39, kFalse },
             EMPTY_DESC
         }
     },
@@ -90,8 +98,8 @@ static const ConditionalResponseDesc nancy1ConditionalResponses[] = {
         0x804,
         0x7F,
         {
-            { 0x13, PlayState::kTrue },
-            { 0x37, PlayState::kFalse },
+            { 0x13, kTrue },
+            { 0x37, kFalse },
             EMPTY_DESC
         }
     },
@@ -101,8 +109,8 @@ static const ConditionalResponseDesc nancy1ConditionalResponses[] = {
         0x7BC,
         0x81,
         {
-            { 0xB, PlayState::kTrue },
-            { 0x38, PlayState::kFalse },
+            { 0xB, kTrue },
+            { 0x38, kFalse },
             EMPTY_DESC
         }
     },
@@ -112,9 +120,9 @@ static const ConditionalResponseDesc nancy1ConditionalResponses[] = {
         0x750,
         0x83,
         {
-            { 0, PlayState::kTrue },
-            { 1, PlayState::kFalse },
-            { 0x6B, PlayState::kFalse },
+            { 0, kTrue },
+            { 1, kFalse },
+            { 0x6B, kFalse },
             EMPTY_DESC
         }
     },
@@ -124,11 +132,11 @@ static const ConditionalResponseDesc nancy1ConditionalResponses[] = {
         0x6F4,
         0x84,
         {
-            { 0x64, PlayState::kTrue },
-            { 0x1E, PlayState::kFalse },
-            { 0x14, PlayState::kFalse },
-            { 0xC, PlayState::kFalse },
-            { 0x6C, PlayState::kFalse },
+            { 0x64, kTrue },
+            { 0x1E, kFalse },
+            { 0x14, kFalse },
+            { 0xC, kFalse },
+            { 0x6C, kFalse },
             EMPTY_DESC
         }
     },
@@ -138,13 +146,13 @@ static const ConditionalResponseDesc nancy1ConditionalResponses[] = {
         0x5EC,
         0x86,
         {
-            { 0x6D, PlayState::kFalse },
-            { 0x6, PlayState::kTrue },
-            { 0x8, PlayState::kTrue },
-            { 0x5E, PlayState::kTrue },
-            { 0x17, PlayState::kTrue },
-            { 0x24, PlayState::kTrue },
-            { 0x9, PlayState::kTrue }
+            { 0x6D, kFalse },
+            { 0x6, kTrue },
+            { 0x8, kTrue },
+            { 0x5E, kTrue },
+            { 0x17, kTrue },
+            { 0x24, kTrue },
+            { 0x9, kTrue }
         }
     },
 
@@ -153,11 +161,11 @@ static const ConditionalResponseDesc nancy1ConditionalResponses[] = {
         0x554,
         0x8B,
         {
-            { 0x6E, PlayState::kFalse },
-            { 0x24, PlayState::kTrue },
-            { 0x9, PlayState::kTrue },
-            { 0x5E, PlayState::kFalse },
-            { 0x8, PlayState::kFalse },
+            { 0x6E, kFalse },
+            { 0x24, kTrue },
+            { 0x9, kTrue },
+            { 0x5E, kFalse },
+            { 0x8, kFalse },
             EMPTY_DESC
         }
     },
@@ -167,11 +175,11 @@ static const ConditionalResponseDesc nancy1ConditionalResponses[] = {
         0x4F0,
         0x8D,
         {
-            { 0x6F, PlayState::kFalse },
-            { 0x5E, PlayState::kTrue },
-            { 0x24, PlayState::kTrue },
-            { 0x9, PlayState::kTrue },
-            { 0x8, PlayState::kFalse },
+            { 0x6F, kFalse },
+            { 0x5E, kTrue },
+            { 0x24, kTrue },
+            { 0x9, kTrue },
+            { 0x8, kFalse },
             EMPTY_DESC
         }
     },
@@ -181,12 +189,12 @@ static const ConditionalResponseDesc nancy1ConditionalResponses[] = {
         0x458,
         0x8F,
         {
-            { 0x70, PlayState::kFalse },
-            { 0x24, PlayState::kTrue },
-            { 0x9, PlayState::kTrue },
-            { 0x6, PlayState::kTrue },
-            { 0x8, PlayState::kTrue },
-            { 0x5E, PlayState::kFalse },
+            { 0x70, kFalse },
+            { 0x24, kTrue },
+            { 0x9, kTrue },
+            { 0x6, kTrue },
+            { 0x8, kTrue },
+            { 0x5E, kFalse },
             EMPTY_DESC
         }
     },
@@ -196,10 +204,10 @@ static const ConditionalResponseDesc nancy1ConditionalResponses[] = {
         0x3BC,
         0x90,
         {
-            { 0x71, PlayState::kFalse },
-            { 0x5E, PlayState::kTrue },
-            { 0x24, PlayState::kFalse },
-            { 0x8, PlayState::kFalse },
+            { 0x71, kFalse },
+            { 0x5E, kTrue },
+            { 0x24, kFalse },
+            { 0x8, kFalse },
             EMPTY_DESC
         }
     },
@@ -209,11 +217,11 @@ static const ConditionalResponseDesc nancy1ConditionalResponses[] = {
         0x320,
         0x91,
         {
-            { 0x72, PlayState::kFalse },
-            { 0x5E, PlayState::kTrue },
-            { 0x8, PlayState::kTrue },
-            { 0x6, PlayState::kTrue },
-            { 0x24, PlayState::kFalse },
+            { 0x72, kFalse },
+            { 0x5E, kTrue },
+            { 0x8, kTrue },
+            { 0x6, kTrue },
+            { 0x24, kFalse },
             EMPTY_DESC
         }
     },
@@ -223,11 +231,11 @@ static const ConditionalResponseDesc nancy1ConditionalResponses[] = {
         0x2AC,
         0x92,
         {
-            { 0x73, PlayState::kFalse },
-            { 0x8, PlayState::kTrue },
-            { 0x6, PlayState::kTrue },
-            { 0x5E, PlayState::kFalse },
-            { 0x24, PlayState::kFalse },
+            { 0x73, kFalse },
+            { 0x8, kTrue },
+            { 0x6, kTrue },
+            { 0x5E, kFalse },
+            { 0x24, kFalse },
             EMPTY_DESC
         }
     },
@@ -237,13 +245,13 @@ static const ConditionalResponseDesc nancy1ConditionalResponses[] = {
         0x1F0,
         0x96,
         {
-            { 0x74, PlayState::kFalse },
-            { 0x1D, PlayState::kTrue },
-            { 0x13, PlayState::kTrue },
-            { 0xB, PlayState::kTrue },
-            { 0x5E, PlayState::kFalse },
-            { 0x24, PlayState::kFalse },
-            { 0x8, PlayState::kFalse }
+            { 0x74, kFalse },
+            { 0x1D, kTrue },
+            { 0x13, kTrue },
+            { 0xB, kTrue },
+            { 0x5E, kFalse },
+            { 0x24, kFalse },
+            { 0x8, kFalse }
         }
     },
 
@@ -252,8 +260,8 @@ static const ConditionalResponseDesc nancy1ConditionalResponses[] = {
         0x190,
         0x97,
         {
-            { 0x27, PlayState::kFalse },
-            { 0x5, PlayState::kTrue },
+            { 0x27, kFalse },
+            { 0x5, kTrue },
             EMPTY_DESC
         }
     },
@@ -263,8 +271,8 @@ static const ConditionalResponseDesc nancy1ConditionalResponses[] = {
         0xF0,
         0x9C,
         {
-            { 0x28, PlayState::kTrue },
-            { 0x75, PlayState::kFalse },
+            { 0x28, kTrue },
+            { 0x75, kFalse },
             EMPTY_DESC
         }
     },
@@ -274,9 +282,9 @@ static const ConditionalResponseDesc nancy1ConditionalResponses[] = {
         0x94,
         0x93,
         {
-            { 0xC, PlayState::kFalse },
-            { 0x6, PlayState::kTrue },
-            { 0x76, PlayState::kFalse },
+            { 0xC, kFalse },
+            { 0x6, kTrue },
+            { 0x76, kFalse },
             EMPTY_DESC
         }
     },
@@ -286,9 +294,9 @@ static const ConditionalResponseDesc nancy1ConditionalResponses[] = {
         0x58,
         0x94,
         {
-            { 0x14, PlayState::kFalse },
-            { 0x4, PlayState::kTrue },
-            { 0x77, PlayState::kFalse },
+            { 0x14, kFalse },
+            { 0x4, kTrue },
+            { 0x77, kFalse },
             EMPTY_DESC
         }
     },
@@ -298,9 +306,9 @@ static const ConditionalResponseDesc nancy1ConditionalResponses[] = {
         0,
         0x95,
         {
-            { 0x1E, PlayState::kFalse },
-            { 0x63, PlayState::kTrue },
-            { 0x78, PlayState::kFalse },
+            { 0x1E, kFalse },
+            { 0x63, kTrue },
+            { 0x78, kFalse },
             EMPTY_DESC
         }
     },
@@ -311,8 +319,8 @@ static const ConditionalResponseDesc nancy1ConditionalResponses[] = {
         0xBE4,
         0xE9,
         {
-            { 0x1D, PlayState::kTrue },
-            { 0x18, PlayState::kFalse },
+            { 0x1D, kTrue },
+            { 0x18, kFalse },
             EMPTY_DESC
         }
     },
@@ -322,8 +330,8 @@ static const ConditionalResponseDesc nancy1ConditionalResponses[] = {
         0xB8C,
         0xEA,
         {
-            { 0x1F, PlayState::kTrue },
-            { 0x19, PlayState::kFalse },
+            { 0x1F, kTrue },
+            { 0x19, kFalse },
             EMPTY_DESC
         }
     },
@@ -333,8 +341,8 @@ static const ConditionalResponseDesc nancy1ConditionalResponses[] = {
         0xB54,
         0xEB,
         {
-            { 0xB, PlayState::kTrue },
-            { 0x1A, PlayState::kFalse },
+            { 0xB, kTrue },
+            { 0x1A, kFalse },
             EMPTY_DESC
         }
     },
@@ -344,8 +352,8 @@ static const ConditionalResponseDesc nancy1ConditionalResponses[] = {
         0xB14,
         0xEC,
         {
-            { 0x26, PlayState::kTrue },
-            { 0x1C, PlayState::kFalse },
+            { 0x26, kTrue },
+            { 0x1C, kFalse },
             EMPTY_DESC
         }
     },
@@ -355,9 +363,9 @@ static const ConditionalResponseDesc nancy1ConditionalResponses[] = {
         0xABC,
         0xED,
         {
-            { 0, PlayState::kTrue },
-            { 1, PlayState::kFalse },
-            { 0x79, PlayState::kFalse },
+            { 0, kTrue },
+            { 1, kFalse },
+            { 0x79, kFalse },
             EMPTY_DESC
         }
     },
@@ -367,9 +375,9 @@ static const ConditionalResponseDesc nancy1ConditionalResponses[] = {
         0xA00,
         0xEE,
         {
-            { 2, PlayState::kTrue },
-            { 3, PlayState::kTrue },
-            { 0x17, PlayState::kFalse },
+            { 2, kTrue },
+            { 3, kTrue },
+            { 0x17, kFalse },
             EMPTY_DESC
         }
     },
@@ -379,8 +387,8 @@ static const ConditionalResponseDesc nancy1ConditionalResponses[] = {
         0x6F4,
         0xEF,
         {
-            { 0x64, PlayState::kTrue },
-            { 0x16, PlayState::kFalse },
+            { 0x64, kTrue },
+            { 0x16, kFalse },
             EMPTY_DESC
         }
     },
@@ -390,8 +398,8 @@ static const ConditionalResponseDesc nancy1ConditionalResponses[] = {
         0x968,
         0xF0,
         {
-            { 0x5, PlayState::kTrue },
-            { 0x14, PlayState::kFalse },
+            { 0x5, kTrue },
+            { 0x14, kFalse },
             EMPTY_DESC
         }
     },
@@ -401,7 +409,7 @@ static const ConditionalResponseDesc nancy1ConditionalResponses[] = {
         0x8C8,
         0xF5,
         {
-            { 0x28, PlayState::kTrue },
+            { 0x28, kTrue },
             EMPTY_DESC
         }
     },
@@ -411,8 +419,8 @@ static const ConditionalResponseDesc nancy1ConditionalResponses[] = {
         0x884,
         0xE7,
         {
-            { 0xD, PlayState::kTrue },
-            { 0x5E, PlayState::kFalse },
+            { 0xD, kTrue },
+            { 0x5E, kFalse },
             EMPTY_DESC
         }
     },
@@ -423,8 +431,8 @@ static const ConditionalResponseDesc nancy1ConditionalResponses[] = {
         0xED0,
         0x1B3,
         {
-            { 0x1D, PlayState::kTrue },
-            { 0x11, PlayState::kFalse },
+            { 0x1D, kTrue },
+            { 0x11, kFalse },
             EMPTY_DESC
         }
     },
@@ -434,8 +442,8 @@ static const ConditionalResponseDesc nancy1ConditionalResponses[] = {
         0x804,
         0x1B5,
         {
-            { 0x13, PlayState::kTrue },
-            { 0xE, PlayState::kFalse },
+            { 0x13, kTrue },
+            { 0xE, kFalse },
             EMPTY_DESC
         }
     },
@@ -445,8 +453,8 @@ static const ConditionalResponseDesc nancy1ConditionalResponses[] = {
         0xE74,
         0x1B6,
         {
-            { 0x1B, PlayState::kTrue },
-            { 0xF, PlayState::kFalse },
+            { 0x1B, kTrue },
+            { 0xF, kFalse },
             EMPTY_DESC
         }
     },
@@ -456,8 +464,8 @@ static const ConditionalResponseDesc nancy1ConditionalResponses[] = {
         0xE2C,
         0x1B7,
         {
-            { 0x26, PlayState::kTrue },
-            { 0x10, PlayState::kFalse },
+            { 0x26, kTrue },
+            { 0x10, kFalse },
             EMPTY_DESC
         }
     },
@@ -467,9 +475,9 @@ static const ConditionalResponseDesc nancy1ConditionalResponses[] = {
         0xDD4,
         0x1B9,
         {
-            { 0, PlayState::kTrue },
-            { 1, PlayState::kFalse },
-            { 0x68, PlayState::kFalse },
+            { 0, kTrue },
+            { 1, kFalse },
+            { 0x68, kFalse },
             EMPTY_DESC
         }
     },
@@ -479,10 +487,10 @@ static const ConditionalResponseDesc nancy1ConditionalResponses[] = {
         0xD48,
         0x1BA,
         {
-            { 0, PlayState::kTrue },
-            { 1, PlayState::kFalse },
-            { 0x20, PlayState::kTrue },
-            { 0x69, PlayState::kFalse },
+            { 0, kTrue },
+            { 1, kFalse },
+            { 0x20, kTrue },
+            { 0x69, kFalse },
             EMPTY_DESC
         }
     },
@@ -492,9 +500,9 @@ static const ConditionalResponseDesc nancy1ConditionalResponses[] = {
         0x6F4,
         0x1BB,
         {
-            { 0x6A, PlayState::kFalse },
-            { 0x64, PlayState::kTrue },
-            { 0x5, PlayState::kFalse },
+            { 0x6A, kFalse },
+            { 0x64, kTrue },
+            { 0x5, kFalse },
             EMPTY_DESC
         }
     },
@@ -504,9 +512,9 @@ static const ConditionalResponseDesc nancy1ConditionalResponses[] = {
         0xCC8,
         0x1BC,
         {
-            { 0x8, PlayState::kTrue },
-            { 0x6, PlayState::kTrue },
-            { 0xC, PlayState::kFalse },
+            { 0x8, kTrue },
+            { 0x6, kTrue },
+            { 0xC, kFalse },
             EMPTY_DESC
         }
     },
@@ -516,7 +524,7 @@ static const ConditionalResponseDesc nancy1ConditionalResponses[] = {
         0xC2C,
         0x1BE,
         {
-            { 0x28, PlayState::kTrue },
+            { 0x28, kTrue },
             EMPTY_DESC
         }
     },
@@ -527,8 +535,8 @@ static const ConditionalResponseDesc nancy1ConditionalResponses[] = {
         0x1164,
         0x14D,
         {
-            { 0x13, PlayState::kTrue },
-            { 0x3A, PlayState::kFalse },
+            { 0x13, kTrue },
+            { 0x3A, kFalse },
             EMPTY_DESC
         }
     },
@@ -538,8 +546,8 @@ static const ConditionalResponseDesc nancy1ConditionalResponses[] = {
         0xB54,
         0x150,
         {
-            { 0xB, PlayState::kTrue },
-            { 0x25, PlayState::kFalse },
+            { 0xB, kTrue },
+            { 0x25, kFalse },
             EMPTY_DESC
         }
     },
@@ -549,8 +557,8 @@ static const ConditionalResponseDesc nancy1ConditionalResponses[] = {
         0x10D8,
         0x153,
         {
-            { 0x12, PlayState::kTrue },
-            { 0x21, PlayState::kFalse },
+            { 0x12, kTrue },
+            { 0x21, kFalse },
             EMPTY_DESC
         }
     },
@@ -561,8 +569,8 @@ static const ConditionalResponseDesc nancy1ConditionalResponses[] = {
         0xE2C,
         0x154,
         {
-            { 0x26, PlayState::kTrue },
-            { 0x22, PlayState::kFalse },
+            { 0x26, kTrue },
+            { 0x22, kFalse },
             EMPTY_DESC
         }
     },
@@ -572,9 +580,9 @@ static const ConditionalResponseDesc nancy1ConditionalResponses[] = {
         0x108C,
         0x155,
         {
-            { 0, PlayState::kTrue },
-            { 1, PlayState::kFalse },
-            { 0x66, PlayState::kFalse },
+            { 0, kTrue },
+            { 1, kFalse },
+            { 0x66, kFalse },
             EMPTY_DESC
         }
     },
@@ -584,8 +592,8 @@ static const ConditionalResponseDesc nancy1ConditionalResponses[] = {
         0x6F4,
         0x156,
         {
-            { 0x67, PlayState::kFalse },
-            { 0x64, PlayState::kTrue },
+            { 0x67, kFalse },
+            { 0x64, kTrue },
             EMPTY_DESC
         }
     },
@@ -595,8 +603,8 @@ static const ConditionalResponseDesc nancy1ConditionalResponses[] = {
         0x1028,
         0x157,
         {
-            { 0x63, PlayState::kTrue },
-            { 0x24, PlayState::kFalse },
+            { 0x63, kTrue },
+            { 0x24, kFalse },
             EMPTY_DESC
         }
     },
@@ -606,8 +614,8 @@ static const ConditionalResponseDesc nancy1ConditionalResponses[] = {
         0xFB0,
         0x158,
         {
-            { 0x5, PlayState::kTrue },
-            { 0x1E, PlayState::kFalse },
+            { 0x5, kTrue },
+            { 0x1E, kFalse },
             EMPTY_DESC
         }
     },
@@ -617,10 +625,11 @@ static const ConditionalResponseDesc nancy1ConditionalResponses[] = {
         0xF10,
         0x159,
         {
-            { 0x28, PlayState::kTrue },
+            { 0x28, kTrue },
             EMPTY_DESC
         }
     }
 };
 
-} // End of namespace Nancy
\ No newline at end of file
+}// End of namespace Action
+} // End of namespace Nancy::Action
diff --git a/engines/nancy/action/secondaryvideo.cpp b/engines/nancy/action/secondaryvideo.cpp
new file mode 100644
index 0000000000..4f19f31d62
--- /dev/null
+++ b/engines/nancy/action/secondaryvideo.cpp
@@ -0,0 +1,315 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/nancy/action/secondaryvideo.h"
+
+#include "engines/nancy/state/scene.h"
+
+#include "engines/nancy/ui/viewport.h"
+
+#include "engines/nancy/nancy.h"
+#include "engines/nancy/util.h"
+#include "engines/nancy/audio.h"
+#include "engines/nancy/cursor.h"
+#include "engines/nancy/input.h"
+#include "engines/nancy/graphics.h"
+
+#include "common/system.h"
+
+#include "common/events.h"
+
+namespace Nancy {
+namespace Action {
+
+void SecondaryVideoDesc::readData(Common::SeekableReadStream &stream) {
+    frameID = stream.readUint16LE();
+    readRect(stream, srcRect);
+    readRect(stream, destRect);
+    stream.skip(0x20);
+}
+
+void PlaySecondaryVideo::init() {
+    if(_decoder.isVideoLoaded()) {
+        _decoder.close();
+    }
+
+    _decoder.loadFile(filename + ".avf");
+    // TODO add 12 ms to the frame time, not sure why this happens in the engine yet
+    _drawSurface.create(_decoder.getWidth(), _decoder.getHeight(), GraphicsManager::pixelFormat);
+
+    setVisible(false);
+
+    RenderObject::init();
+}
+
+void PlaySecondaryVideo::updateGraphics() {
+    if (!_decoder.isVideoLoaded()) {
+        return;
+    }
+
+    if (_isPlaying) {
+        switch (hoverState) {
+            case kNoHover:
+                if (!_decoder.isPlaying()) {
+                    _decoder.start();
+                }
+                if (_isHovered) {
+                    _decoder.seekToFrame(onHoverFirstFrame);
+                    
+                    hoverState = kHover;
+                } else {
+                    if (_decoder.getCurFrame() == loopLastFrame) {
+                        // loop back to beginning
+                        _decoder.seekToFrame(loopFirstFrame);
+                    }
+                    break;
+                }
+                // fall through
+            case kHover:
+                if (!_isHovered) {
+                    // Stopped hovering, reverse playback
+                    _decoder.seekToFrame(onHoverEndLastFrame);
+                    _decoder.setRate(-_decoder.getRate());
+                    if (!_decoder.isPlaying()) {
+                        _decoder.start();
+                    }
+                    hoverState = kEndHover;
+                } else {
+                    break;
+                }
+                // fall through
+            case kEndHover:
+                if (_decoder.getCurFrame() == onHoverEndFirstFrame) {
+                    // reversed playback has ended, go back to no hover state
+                    _decoder.seekToFrame(loopFirstFrame);
+                    _decoder.setRate(-_decoder.getRate());
+                    hoverState = kNoHover;
+                }
+                break;
+        }
+
+        if (_decoder.needsUpdate() && !_screenPosition.isEmpty()) {
+            for (uint i = 0; i < videoDescs.size(); ++i) {
+                if ((uint16)videoDescs[i].frameID == _currentViewportFrame) {
+                    _drawSurface.blitFrom(*_decoder.decodeNextFrame(), videoDescs[i].srcRect, Common::Point());
+                    break;
+                }
+            }
+            _needsRedraw = true;
+        }
+    } else {
+        _decoder.seekToFrame(0);
+    }
+
+    RenderObject::updateGraphics();
+}
+
+void PlaySecondaryVideo::handleInput(NancyInput &input) {
+    if (hasHotspot && _engine->scene->getViewport().convertViewportToScreen(hotspot).contains(input.mousePos)) {
+        _isHovered = true;
+    } else {
+        _isHovered = false;
+    }
+}
+
+uint16 PlaySecondaryVideo::readData(Common::SeekableReadStream &stream) {
+    char buf[10];
+    stream.read(buf, 10);
+    filename = buf;
+    stream.skip(0x14);
+    loopFirstFrame = stream.readUint16LE();
+    loopLastFrame = stream.readUint16LE();
+    onHoverFirstFrame = stream.readUint16LE();
+    onHoverLastFrame = stream.readUint16LE();
+    onHoverEndFirstFrame = stream.readUint16LE();
+    onHoverEndLastFrame = stream.readUint16LE();
+    SceneChange::readData(stream);
+    stream.skip(1);
+
+    uint16 numVideoDescs = stream.readUint16LE();
+    for (uint i = 0; i < numVideoDescs; ++i) {
+        videoDescs.push_back(SecondaryVideoDesc());
+        videoDescs[i].readData(stream);
+    }
+
+    return 0x35 + (numVideoDescs * 0x42);
+}
+
+void PlaySecondaryVideo::execute(NancyEngine *engine) {
+    switch (state) {
+        case kBegin:
+            init();
+            registerGraphics();
+            state = kRun;
+            // fall through
+        case kRun: {
+            // Set correct position according to viewport frame
+            if (_currentViewportFrame != engine->scene->getSceneInfo().frameID) {
+                _currentViewportFrame = engine->scene->getSceneInfo().frameID;
+
+                int activeFrame = -1;
+
+                for (uint i = 0; i < videoDescs.size(); ++i) {
+                    if ((uint16)videoDescs[i].frameID == _currentViewportFrame) {
+                        activeFrame = i;
+                    }
+                }
+
+                if (activeFrame != -1) {
+                    // Make the drawing destination rectangle valid
+                    _screenPosition = videoDescs[activeFrame].destRect;
+
+                    // Activate the hotspot
+                    hotspot = videoDescs[activeFrame].destRect;
+                    hasHotspot = true;
+                    _isPlaying = true;
+                    setVisible(true);
+                } else {
+                    setVisible(false);
+                    hasHotspot = false;
+                    _isPlaying = false;
+                }
+            }
+
+            break;
+        }
+        case kActionTrigger:
+            engine->scene->pushScene();
+            SceneChange::execute(engine);
+            break;
+    }
+}
+
+uint16 PlaySecondaryMovie::readData(Common::SeekableReadStream &stream) {  
+    char name[10];
+    stream.read(name, 10);
+    videoName = name;
+
+    stream.skip(0x1C);
+    for (uint i = 0; i < 15; ++i) {
+        frameFlags[i].frameID = stream.readSint16LE();
+        frameFlags[i].flagDesc.label = stream.readSint16LE();
+        frameFlags[i].flagDesc.flag = (NancyFlag)stream.readUint16LE();
+    }
+
+    triggerFlags.readData(stream);
+    stream.read(name, 10);
+    soundName = Common::String(name);
+    soundChannel = stream.readUint16LE();
+    stream.skip(0xE);
+    soundVolume = stream.readUint16LE();
+    
+    stream.skip(6);
+    SceneChange::readData(stream);
+
+    uint16 numVideoDescs = stream.readUint16LE();
+    for (uint i = 0; i < numVideoDescs; ++i) {
+        videoDescs.push_back(SecondaryVideoDesc());
+        videoDescs[i].readData(stream);
+    }
+
+    return 0xD4 + numVideoDescs * 0x42; // TODO
+}
+
+void PlaySecondaryMovie::init() {
+    if(_decoder.isVideoLoaded()) {
+        _decoder.close();
+    }
+    _drawSurface.create(_decoder.getWidth(), _decoder.getHeight());
+
+    RenderObject::init();
+}
+
+void PlaySecondaryMovie::updateGraphics() {
+    if (!_decoder.isVideoLoaded()) {
+        return;
+    }
+
+    if (!_decoder.isPlaying()) {
+        _decoder.start();
+    }
+
+    if (_decoder.needsUpdate() && !_screenPosition.isEmpty()) {
+        const Graphics::Surface *surf = _decoder.decodeNextFrame();
+        int descID = -1;
+        for (uint i = 0; i < videoDescs.size(); ++i) {
+            if (videoDescs[i].frameID == _decoder.getCurFrame()) {
+                descID = i;
+            }
+        }
+        _drawSurface.blitFrom(*surf, videoDescs[descID].srcRect, Common::Point());
+        _needsRedraw = true;
+    } else {
+        // Set flag if not drawing new frame
+        for (auto f : frameFlags) {
+            if (_decoder.getCurFrame() == f.frameID) {
+                _engine->scene->setEventFlag(f.flagDesc.label, f.flagDesc.flag);
+            }
+        }
+    }
+
+    RenderObject::updateGraphics();
+}
+
+void PlaySecondaryMovie::execute(NancyEngine *engine) {
+    switch (state) {
+        case kBegin:
+            init();
+            if (soundName != "NO SOUND") {
+                engine->sound->loadSound(soundName, soundChannel, 1, soundVolume);
+            }
+            state = kRun;
+            // fall through
+        case kRun: {
+            engine->cursorManager->showCursor(false);
+            
+            int activeFrame = -1;
+
+            for (uint i = 0; i < videoDescs.size(); ++i) {
+                if (_engine->scene->getSceneInfo().frameID == videoDescs[i].frameID) {
+                    activeFrame = i;
+                }
+            }
+
+            if (activeFrame != -1) {
+                _screenPosition = videoDescs[activeFrame].destRect;
+
+                // Start sound if any
+                if (soundName != "NO SOUND") {
+                    engine->sound->pauseSound(soundChannel, false);
+                }
+            } else {
+                _screenPosition = Common::Rect();
+                _needsRedraw = true;
+            }
+
+            break;
+        }
+        case kActionTrigger:
+            triggerFlags.execute(engine);
+            SceneChange::execute(engine);
+            break;
+    }
+}
+
+} // End of namespace Action
+} // End of namespace Nancy
diff --git a/engines/nancy/action/secondaryvideo.h b/engines/nancy/action/secondaryvideo.h
new file mode 100644
index 0000000000..bd98177108
--- /dev/null
+++ b/engines/nancy/action/secondaryvideo.h
@@ -0,0 +1,125 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef NANCY_ACTION_SECONDARYVIDEO_H
+#define NANCY_ACTION_SECONDARYVIDEO_H
+
+#include "engines/nancy/action/recordtypes.h"
+#include "engines/nancy/renderobject.h"
+
+#include "engines/nancy/video.h"
+
+#include "common/str.h"
+#include "common/array.h"
+
+namespace Nancy {
+namespace Action {
+
+struct SecondaryVideoDesc {
+    int16 frameID;
+    Common::Rect srcRect;
+    Common::Rect destRect;
+    // 2 unknown/empty rects
+
+    void readData(Common::SeekableReadStream &stream);
+};
+
+// ActionRecord that shows NPC animations outside of dialogue. Supports
+// different animations depending on whether the NPC is hovered by the mouse
+class PlaySecondaryVideo : public SceneChange, public RenderObject {
+public:
+    enum HoverState { kNoHover, kHover, kEndHover };
+
+    PlaySecondaryVideo(RenderObject &redrawFrom) : RenderObject(redrawFrom) {}
+    virtual ~PlaySecondaryVideo() { _decoder.close(); }
+
+    virtual void init() override;
+    virtual void updateGraphics() override;
+    virtual void handleInput(NancyInput &input) override;
+
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void execute(NancyEngine *engine) override;
+
+    Common::String filename;
+    //...
+    uint16 loopFirstFrame = 0; // 0x1E
+    uint16 loopLastFrame = 0; // 0x20
+    uint16 onHoverFirstFrame = 0; // 0x22
+    uint16 onHoverLastFrame = 0; // 0x24
+    uint16 onHoverEndFirstFrame = 0; // 0x26
+    uint16 onHoverEndLastFrame = 0; // 0x28
+    // SceneChange data is at 0x2A
+    // unknown byte
+    Common::Array<SecondaryVideoDesc> videoDescs; // 0x35
+
+protected:
+    virtual uint16 getZOrder() const override { return 8; }
+    virtual BlitType getBlitType() const override { return kTrans; }
+    virtual bool isViewportRelative() const override { return true; }
+
+    HoverState hoverState = kNoHover;
+    AVFDecoder _decoder;
+    uint _currentViewportFrame = 0;
+    bool _isPlaying = false;
+    bool _isHovered = false;
+};
+
+class PlaySecondaryMovie : public SceneChange, public RenderObject {
+public:
+    struct FlagAtFrame {
+        int16 frameID;
+        FlagDesc flagDesc;
+    };
+
+    PlaySecondaryMovie(RenderObject &redrawFrom) : RenderObject(redrawFrom) {}
+    virtual ~PlaySecondaryMovie() { _decoder.close(); }
+
+    virtual void init()override;
+    virtual void updateGraphics() override;
+
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void execute(NancyEngine *engine) override;
+
+    Common::String videoName; // 0x00
+
+    FlagAtFrame frameFlags[15]; // 0x26
+    EventFlagsDesc triggerFlags; // 0x80
+
+    Common::String soundName; // 0xA8
+    uint16 soundChannel = 0; // 0xB2
+    uint16 soundVolume = 0; // 0xC2
+
+    // SceneChange data at 0xCA
+    Common::Array<SecondaryVideoDesc> videoDescs; // 0xD4
+
+protected:
+    virtual uint16 getZOrder() const override { return 8; }
+    virtual BlitType getBlitType() const override { return kNoTrans; }
+    virtual bool isViewportRelative() const override { return true; }
+
+    AVFDecoder _decoder;
+};
+
+} // End of namespace Action
+} // End of namespace Nancy
+
+#endif // NANCY_ACTION_SECONDARYVIDEO_H
diff --git a/engines/nancy/action/staticbitmapanim.cpp b/engines/nancy/action/staticbitmapanim.cpp
new file mode 100644
index 0000000000..0e1bc7dbd3
--- /dev/null
+++ b/engines/nancy/action/staticbitmapanim.cpp
@@ -0,0 +1,141 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/nancy/action/staticbitmapanim.h"
+
+#include "engines/nancy/state/scene.h"
+
+#include "engines/nancy/nancy.h"
+#include "engines/nancy/util.h"
+#include "engines/nancy/resource.h"
+
+#include "common/rational.h"
+
+namespace Nancy {
+namespace Action {
+
+void PlayIntStaticBitmapAnimation::init() {
+    Graphics::Surface surf;
+    _engine->_res->loadImage("ciftree", imageName, surf);
+
+    _fullSurface.create(surf.w, surf.h, surf.format);
+    _fullSurface.blitFrom(surf);
+    setFrame(0);
+
+    RenderObject::init();
+}
+
+uint16 PlayIntStaticBitmapAnimation::readData(Common::SeekableReadStream &stream) {
+    uint beginOffset = stream.pos();
+    char name[10];
+    stream.read(name, 10);
+    imageName = Common::String(name);
+
+    stream.skip(0xA);
+    firstFrame = stream.readUint16LE();
+    stream.skip(2);
+    lastFrame = stream.readUint16LE();
+    frameTime = Common::Rational(1000, stream.readUint16LE()).toInt();
+    stream.skip(2);
+    soundFlagDesc.label = stream.readSint16LE();
+    soundFlagDesc.flag = (NancyFlag)stream.readUint16LE();
+
+    SceneChange::readData(stream);
+
+    triggerFlags.readData(stream);
+
+    stream.read(name, 10);
+    soundName = name;
+    channelID = stream.readUint16LE();
+
+    stream.seek(beginOffset + 0x74, SEEK_SET);
+    uint numFrames = stream.readUint16LE();
+
+    for (uint i = firstFrame; i <= lastFrame; ++i) {
+        srcRects.push_back(Common::Rect());
+        readRect(stream, srcRects[i]);
+    }
+
+    for (uint i = 0; i < numFrames; ++i) {
+        bitmaps.push_back(BitmapDesc());
+        BitmapDesc &rects = bitmaps[i];
+        rects.frameID = stream.readUint16LE();
+        readRect(stream, rects.src);
+        readRect(stream, rects.dest);
+    }
+
+    return 0x76 + numFrames * 0x22 + (lastFrame - firstFrame + 1) * 16;
+}
+
+void PlayIntStaticBitmapAnimation::execute(NancyEngine *engine) {
+    // TODO handle sound, event flags
+    uint32 currentFrameTime = engine->getTotalPlayTime();
+    switch (state) {
+        case kBegin:
+            init();
+            registerGraphics();
+
+            if (soundName != "NO SOUND") {
+                warning("PlayIntStaticBitmapAnimation has a sound, please implement it!");
+            }
+
+            state = kRun;
+            // fall through
+        case kRun: {
+            // Check if we've moved the viewport
+            uint16 newFrame = engine->scene->getSceneInfo().frameID;
+            if (currentViewportFrame != newFrame) {
+                currentViewportFrame = newFrame;
+                for (uint i = 0; i < bitmaps.size(); ++i) {
+                    if (currentViewportFrame == bitmaps[i].frameID) {
+                        nextFrameTime = 0;
+                        _screenPosition = bitmaps[i].dest;
+                        break;
+                    }
+                }
+            }
+
+            // Check the timer to see if we need to draw the next animation frame
+            if (nextFrameTime <= currentFrameTime) {
+                nextFrameTime = currentFrameTime + frameTime;
+                currentFrame = ++currentFrame > lastFrame ? firstFrame : currentFrame;
+                setFrame(currentFrame);
+            }
+            
+            break;
+        }
+        case kActionTrigger:
+            triggerFlags.execute(engine);
+
+            SceneChange::execute(engine);
+            break;
+    }
+}
+
+void PlayIntStaticBitmapAnimation::setFrame(uint frame) {
+    currentFrame = frame;
+    _drawSurface.create(_fullSurface, srcRects[frame]);
+    _needsRedraw = true;
+}
+
+} // End of namespace Action
+} // End of namespace Nancy
diff --git a/engines/nancy/action/staticbitmapanim.h b/engines/nancy/action/staticbitmapanim.h
new file mode 100644
index 0000000000..f079a60193
--- /dev/null
+++ b/engines/nancy/action/staticbitmapanim.h
@@ -0,0 +1,81 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef NANCY_ACTION_STATICBITMAPANIM_H
+#define NANCY_ACTION_STATICBITMAPANIM_H
+
+#include "engines/nancy/action/recordtypes.h"
+#include "engines/nancy/renderobject.h"
+
+#include "common/str.h"
+#include "common/array.h"
+
+namespace Nancy {
+namespace Action {
+
+// ActionRecord subclass describing a short "flipbook" animation from a single bitmap
+// Can also play sound, but this has not yet been implemented
+class PlayIntStaticBitmapAnimation : public SceneChange, public RenderObject {
+public:
+    PlayIntStaticBitmapAnimation(RenderObject &redrawFrom) : RenderObject(redrawFrom) {}
+    virtual ~PlayIntStaticBitmapAnimation() { _fullSurface.free(); }
+
+    virtual void init()override;
+
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void execute(NancyEngine *engine) override;
+
+    Common::String imageName;
+    uint16 firstFrame;
+    uint16 lastFrame;
+    FlagDesc soundFlagDesc;
+    EventFlagsDesc triggerFlags;
+    Time frameTime;
+
+    // Todo
+    Common::String soundName;
+    uint16 channelID;
+
+    // Describes a single frame in this animation
+    Common::Array<Common::Rect> srcRects;
+    // Describes how the animation will be displayed on a single
+    // frame of the viewport
+    Common::Array<BitmapDesc> bitmaps;
+
+    int16 currentFrame = -1;
+    int16 currentViewportFrame = -1;
+    Time nextFrameTime;
+    
+protected:
+    virtual uint16 getZOrder() const override { return 7; }
+    virtual BlitType getBlitType() const override { return kNoTrans; }
+    virtual bool isViewportRelative() const override { return true; }
+
+    void setFrame(uint frame);
+    
+    Graphics::ManagedSurface _fullSurface;
+};
+
+} // End of namespace Action
+} // End of namespace Nancy
+
+#endif // NANCY_ACTION_STATICBITMAPANIM_H
diff --git a/engines/nancy/audio.cpp b/engines/nancy/audio.cpp
index d701e2bb50..64df060a7a 100644
--- a/engines/nancy/audio.cpp
+++ b/engines/nancy/audio.cpp
@@ -224,4 +224,4 @@ void SoundManager::stopAllSounds() {
 	_mixer->stopAll();
 }
 
-} // End of namespace Nancy
\ No newline at end of file
+} // End of namespace Nancy
diff --git a/engines/nancy/audio.h b/engines/nancy/audio.h
index a19980b499..eb07003292 100644
--- a/engines/nancy/audio.h
+++ b/engines/nancy/audio.h
@@ -64,4 +64,4 @@ private:
 
 } // End of namespace Nancy
 
-#endif // NANCY_AUDIO_H
\ No newline at end of file
+#endif // NANCY_AUDIO_H
diff --git a/engines/nancy/menu.cpp b/engines/nancy/commontypes.h
similarity index 67%
rename from engines/nancy/menu.cpp
rename to engines/nancy/commontypes.h
index bde90bfab6..21d816dea7 100644
--- a/engines/nancy/menu.cpp
+++ b/engines/nancy/commontypes.h
@@ -20,32 +20,16 @@
  *
  */
 
-#include "engines/nancy/menu.h"
-#include "engines/nancy/nancy.h"
+#ifndef NANCY_COMMONYPES_H
+#define NANCY_COMMONYPES_H
 
-#include "graphics/surface.h"
+#include "common/scummsys.h"
 
 namespace Nancy {
 
-void MainMenu::process() {
-    switch(_state) {
-    case kInit:
-        init();
-        break;
-    case kStartSound:
-        startSound();
-        break;
-    case kRun:
-        run();
-        break;
-    }
-}
-
-void MainMenu::init() {
-    _surf = new Graphics::Surface;
-    _menuImage = new Graphics::Surface;
-    _cursor = new Graphics::Surface;
-    
-}
-
-} // End of namespace Nancy
\ No newline at end of file
+enum NancyFlag { kFalse = 1, kTrue = 2 };
+enum MovementDirection : byte { kUp = 1, kDown = 2, kLeft = 4, kRight = 8, kMoveFast = 16 };
+
+} // End of namespace Nancy
+
+#endif // NANCY_COMMONYPES_H
diff --git a/engines/nancy/console.cpp b/engines/nancy/console.cpp
index 0033c0a98a..716148873e 100644
--- a/engines/nancy/console.cpp
+++ b/engines/nancy/console.cpp
@@ -26,7 +26,7 @@
 #include "engines/nancy/video.h"
 #include "engines/nancy/audio.h"
 #include "engines/nancy/iff.h"
-#include "engines/nancy/scene.h"
+#include "engines/nancy/state/scene.h"
 
 #include "common/system.h"
 #include "common/events.h"
@@ -301,11 +301,8 @@ bool NancyConsole::Cmd_loadScene(int argc, const char **argv) {
 		return true;
 	}
 
-	_vm->sceneManager->_sceneID = (uint16)atoi(argv[1]);
-	_vm->playState.queuedViewFrame = 0;
-	_vm->playState.queuedMaxVerticalScroll = 0;
-	_vm->sceneManager->doNotStartSound = false;
-	_vm->sceneManager->_state = SceneManager::kLoadNew;
+	_vm->scene->changeScene((uint16)atoi(argv[1]), 0, 0, false);
+	_vm->scene->_state = State::Scene::kLoadNew;
 	return cmdExit(0, 0);
 }
 
@@ -315,8 +312,8 @@ bool NancyConsole::Cmd_sceneID(int argc, const char **argv) {
 		return true;
 	}
 
-	debugPrintf("Scene: %u, Frame: %i \n", _vm->sceneManager->_sceneID, _vm->playState.currentViewFrame);
+	debugPrintf("Scene: %u, Frame: %i \n", _vm->scene->getSceneInfo().sceneID, _vm->scene->getSceneInfo().frameID);
 	return true;
 }
 
-} // End of namespace Nancy
\ No newline at end of file
+} // End of namespace Nancy
diff --git a/engines/nancy/console.h b/engines/nancy/console.h
index f56ef0d40f..cb6fa19942 100644
--- a/engines/nancy/console.h
+++ b/engines/nancy/console.h
@@ -25,6 +25,8 @@
 
 #include "gui/debugger.h"
 
+#include "common/str.h"
+
 namespace Nancy {
 
 class NancyEngine;
@@ -56,4 +58,4 @@ private:
 
 } // End of namespace Nancy
 
-#endif // NANCY_CONSOLE_H
\ No newline at end of file
+#endif // NANCY_CONSOLE_H
diff --git a/engines/nancy/cursor.cpp b/engines/nancy/cursor.cpp
new file mode 100644
index 0000000000..2f338aa161
--- /dev/null
+++ b/engines/nancy/cursor.cpp
@@ -0,0 +1,140 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/nancy/cursor.h"
+
+#include "engines/nancy/nancy.h"
+#include "engines/nancy/graphics.h"
+#include "engines/nancy/resource.h"
+#include "engines/nancy/util.h"
+
+#include "common/stream.h"
+#include "common/str.h"
+
+namespace Nancy {
+
+void CursorManager::init() {
+    Common::SeekableReadStream *chunk = _engine->getBootChunkStream("INV");
+    chunk->seek(0x1D2); // TODO
+    Common::String inventoryCursorsImageName = chunk->readString();
+
+    chunk = _engine->getBootChunkStream("CURS");
+    for (uint i = 0; i < 56; ++i) {
+        _cursors.push_back(Cursor());
+        chunk->seek(i * 16, SEEK_SET);
+        readRect(*chunk, _cursors[i].bounds);
+        chunk->seek(0x380 + i * 8, SEEK_SET);
+        _cursors[i].hotspot.x = chunk->readUint32LE();
+        _cursors[i].hotspot.y = chunk->readUint32LE();
+    }
+
+    readRect(*chunk, _primaryVideoInactiveZone);
+    _primaryVideoInitialPos.x = chunk->readUint16LE();
+    _primaryVideoInitialPos.y = chunk->readUint16LE();
+
+    Graphics::Surface surf;
+    _engine->_res->loadImage("ciftree", inventoryCursorsImageName, surf);
+    _invCursorsSurface.create(surf.w, surf.h, surf.format);
+    _invCursorsSurface.blitFrom(surf);
+
+    setCursor(kNormalArrow, -1);
+    showCursor(false);
+
+    _isInitialized = true;
+}
+
+void CursorManager::setCursor(CursorType type, int16 itemID) {
+    if (!_isInitialized) {
+        return;
+    }
+
+    if (type == _curCursorType && itemID == _curItemID) {
+        return;
+    } else {
+        _curCursorType = type;
+        _curItemID = itemID;
+    }
+
+    uint16 newID = 0;
+    bool hasItem = false;
+
+    switch (type) {
+        case kNormalArrow:
+            newID = 4;
+            break;
+        case kHotspotArrow:
+            newID = 6;
+            break;
+        case kExitArrow:
+            newID = 3;
+            break;
+        default: {
+            if (itemID == -1) {
+                // No item held, set to eyeglass
+                itemID = 0;
+            } else {
+                // Item held
+                itemID += 3;
+                hasItem = true;
+            }
+
+            newID = itemID * 4 + type;
+        }
+    }
+
+    Graphics::ManagedSurface *surf;
+    Common::Rect bounds = _cursors[newID].bounds;
+    Common::Point hotspot = _cursors[newID].hotspot;
+
+    if (hasItem) {
+        surf = &_invCursorsSurface;
+        
+    } else {
+        surf = &_engine->graphicsManager->object0;
+    }
+
+    // TODO this is ridiculous, figure out why just calling
+    // GetBasePtr() results in garbage
+    Graphics::Surface s;
+    s.create(bounds.width(), bounds.height(), surf->format);
+    s.copyRectToSurface(*surf, 0, 0, bounds);
+
+    // TODO hotspots are terrible for arrow cursors, fix that??
+    CursorMan.replaceCursor(s.getPixels(), s.w, s.h, hotspot.x, hotspot.y, GraphicsManager::transColor, false, &GraphicsManager::pixelFormat);
+
+    s.free();
+
+}
+
+void CursorManager::setCursorType(CursorType type) {
+    setCursor(type, _curItemID);
+}
+
+void CursorManager::setCursorItemID(int16 itemID) {
+    setCursor(_curCursorType, itemID);
+}
+
+void CursorManager::showCursor(bool shouldShow) {
+    CursorMan.showMouse(shouldShow);
+}
+
+} // End of namespace Nancy
diff --git a/engines/nancy/cursor.h b/engines/nancy/cursor.h
new file mode 100644
index 0000000000..e15621b125
--- /dev/null
+++ b/engines/nancy/cursor.h
@@ -0,0 +1,73 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef NANCY_CURSOR_H
+#define NANCY_CURSOR_H
+
+#include "graphics/cursorman.h"
+#include "graphics/managed_surface.h"
+
+namespace Nancy {
+
+class NancyEngine;
+
+class CursorManager {
+public:
+    enum CursorType { kNormal = 0, kHotspot = 1, kMove = 2, kNormalArrow, kHotspotArrow, kExitArrow };
+
+    CursorManager(NancyEngine *engine) :
+        _engine(engine),
+        _isInitialized(false),
+        _curItemID(-1),
+        _curCursorType(kNormal) {}
+
+    void init();
+
+    void setCursor(CursorType type, int16 itemID);
+    void setCursorType(CursorType type);
+    void setCursorItemID(int16 itemID);
+    void showCursor(bool shouldShow);
+
+private:
+    struct Cursor {
+        Common::Rect bounds;
+        Common::Point hotspot;
+    };
+
+    NancyEngine *_engine;
+
+    // CURS data
+    Common::Array<Cursor> _cursors;
+
+    Common::Rect _primaryVideoInactiveZone;
+    Common::Point _primaryVideoInitialPos;
+
+    Graphics::ManagedSurface _invCursorsSurface;
+
+    CursorType _curCursorType;
+    int16 _curItemID;
+    bool _isInitialized;
+};
+
+} // End of namespace Nancy
+
+#endif // NANCY_CURSOR_H
diff --git a/engines/nancy/datatypes.cpp b/engines/nancy/datatypes.cpp
deleted file mode 100644
index c90af2b012..0000000000
--- a/engines/nancy/datatypes.cpp
+++ /dev/null
@@ -1,166 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "engines/nancy/datatypes.h"
-
-#include "common/stream.h"
-
-namespace Nancy {
-
-// Simple helper function to read rectangles
-// TODO identical to the one in recordtypes.h, move somewhere else
-static void readRect(Common::SeekableReadStream &stream, Common::Rect &inRect) {
-    inRect.left = stream.readUint32LE();
-    inRect.top = stream.readUint32LE();
-    inRect.right = stream.readUint32LE();
-    inRect.bottom = stream.readUint32LE();
-}
-
-void SceneSummary::read(Common::SeekableReadStream &stream) {
-    char *buf = new char[0x32];
-
-    stream.read(buf, 0x31);
-    buf[32] = 0;
-    description = Common::String(buf);
-
-    stream.seek(1, SEEK_CUR);
-    stream.read(buf, 9);
-    buf[9] = 0;
-    videoFile = Common::String(buf);
-
-    // skip 1 extra byte & 2 unknown bytes
-    stream.seek(3, SEEK_CUR);
-    videoFormat = stream.readUint16LE();
-
-    stream.read(buf, 10);
-    buf[9] = 0;
-    audioFile = Common::String(buf);
-    audioID = stream.readSint16LE();
-    stream.skip(0xE);
-    audioVolume = stream.readUint16LE();
-
-    stream.seek(0x72);
-    verticalScrollDelta = stream.readUint16LE();
-    horizontalEdgeSize = stream.readUint16LE();
-    verticalEdgeSize = stream.readUint16LE();
-    slowMoveTimeDelta = stream.readUint16LE();
-    fastMoveTimeDelta = stream.readUint16LE();
-    unknown7C = stream.readByte();
-
-    delete[] buf;
-}
-
-// Takes a VIEW chunk as input
-void View::read(Common::SeekableReadStream &stream) {
-    stream.seek(0);
-    readRect(stream, destination);
-    readRect(stream, source);
-    readRect(stream, f1Dest);
-    readRect(stream, f2Dest);
-}
-
-// Takes a CURS chunk as input
-void Cursors::read(Common::SeekableReadStream &stream) {
-    stream.seek(0);
-    for (uint i = 0; i < 84; ++i) {
-        readRect(stream, rects[i]);
-    }
-    readRect(stream, primaryVideoInactiveZone);
-    primaryVideoInitialPos.x = stream.readUint16LE();
-    primaryVideoInitialPos.y = stream.readUint16LE();
-}
-
-void Inventory::read(Common::SeekableReadStream &stream) {
-    stream.seek(0, SEEK_SET);
-
-    readRect(stream, sliderSource);
-    // Stored with uint16s for some reason
-    sliderDefaultDest.x = stream.readUint16LE();
-    sliderDefaultDest.y = stream.readUint16LE();
-
-    stream.seek(0xD6, SEEK_SET);
-    for (uint i = 0; i < 14; ++i) {
-        readRect(stream, shadesSrc[i]);
-    }
-    readRect(stream, shadesDst);
-    shadesFrameTime = stream.readUint16LE();
-
-    char name[10];
-    stream.read(name, 10);
-    inventoryBoxIconsImageName = Common::String(name);
-    stream.read(name, 10);
-    inventoryCursorsImageName = Common::String(name);
-
-    stream.skip(8);
-    readRect(stream, emptySpaceSource);
-
-    char itemName[0x14];
-    for (uint i = 0; i < 11; ++i) {
-        stream.read(itemName, 0x14);
-        items[i].name = Common::String(itemName);
-        items[i].oneTimeUse = stream.readUint16LE();
-        readRect(stream, items[i].sourceRect);
-    }
-}
-
-void Font::read(Common::SeekableReadStream &stream) {
-    char name[10];
-    stream.read(name, 10);
-    imageName = name;
-
-    char desc[0x20];
-    stream.read(desc, 0x20);
-    description = desc;
-    stream.skip(8);
-    colorCoordsOffset.x = stream.readUint16LE();
-    colorCoordsOffset.y = stream.readUint16LE();
-
-    stream.skip(2);
-    spaceWidth = stream.readUint16LE();
-    stream.skip(2);
-    uppercaseOffset = stream.readUint16LE();
-    lowercaseOffset = stream.readUint16LE();
-    digitOffset = stream.readUint16LE();
-    periodOffset = stream.readUint16LE();
-    commaOffset = stream.readUint16LE();
-    equalitySignOffset = stream.readUint16LE();
-    colonOffset = stream.readUint16LE();
-    dashOffset = stream.readUint16LE();
-    questionMarkOffset = stream.readUint16LE();
-    exclamationMarkOffset = stream.readUint16LE();
-    percentOffset = stream.readUint16LE();
-    ampersandOffset = stream.readUint16LE();
-    asteriskOffset = stream.readUint16LE();
-    leftBracketOffset = stream.readUint16LE();
-    rightBracketOffset = stream.readUint16LE();
-    plusOffset = stream.readUint16LE();
-    apostropheOffset = stream.readUint16LE();
-    semicolonOffset = stream.readUint16LE();
-    slashOffset = stream.readUint16LE();
-
-    for (uint i = 0; i < 78; ++i) {
-        symbolRects.push_back(Common::Rect());
-        readRect(stream, symbolRects[i]);
-    }
-}
-
-} // End of namespace Nancy
\ No newline at end of file
diff --git a/engines/nancy/datatypes.h b/engines/nancy/datatypes.h
deleted file mode 100644
index 7606821161..0000000000
--- a/engines/nancy/datatypes.h
+++ /dev/null
@@ -1,142 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef NANCY_DATATYPES_H
-#define NANCY_DATATYPES_H
-
-#include "common/str.h"
-#include "common/rect.h"
-#include "common/array.h"
-
-#include "graphics/surface.h"
-
-namespace Common {
-class SeekableReadStream;
-}
-
-namespace Nancy {
-
-// Various data types we need to extract from chunks go here
-
-// Describes a scene
-struct SceneSummary {
-    void read(Common::SeekableReadStream &stream);
-
-    Common::String description; // 0x00
-    Common::String videoFile;   // 0x32
-    //
-    uint16 videoFormat;         // 0x3E, value is 1 or 2
-    Common::String audioFile;   // 0x40
-    int16 audioID;              // 0x4A
-    uint16 audioVolume;         // 0x5A
-    //
-    uint16 verticalScrollDelta; // 0x72
-    uint16 horizontalEdgeSize;  // 0x74
-    uint16 verticalEdgeSize;    // 0x76
-    uint16 slowMoveTimeDelta;   // 0x78
-    uint16 fastMoveTimeDelta;   // 0x7A
-    byte unknown7C;             // 0x7C, enum with 4 values
-    //
-};
-
-// Describes the viewport
-struct View {
-    void read(Common::SeekableReadStream &stream);
-    // The bounds of the destination rectangle on screen
-    Common::Rect destination;
-    // The bounds of the source rectangle (Background -> screen)
-    Common::Rect source;
-    // VideoFileFormat 1 rectangle bounds (video -> Background)
-    Common::Rect f1Dest;
-    // VideoFileFormat 2 rectangle bounds (video -> Background)
-    Common::Rect f2Dest;
-};
-
-// Holds the coordinates for the bitmaps of all cursors
-struct Cursors {
-    void read(Common::SeekableReadStream &stream);
-
-    Common::Rect rects[84];
-    Common::Rect primaryVideoInactiveZone;
-    Common::Point primaryVideoInitialPos;
-};
-
-struct Inventory {
-    struct ItemDesc {
-        Common::String name; // 0x00
-        byte oneTimeUse = 0; // 0x14
-        Common::Rect sourceRect; // 0x16
-    };
-
-    void read(Common::SeekableReadStream &stream);
-
-    Common::Rect sliderSource; // 0x00
-    Common::Point sliderDefaultDest; // 0x10
-    //...
-    Common::Rect shadesSrc[14]; // 0xD6
-    Common::Rect shadesDst; // 0x1B6
-    uint16 shadesFrameTime; // 0x1C6
-    Common::String inventoryBoxIconsImageName; // 0x1C8
-    Common::String inventoryCursorsImageName; // 0x1D2
-
-    Common::Rect emptySpaceSource; // 0x1E4
-    ItemDesc items[11]; // 0x1F4
-};
-
-struct Font {
-    void read(Common::SeekableReadStream &stream);
-
-    Common::String imageName; // 0x00
-    Common::String description; // 0xA
-    //
-    Common::Point colorCoordsOffset; // y is def at 0x34, x is just a guess
-
-    uint16 spaceWidth = 0;              // 0x38
-    
-    uint16 uppercaseOffset = 0;         // 0x3C
-    uint16 lowercaseOffset = 0;         // 0x3E
-    uint16 digitOffset = 0;             // 0x40
-    uint16 periodOffset = 0;            // 0x42
-    uint16 commaOffset = 0;             // 0x44
-    uint16 equalitySignOffset = 0;      // 0x46
-    uint16 colonOffset = 0;             // 0x48
-    uint16 dashOffset = 0;              // 0x4A
-    uint16 questionMarkOffset = 0;      // 0x4C
-    uint16 exclamationMarkOffset = 0;   // 0x4E
-    uint16 percentOffset = 0;           // 0x50
-    uint16 ampersandOffset = 0;         // 0x52
-    uint16 asteriskOffset = 0;          // 0x54
-    uint16 leftBracketOffset = 0;       // 0x56
-    uint16 rightBracketOffset = 0;      // 0x58
-    uint16 plusOffset = 0;              // 0x5A
-    uint16 apostropheOffset = 0;        // 0x5C
-    uint16 semicolonOffset = 0;         // 0x5E
-    uint16 slashOffset = 0;             // 0x60
-
-    Common::Array<Common::Rect> symbolRects; // 0x62
-    
-    Graphics::Surface image;
-};
-
-} // End of namespace Nancy
-
-#endif // NANCY_DATATYPES_H
\ No newline at end of file
diff --git a/engines/nancy/decompress.cpp b/engines/nancy/decompress.cpp
index ad20d278ef..b16563ba75 100644
--- a/engines/nancy/decompress.cpp
+++ b/engines/nancy/decompress.cpp
@@ -100,4 +100,4 @@ bool Decompressor::decompress(Common::ReadStream &input, Common::MemoryWriteStre
 	return true;
 }
 
-} // End of namespace Nancy
\ No newline at end of file
+} // End of namespace Nancy
diff --git a/engines/nancy/decompress.h b/engines/nancy/decompress.h
index 1fc89e46c3..9a22db8828 100644
--- a/engines/nancy/decompress.h
+++ b/engines/nancy/decompress.h
@@ -59,4 +59,4 @@ private:
 
 } // End of namespace Nancy
 
-#endif // NANCY_DECOMPRESS_H
\ No newline at end of file
+#endif // NANCY_DECOMPRESS_H
diff --git a/engines/nancy/detection.cpp b/engines/nancy/detection.cpp
index e21ed18209..964e5fe41a 100644
--- a/engines/nancy/detection.cpp
+++ b/engines/nancy/detection.cpp
@@ -222,4 +222,4 @@ public:
 	
 };
 
-REGISTER_PLUGIN_STATIC(NANCY_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, NancyMetaEngineDetection);
\ No newline at end of file
+REGISTER_PLUGIN_STATIC(NANCY_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, NancyMetaEngineDetection);
diff --git a/engines/nancy/detection.h b/engines/nancy/detection.h
index 6a2b7b483c..1d98c8ccba 100644
--- a/engines/nancy/detection.h
+++ b/engines/nancy/detection.h
@@ -41,4 +41,4 @@ struct NancyGameDescription {
 
 } // End of namespace Nancy
 
-#endif // NANCY_DETECTION_H
\ No newline at end of file
+#endif // NANCY_DETECTION_H
diff --git a/engines/nancy/font.cpp b/engines/nancy/font.cpp
new file mode 100644
index 0000000000..df12c2421a
--- /dev/null
+++ b/engines/nancy/font.cpp
@@ -0,0 +1,185 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/nancy/font.h"
+
+#include "engines/nancy/util.h"
+#include "engines/nancy/nancy.h"
+#include "engines/nancy/resource.h"
+#include "engines/nancy/graphics.h"
+
+namespace Nancy {
+
+void Font::read(Common::SeekableReadStream &stream, NancyEngine *engine) {
+    _transColor = engine->graphicsManager->transColor;
+    _maxCharWidth = 0;
+    _fontHeight = 0;
+
+    char name[10];
+    stream.read(name, 10);
+    Common::String imageName = name;
+
+    engine->_res->loadImage("ciftree", name, _image);
+
+    char desc[0x20];
+    stream.read(desc, 0x20);
+    _description = desc;
+    stream.skip(8);
+    _colorCoordsOffset.x = stream.readUint16LE();
+    _colorCoordsOffset.y = stream.readUint16LE();
+
+    stream.skip(2);
+    _spaceWidth = stream.readUint16LE();
+    stream.skip(2);
+    _uppercaseOffset = stream.readUint16LE();
+    _lowercaseOffset = stream.readUint16LE();
+    _digitOffset = stream.readUint16LE();
+    _periodOffset = stream.readUint16LE();
+    _commaOffset = stream.readUint16LE();
+    _equalitySignOffset = stream.readUint16LE();
+    _colonOffset = stream.readUint16LE();
+    _dashOffset = stream.readUint16LE();
+    _questionMarkOffset = stream.readUint16LE();
+    _exclamationMarkOffset = stream.readUint16LE();
+    _percentOffset = stream.readUint16LE();
+    _ampersandOffset = stream.readUint16LE();
+    _asteriskOffset = stream.readUint16LE();
+    _leftBracketOffset = stream.readUint16LE();
+    _rightBracketOffset = stream.readUint16LE();
+    _plusOffset = stream.readUint16LE();
+    _apostropheOffset = stream.readUint16LE();
+    _semicolonOffset = stream.readUint16LE();
+    _slashOffset = stream.readUint16LE();
+
+    
+
+    for (uint i = 0; i < 78; ++i) {
+        _symbolRects.push_back(Common::Rect());
+        Common::Rect &cur = _symbolRects[i];
+        readRect(stream, cur);
+
+        _maxCharWidth = MAX<int>(cur.width(), _maxCharWidth);
+        _fontHeight = MAX<int>(cur.height(), _maxCharWidth);
+    }
+}
+
+int Font::getCharWidth(uint32 chr) const {
+    return getCharacterSourceRect(chr).width();
+}
+
+void Font::drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const {
+    Common::Rect srcRect = getCharacterSourceRect(chr);
+    if (color != 0) {
+        srcRect.translate(_colorCoordsOffset.x, _colorCoordsOffset.y);
+    }
+
+    uint width = srcRect.width();
+    uint height = srcRect.height();
+    uint yOffset = getFontHeight() - height;
+    
+    for (uint curY = 0; curY < height; ++curY) {
+        for (uint curX = 0; curX < width; ++curX) {
+            // Assumes 2 bytes per pixel
+            uint16 curByte = *(const uint16 *)_image.getBasePtr(srcRect.left + curX, srcRect.top +  curY);
+            
+            if (curByte != _transColor) {
+                *(uint16 *)dst->getBasePtr(x + curX, y + yOffset + curY) = curByte;
+            }
+        }
+    }
+}
+
+Common::Rect Font::getCharacterSourceRect(char chr) const {
+    using namespace Common;
+    uint offset = 0;
+    Common::Rect ret;
+
+    if (isUpper(chr)) {
+        offset = chr + _uppercaseOffset - 0x41;
+    } else if (isLower(chr)) {
+        offset = chr + _lowercaseOffset - 0x61;
+    } else if (isDigit(chr)) {
+        offset = chr + _digitOffset - 0x30;
+    } else if (isSpace(chr)) {
+        ret.setWidth(_spaceWidth - 1); // Not sure why we sutract 1
+        return ret;
+    } else if (isPunct(chr)) {
+        switch (chr) {
+            case '.':
+                offset = _periodOffset;
+                break;
+            case ',':
+                offset = _commaOffset;
+                break;
+            case '=':
+                offset = _equalitySignOffset;
+                break;
+            case ':':
+                offset = _colonOffset;
+                break;
+            case '-':
+                offset = _dashOffset;
+                break;
+            case '?':
+                offset = _questionMarkOffset;
+                break;
+            case '!':
+                offset = _exclamationMarkOffset;
+                break;
+            case '%':
+                offset = _percentOffset;
+                break;
+            case '&':
+                offset = _ampersandOffset;
+                break;
+            case '*':
+                offset = _asteriskOffset;
+                break;
+            case '(':
+                offset = _leftBracketOffset;
+                break;
+            case ')':
+                offset = _rightBracketOffset;
+                break;
+            case '+':
+                offset = _plusOffset;
+                break;
+            case '\'':
+                offset = _apostropheOffset;
+                break;
+            case ';':
+                offset = _semicolonOffset;
+                break;
+            case '/':
+                offset = _slashOffset;
+                break;
+            default:
+                error("Unsupported FONT character: %c", chr);
+        }
+    }
+    ret = _symbolRects[offset];
+    ret.right += 1;
+    ret.bottom += 1;
+    return ret;
+}
+
+} // End of namespace Nancy
diff --git a/engines/nancy/font.h b/engines/nancy/font.h
new file mode 100644
index 0000000000..a4dbf420ff
--- /dev/null
+++ b/engines/nancy/font.h
@@ -0,0 +1,91 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef NANCY_FONT_H
+#define NANCY_FONT_H
+
+#include "graphics/font.h"
+
+#include "graphics/surface.h"
+
+#include "common/stream.h"
+#include "common/array.h"
+#include "common/rect.h"
+#include "common/str.h"
+
+namespace Nancy {
+
+class NancyEngine;
+
+class Font : public Graphics::Font {
+public:
+	Font() =default;
+	~Font() =default;
+    
+    void read(Common::SeekableReadStream &stream, NancyEngine *engine);
+
+    int getFontHeight() const override { return _fontHeight; }
+	int getMaxCharWidth() const override { return _maxCharWidth; }
+	int getCharWidth(uint32 chr) const override;
+    int getKerningOffset(uint32 left, uint32 right) const override { return 1; }
+
+	void drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const override;
+
+private:
+    Common::Rect getCharacterSourceRect(char chr) const;
+
+    Common::String _description; // 0xA
+    Common::Point _colorCoordsOffset; // y is def at 0x34, x is just a guess
+
+    uint16 _spaceWidth;              // 0x38
+    
+    uint16 _uppercaseOffset;         // 0x3C
+    uint16 _lowercaseOffset;         // 0x3E
+    uint16 _digitOffset;             // 0x40
+    uint16 _periodOffset;            // 0x42
+    uint16 _commaOffset;             // 0x44
+    uint16 _equalitySignOffset;      // 0x46
+    uint16 _colonOffset;             // 0x48
+    uint16 _dashOffset;              // 0x4A
+    uint16 _questionMarkOffset;      // 0x4C
+    uint16 _exclamationMarkOffset;   // 0x4E
+    uint16 _percentOffset;           // 0x50
+    uint16 _ampersandOffset;         // 0x52
+    uint16 _asteriskOffset;          // 0x54
+    uint16 _leftBracketOffset;       // 0x56
+    uint16 _rightBracketOffset;      // 0x58
+    uint16 _plusOffset;              // 0x5A
+    uint16 _apostropheOffset;        // 0x5C
+    uint16 _semicolonOffset;         // 0x5E
+    uint16 _slashOffset;             // 0x60
+
+    Common::Array<Common::Rect> _symbolRects; // 0x62
+    
+    Graphics::Surface _image;
+
+    int _fontHeight;
+    int _maxCharWidth;
+    uint _transColor;
+};
+
+} // End of namespace Nancy
+
+#endif // NANCY_FONT_H
diff --git a/engines/nancy/graphics.cpp b/engines/nancy/graphics.cpp
index 2e6f664049..47187b789a 100644
--- a/engines/nancy/graphics.cpp
+++ b/engines/nancy/graphics.cpp
@@ -21,509 +21,123 @@
  */
 
 #include "engines/nancy/graphics.h"
-#include "engines/nancy/resource.h"
-#include "engines/nancy/action/recordtypes.h"
-#include "engines/nancy/scene.h"
-
-#include "common/error.h"
-#include "common/system.h"
 
-#include "graphics/managed_surface.h"
+#include "engines/nancy/renderobject.h"
+#include "engines/nancy/resource.h"
+#include "engines/nancy/nancy.h"
+#include "engines/nancy/resource.h"
+#include "engines/nancy/cursor.h"
+#include "engines/nancy/state/scene.h"
+#include "engines/nancy/ui/viewport.h"
 
 namespace Nancy {
 
-// TODO the original engine uses a sixth byte for the green; 
-// so this should be Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0)
-// and transColor should be 0x7C0
-// so the colors are gonna be slightly wrong for now
-//const Graphics::PixelFormat GraphicsManager::pixelFormat = Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0);
-//const uint GraphicsManager::transColor = 0x7C0;
 const Graphics::PixelFormat GraphicsManager::pixelFormat = Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0);
 const uint GraphicsManager::transColor = 0x3E0;
 
-
-GraphicsManager::GraphicsManager(NancyEngine *engine) :
-        _engine(engine),
-        _textbox(engine) {
+void GraphicsManager::init() {
     _screen.create(640, 480, pixelFormat);
-}
+    _screen.setTransparentColor(transColor); 
 
-void GraphicsManager::init() {  
-    Common::SeekableReadStream *chunk = _engine->getBootChunkStream("VIEW");
-    viewportDesc.read(*chunk);
-    uint32 width = viewportDesc.destination.right - viewportDesc.destination.left;
-    uint32 height = viewportDesc.destination.bottom - viewportDesc.destination.top;
-    _background.create(width, height, pixelFormat);
-
-    _textbox.init();
-}
+    Graphics::Surface surf;
+    _engine->_res->loadImage("ciftree", "OBJECT0", surf);
+    object0.create(surf.w, surf.h, surf.format);
+    object0.blitFrom(surf, Common::Point(0, 0));
 
-GraphicsManager::~GraphicsManager() {
-    _background.free();
-    _screen.free();
-    _primaryFrameSurface.free();
-    _object0Surface.free();
-    _inventoryBoxIconsSurface.free();
-    _inventoryCursorsSurface.free();
-    _inventoryBitmapSurface.free();
-    _object0Surface.free();
-    _genericSurface.free();
-
-    _secMovieSurface.free();
-    _primaryVideoSurface.free();
-
-
-    for (auto st : _ZRender) {
-        delete st._value.renderFunction;
+    Common::SeekableReadStream *fontChunk = _engine->getBootChunkStream("FONT");
+    while(fontChunk->pos() != fontChunk->size()) {
+        _fonts.push_back(Font());
+        _fonts.back().read(*fontChunk, _engine);
     }
 }
 
-void GraphicsManager::clearZRenderStruct(Common::String name) {
-    _ZRender.erase(name);
-}
-
-void GraphicsManager::clearZRenderStructs() {
-    _ZRender.clear();
-}
-
-ZRenderStruct &GraphicsManager::getZRenderStruct(Common::String name) {
-    // Creates a new struct if one didn't exist before
-    return _ZRender[name];
-}
-
-Common::String &GraphicsManager::initZRenderStruct(char const *name,
-                                        uint32 z,
-                                        bool isActive,
-                                        ZRenderStruct::BltType bltType,
-                                        Graphics::Surface *surface,
-                                        RenderFunction *func,
-                                        Common::Rect *sourceRect,
-                                        Common::Rect *destRect ) {
-    clearZRenderStruct(name);
-    ZRenderStruct &st = getZRenderStruct(name);
-    st.name = name;
-    st.z = z;
-    st.isActive = isActive;
-    st.isInitialized = true;
-    st.bltType = bltType;
-    st.sourceSurface = surface;
-    if (sourceRect)
-        st.sourceRect = *sourceRect;
-    else st.sourceRect = Common::Rect();
-    if (destRect)
-        st.destRect = *destRect;
-    else st.destRect = Common::Rect();
-    st.renderFunction = func;
-
-    return st.name;
-}
-
-#define READ_RECT(where, x) chunk->seek(x); \
-                            where->left = chunk->readUint32LE(); \
-                            where->top = chunk->readUint32LE(); \
-                            where->right = chunk->readUint32LE(); \
-                            where->bottom = chunk->readUint32LE();
-
-// TODO nancy1 only, move to subclass whenever we support multiple games
-// TODO most of these are wrong and/or incomplete
-// The original engine uses dirty rectangles for optimization and marks
-// their location with zrender structs whose names start with RES.
-// I'm using a more naive implementation where everything is redrawn every frame
-// for code simplicity, but that can be changed in the future if needed
-void GraphicsManager::initSceneZRenderStructs(Common::Array<Common::String> &outNames) {
-    Common::Rect *source = new Common::Rect();
-    Common::Rect *dest = new Common::Rect();
-    Common::SeekableReadStream *chunk = nullptr;
-    
-
-    chunk = _engine->getBootChunkStream("MENU");
-    READ_RECT(source, 16)
-    // Skip the custom rendering function since we're not doing dirty rectangles
-    outNames.push_back(initZRenderStruct(  "FRAME", 1, true, ZRenderStruct::kNoTrans, &_primaryFrameSurface,
-                        nullptr, source, source));
-    outNames.push_back(initZRenderStruct(  "CUR IMAGE CURSOR", 11, true, ZRenderStruct::kTrans, &_object0Surface));
-
-    chunk = _engine->getBootChunkStream("TBOX");
-    READ_RECT(source, 0)
-    outNames.push_back(initZRenderStruct(  "CUR TB BAT SLIDER", 9, true, ZRenderStruct::kTrans,
-                        &_object0Surface, nullptr, source, nullptr));
-
-    chunk = _engine->getBootChunkStream("BSUM");
-    READ_RECT(dest, 356)
-    outNames.push_back(initZRenderStruct(  "FRAME TB SURF", 6, false, ZRenderStruct::kNoTrans,
-                        nullptr, nullptr, nullptr, dest));
-    getZRenderStruct("FRAME TB SURF").managedSource = &_textbox._surface;
-
-    READ_RECT(source, 388)
-    READ_RECT(dest, 420)
-    outNames.push_back(initZRenderStruct(  "MENU BUT DN", 5, false, ZRenderStruct::kTrans,
-                        &_object0Surface, nullptr, source, dest));
-
-    READ_RECT(source, 404)
-    READ_RECT(dest, 436)
-    outNames.push_back(initZRenderStruct(  "HELP BUT DN", 5, false, ZRenderStruct::kTrans,
-                        &_object0Surface, nullptr, source, dest));
-
-    chunk = _engine->getBootChunkStream("INV");
-    READ_RECT(source, 0)
-    outNames.push_back(initZRenderStruct(  "CUR INV SLIDER", 9, true, ZRenderStruct::kTrans,
-                         &_object0Surface, nullptr, source, nullptr));
-
-    outNames.push_back(initZRenderStruct(  "FRAME INV BOX", 6, true, ZRenderStruct::kNoTrans, nullptr,
-                        new RenderFunction(this, &GraphicsManager::renderFrameInvBox)));
-    
-    outNames.push_back(initZRenderStruct(  "INV BITMAP", 9, false, ZRenderStruct::kNoTrans, &_inventoryBitmapSurface));
-    outNames.push_back(initZRenderStruct(  "PRIMARY VIDEO", 8, false, ZRenderStruct::kNoTrans, &_primaryVideoSurface,
-                        new RenderFunction(this, &GraphicsManager::renderPrimaryVideo)));
-    outNames.push_back(initZRenderStruct(  "SEC VIDEO 0", 8, false, ZRenderStruct::kTrans, &channels[0].surf));
-    outNames.push_back(initZRenderStruct(  "SEC VIDEO 1", 8, false, ZRenderStruct::kTrans, &channels[1].surf));
-    outNames.push_back(initZRenderStruct(  "SEC MOVIE", 8, false, ZRenderStruct::kNoTrans, &_secMovieSurface));
-    outNames.push_back(initZRenderStruct(  "ORDERING PUZZLE", 7, false, ZRenderStruct::kNoTrans, nullptr,
-                        new RenderFunction(this, &GraphicsManager::renderOrderingPuzzle)));
-    outNames.push_back(initZRenderStruct(  "ROTATING LOCK PUZZLE", 7, false, ZRenderStruct::kNoTrans, nullptr,
-                        new RenderFunction(this, &GraphicsManager::renderRotatingLockPuzzle)));
-    outNames.push_back(initZRenderStruct(  "LEVER PUZZLE", 7, false, ZRenderStruct::kNoTrans, nullptr,
-                        new RenderFunction(this, &GraphicsManager::renderLeverPuzzle)));
-    outNames.push_back(initZRenderStruct(  "TELEPHONE", 7, false, ZRenderStruct::kNoTrans, nullptr,
-                        new RenderFunction(this, &GraphicsManager::renderTelephone)));
-    outNames.push_back(initZRenderStruct(  "SLIDER PUZZLE", 7, false, ZRenderStruct::kNoTrans, nullptr,
-                        new RenderFunction(this, &GraphicsManager::renderSliderPuzzle)));
-    outNames.push_back(initZRenderStruct(  "PASSWORD PUZZLE", 7, false, ZRenderStruct::kNoTrans, nullptr,
-                        new RenderFunction(this, &GraphicsManager::renderPasswordPuzzle)));
-
-    // Moved here from SceneManager::load(), should be ok
-    outNames.push_back(initZRenderStruct(  "VIEWPORT AVF", 6, true, ZRenderStruct::kNoTrans,
-                        &_background, nullptr, &viewportDesc.source, &viewportDesc.destination));
-
-    // Moved from PlayIntStaticBitmap
-    outNames.push_back(initZRenderStruct(  "STATIC BITMAP ANIMATION", 7, false, ZRenderStruct::kNoTrans,
-                        &_genericSurface));
-
-
-    delete source;
-    delete dest;
-}
-
-void GraphicsManager::initMapRenderStructs(Common::Array<Common::String> &outNames) {
-    Common::Rect *src = new Common::Rect();
-    Common::Rect *dest = new Common::Rect();
-    Common::SeekableReadStream *chunk = _engine->getBootChunkStream("MAP");
-
-    outNames.push_back("FRAME");
-    outNames.push_back("VIEWPORT AVF"); // Replaces MAP AVF
-    outNames.push_back("CUR IMAGE CURSOR"); // Replaces CUR MAP CURSOR
-    outNames.push_back("CUR TB BAT SLIDER");
-    outNames.push_back("CUR INV SLIDER");
-    outNames.push_back(initZRenderStruct("MAP LABELS", 7, true, ZRenderStruct::kTrans, &_object0Surface));
-    READ_RECT(src, 0x7A);
-    READ_RECT(dest, 0x8A);
-    outNames.push_back(initZRenderStruct("MAP ANIM", 9, true, ZRenderStruct::kNoTrans, &_object0Surface,
-                        nullptr, src, dest));
-
-    READ_RECT(src, 0x58)
-    getZRenderStruct("VIEWPORT AVF").sourceRect = *src;
-
-    delete src;
-    delete dest;
-}   
+void GraphicsManager::draw() {
+    // First go through all objects and update them
+    // Then add dirty rects to layers below if transparent
+    for (auto it : _objects) {
+        RenderObject &current = *it;
+        current.updateGraphics();
+    }
 
-#undef READ_RECT
+    Common::Array<Common::Rect> drawn;
 
-void GraphicsManager::renderDisplay() {
-    // Construct a list containing every struct and pass it along
-    Common::Array<Common::String> array;
-    for (auto i : _ZRender) {
-        array.push_back(i._key);
-    }
+    for (auto it : _objects) {
+        RenderObject &current = *it;
 
-    renderDisplay(array);
-}
+        if (current._isVisible && current._needsRedraw) {
+            // object is visible and updated
 
-void GraphicsManager::renderDisplay(Common::Array<Common::String> ids) {
-    for (uint currentZ = _startingZ; currentZ < 12; ++currentZ) {
-        for (uint i = 0; i < ids.size(); ++i) {
-            ZRenderStruct &current = getZRenderStruct(ids[i]);
-            if (current.isActive && current.isInitialized && current.z == currentZ) {
-                if (current.renderFunction && current.renderFunction->isValid()) {
-                    current.renderFunction->operator()();
-                }
-                else {
-                    switch (current.bltType) {
-                        // making some assumptions here
-                        case ZRenderStruct::kNoTrans: {
-                            Common::Point dest(current.destRect.left, current.destRect.top);
-                            if (current.sourceSurface) {
-                                _screen.blitFrom(*current.sourceSurface, current.sourceRect, dest);
-                            } else {
-                                _screen.blitFrom(*current.managedSource, current.sourceRect, dest);
-                            }
-                            break;
-                        }
-                        case ZRenderStruct::kTrans: {
-                            Common::Point dest(current.destRect.left, current.destRect.top);
-                            if (current.sourceSurface) {
-                                _screen.transBlitFrom(*current.sourceSurface, current.sourceRect, dest, transColor);
-                            } else {
-                                _screen.transBlitFrom(*current.managedSource, current.sourceRect, dest, transColor);
-                            }
-                            break;
-                        }
-                        default:
-                            error("Bad ZRender Blt type!");
-                    }
+            if (current._redrawFrom) {
+                if (current.hasMoved() && !current.getPreviousScreenPosition().isEmpty()) {
+                    // Redraw previous location if moved
+                    blitToScreen(*current._redrawFrom, current.getPreviousScreenPosition());
                 }
 
-                // Current struct has been rendered, remove from list
-                ids.remove_at(i);
-                --i;
+                if (current.getBlitType() == RenderObject::kTrans) {
+                    // Redraw below if transparent
+                    blitToScreen(*current._redrawFrom, current.getScreenPosition());
+                }
             }
-        }
-    }
-    _screen.update();
-}
 
-void GraphicsManager::loadBackgroundVideo(const Common::String &filename) {
-    if (_videoDecoder.isVideoLoaded()) {
-        _videoDecoder.close();
-    }
-    _videoDecoder.loadFile(filename + ".avf");
-}
+            // Draw the object itself
+            blitToScreen(current, current.getScreenPosition());
+        } else if (!current._isVisible && current._needsRedraw && current._redrawFrom && !current.getPreviousScreenPosition().isEmpty()) {
+            // Object just turned invisible, redraw below
+            blitToScreen(*current._redrawFrom, current.getPreviousScreenPosition());
+        }
 
-const Graphics::Surface *GraphicsManager::getBackgroundFrame(uint16 frameId)  {
-    if (!_videoDecoder.isVideoLoaded()) {
-        error("No video loaded");
-        return nullptr;
+        current._needsRedraw = false;
+        current._previousScreenPosition = current._screenPosition;
     }
-    return _videoDecoder.decodeFrame(frameId);
-}
 
-uint32 GraphicsManager::getBackgroundFrameCount() {
-    return _videoDecoder.getFrameCount();
-}
-
-uint32 GraphicsManager::getBackgroundWidth() {
-    return _videoDecoder.getWidth();
-}
-
-uint32 GraphicsManager::getBackgroundHeight() {
-    return _videoDecoder.getHeight();
-}
-
-void GraphicsManager::loadSecondaryVideo(uint channel, Common::String &filename, PlaySecondaryVideo *record) {
-    AVFDecoder &decoder = channels[channel].decoder;
-    if (decoder.isVideoLoaded()) {
-        decoder.close();
-    }
-    decoder.loadFile(filename + ".avf");
-    channels[channel].record = record;
+    // Draw the screen
+    _screen.update();
 }
 
-void GraphicsManager::setupSecondaryVideo(uint channel, uint16 begin, uint16 end, bool loop) {
-    channels[channel].beginFrame = begin;
-    channels[channel].endFrame = end;
-    channels[channel].loop = loop;
-    channels[channel].decoder.seekToFrame(begin);
+void GraphicsManager::addObject(RenderObject *object) {
+    _objects.insert(object);
 }
 
-void GraphicsManager::playSecondaryVideo(uint channel) {
-    AVFDecoder &decoder = channels[channel].decoder;
-    VideoChannel &chan = channels[channel];
-    if (!decoder.isVideoLoaded()) {
-        return;
-    }
-
-    // toggle between normal and reverse playback if needed
-    bool isReversed = chan.endFrame < chan.beginFrame;
-    bool wasReversed = decoder.getRate() < 0;
-    if (isReversed != wasReversed) {
-        decoder.setRate(-decoder.getRate());
-    }
-
-    if (!decoder.isPlaying()) {
-        decoder.start();
-        decoder.seekToFrame(chan.beginFrame);
-        
-        chan.surf.w = decoder.getWidth();
-        chan.surf.h = decoder.getHeight();
-        chan.surf.format = decoder.getPixelFormat();
-    }
-
-
-    if (decoder.needsUpdate()) {
-        chan.surf = *decoder.decodeNextFrame();
-    }
-    
-    // TODO loop is choppy and repeats a frame
-    if (decoder.getCurFrame() == chan.endFrame || decoder.endOfVideo()) {
-        if (chan.record->hoverState == PlaySecondaryVideo::kEndHover) {
-            chan.record->hoverState = PlaySecondaryVideo::kEndHoverDone;
-        }
-
-        if (chan.loop) {
-            decoder.seekToFrame(chan.beginFrame);
+void GraphicsManager::removeObject(RenderObject *object) {
+    for (auto &r : _objects) {
+        if (r == object) {
+            _objects.erase(&r);
         }
     }
 }
 
-void GraphicsManager::stopSecondaryVideo(uint channel) {
-    channels[channel].decoder.stop();
+void GraphicsManager::clearObjects() {
+    _objects.clear();
 }
 
-void GraphicsManager::loadSecondaryMovie(Common::String &filename) {
-    if (_secMovieDecoder.isVideoLoaded()) {
-        _secMovieDecoder.close();
-    }
-    _secMovieDecoder.loadFile(filename + ".avf");
-}
-
-bool GraphicsManager::playSecondaryMovie(uint16 &outFrameNr) {
-    if (!_secMovieDecoder.isPlaying()) {
-        _secMovieDecoder.start();
-        
-        _secMovieSurface.w = _secMovieDecoder.getWidth();
-        _secMovieSurface.h = _secMovieDecoder.getHeight();
-        _secMovieSurface.format = _secMovieDecoder.getPixelFormat();
-    }
-
-    outFrameNr = _secMovieDecoder.getCurFrame();
-    if (_secMovieDecoder.needsUpdate()) {
-        _secMovieSurface = *_secMovieDecoder.decodeNextFrame();
-        return true;
+void GraphicsManager::loadFonts() {
+    Common::SeekableReadStream *chunk = _engine->getBootChunkStream("FONT");
+    
+    chunk->seek(0);
+    while (chunk->pos() < chunk->size() - 1) {
+        _fonts.push_back(Font());
+        _fonts.back().read(*chunk, _engine);
     }
-
-    return false;
 }
 
-// Called when the order has changed
-void GraphicsManager::updateInvBox() {
-    Inventory &inventoryDesc = _engine->sceneManager->inventoryDesc;
-    for (uint i = 0; i < 4; ++i) {
-        if (i < inventoryBoxDesc.itemsOrder.size()) {
-            uint oi = inventoryBoxDesc.itemsOrder.size() - i - 1;
-            inventoryBoxDesc.onScreenItems[i].itemId = inventoryBoxDesc.itemsOrder[oi];
-            inventoryBoxDesc.onScreenItems[i].source = inventoryDesc.items[inventoryBoxDesc.itemsOrder[oi]].sourceRect;
-        } else {
-            inventoryBoxDesc.onScreenItems[i].itemId = -1;
-        }
-    }
-
-    // If size is 0 or 1, trigger the blinds closing/opening animation
-    if (inventoryBoxDesc.itemsOrder.size() < 2) {
-        inventoryBoxDesc.nextFrameTime = _engine->getTotalPlayTime() + inventoryDesc.shadesFrameTime;
+// Draw a given screen-space rectangle to the screen
+void GraphicsManager::blitToScreen(const RenderObject &src, Common::Rect screenRect) {
+    Common::Point pointDest(screenRect.left, screenRect.top);
+    if (src.getBlitType() == RenderObject::kNoTrans) {
+        _screen.blitFrom(src._drawSurface, src.convertToLocal(screenRect), pointDest);
+    } else if (src.getBlitType() == RenderObject::kTrans) {
+        _screen.transBlitFrom(src._drawSurface, src.convertToLocal(screenRect), pointDest, transColor);
     }
 }
 
-void GraphicsManager::renderFrame() {
-    ZRenderStruct &zr = getZRenderStruct("FRAME");
-    Common::Point dest(zr.destRect.left, zr.destRect.top);
-    _screen.blitFrom(*zr.sourceSurface, zr.sourceRect, dest);
-
-    // not sure why we do this
-    _numTimesRenderedFrame += 1;
-    if (_numTimesRenderedFrame > 1) {
-        _startingZ = 2;
-        _numTimesRenderedFrame = 0;
+int GraphicsManager::objectComparator(const void *a, const void *b) {
+	if (((const RenderObject*)a)->getZOrder() < ((const RenderObject*)b)->getZOrder()) {
+        return -1;
+    } else if (((const RenderObject*)a)->getZOrder() > ((const RenderObject*)b)->getZOrder()) {
+        return 1;
     } else {
-        _startingZ = 1;
-    }
-}
-
-void GraphicsManager::renderFrameInvBox() {
-    Inventory &inv = _engine->sceneManager->inventoryDesc;
-
-    // Draw the current four visible items if any
-    for (uint i = 0; i < 4; ++i) {
-        if (inventoryBoxDesc.onScreenItems[i].itemId != -1) {
-            Common::Point dest(inventoryBoxDesc.onScreenItems[i].dest.left, inventoryBoxDesc.onScreenItems[i].dest.top);
-            _screen.blitFrom(_inventoryBoxIconsSurface, inventoryBoxDesc.onScreenItems[i].source, dest);
-        }
-    }
-
-    // Check if we need to draw the next frame
-    if (inventoryBoxDesc.nextFrameTime != 0 &&
-        inventoryBoxDesc.nextFrameTime < _engine->getTotalPlayTime()) {
-            bool isInventoryEmpty = true;
-            for (uint i = 0; i < 11; ++i) {
-                if (_engine->playState.inventory.items[i] == PlayState::kTrue) {
-                    isInventoryEmpty = false;
-                    break;
-                }
-            }
-
-            inventoryBoxDesc.blindsAnimFrame += isInventoryEmpty ? 1 : -1;
-            if (inventoryBoxDesc.blindsAnimFrame < 0 || inventoryBoxDesc.blindsAnimFrame > 6) {
-                inventoryBoxDesc.blindsAnimFrame = CLIP<int16>(inventoryBoxDesc.blindsAnimFrame, 0, 6);
-                inventoryBoxDesc.nextFrameTime = 0;
-            } else {
-                inventoryBoxDesc.nextFrameTime = _engine->getTotalPlayTime() + _engine->sceneManager->inventoryDesc.shadesFrameTime;
-            }
-        }
-
-    // Draw the shades
-    if (inventoryBoxDesc.blindsAnimFrame < 7 &&
-        inventoryBoxDesc.blindsAnimFrame > 0) {
-        // Draw left shade
-        Common::Point dest(inv.shadesDst.left, inv.shadesDst.top);
-        _screen.blitFrom(_object0Surface, inv.shadesSrc[(6 - inventoryBoxDesc.blindsAnimFrame) * 2], dest);
-
-        // Draw right shade
-        dest.x = inv.shadesDst.right - inv.shadesSrc[(6 - inventoryBoxDesc.blindsAnimFrame) * 2 + 1].width();
-        _screen.blitFrom(_object0Surface, inv.shadesSrc[(6 - inventoryBoxDesc.blindsAnimFrame) * 2 + 1], dest);
+        return 0;
     }
 }
 
-void GraphicsManager::renderPrimaryVideo() {
-    ZRenderStruct &zr = getZRenderStruct("PRIMARY VIDEO");
-    if (!_primaryVideoDecoder.isPlaying()) {
-        _primaryVideoDecoder.start();
-        
-        _primaryVideoSurface.w = _secMovieDecoder.getWidth();
-        _primaryVideoSurface.h = _secMovieDecoder.getHeight();
-        _primaryVideoSurface.format = _secMovieDecoder.getPixelFormat();
-    }
-
-    if (_primaryVideoDecoder.needsUpdate()) {
-        _primaryVideoSurface = *_primaryVideoDecoder.decodeNextFrame();
-    }
-
-    Common::Point dest(zr.destRect.left, zr.destRect.top);
-     _screen.blitFrom(_primaryVideoSurface, zr.sourceRect, dest);
-}
-
-void GraphicsManager::renderSecVideo0() {
-    // TODO
-}
-
-void GraphicsManager::renderSecVideo1() {
-    // TODO
-}
-
-void GraphicsManager::renderSecMovie() {
-    // TODO
-}
-
-void GraphicsManager::renderOrderingPuzzle() {
-    // TODO
-}
-
-void GraphicsManager::renderRotatingLockPuzzle() {
-    // TODO
-}
-
-void GraphicsManager::renderLeverPuzzle() {
-    // TODO
-}
-
-void GraphicsManager::renderTelephone() {
-    // TODO
-}
-
-void GraphicsManager::renderSliderPuzzle() {
-    // TODO
-}
-
-void GraphicsManager::renderPasswordPuzzle() {
-    // TODO
-}
-
-} // End of namespace Nancy
\ No newline at end of file
+} // End of namespace Nancy
diff --git a/engines/nancy/graphics.h b/engines/nancy/graphics.h
index 144ad69070..4ae2d02531 100644
--- a/engines/nancy/graphics.h
+++ b/engines/nancy/graphics.h
@@ -23,158 +23,49 @@
 #ifndef NANCY_GRAPHICS_H
 #define NANCY_GRAPHICS_H
 
-#include "engines/nancy/nancy.h"
-#include "engines/nancy/video.h"
-#include "engines/nancy/datatypes.h"
-#include "engines/nancy/textbox.h"
+#include "engines/nancy/renderobject.h"
+#include "engines/nancy/font.h"
 
-#include "common/func.h"
-#include "common/stack.h"
+#include "common/array.h"
 
 #include "graphics/screen.h"
 
 namespace Nancy {
 
-class PlaySecondaryVideo;
-
-typedef Common::Functor0Mem<void, GraphicsManager> RenderFunction;
-
-struct ZRenderStruct {
-public:
-    enum BltType { kNone, kNoTrans, kTrans };
-
-    uint32 z = 0;
-    RenderFunction *renderFunction = nullptr;
-    Graphics::Surface *sourceSurface = nullptr;
-    Graphics::ManagedSurface *managedSource = nullptr;
-    Common::Rect sourceRect;
-    Common::Rect destRect;
-    bool isActive = false;
-    bool isInitialized = false;
-    BltType bltType = kNone;
-    Common::String name;
-};
-
-struct VideoChannel {
-        Graphics::Surface surf;
-        uint16 beginFrame = 0;
-        uint16 endFrame = 0;
-        bool loop = false;
-        PlaySecondaryVideo *record = nullptr;
-        AVFDecoder decoder;
-
-        ~VideoChannel() { surf.free(); decoder.close(); }
-};
+class NancyEngine;
 
+// Graphics class that handles multilayered surface rendering with minimal redraw
 class GraphicsManager {
 public:
-    struct InventoryBox {
-        struct Item {
-            Common::Rect source;
-            Common::Rect dest;
-            int16 itemId = -1;
-        };
-
-        // The generation of these coordinates is really long and stupid so
-        // just directly copy them for now
-        InventoryBox() {
-            onScreenItems[0].dest = Common::Rect(0x1B6, 0x152, 0x200, 0x18F);
-            onScreenItems[1].dest = Common::Rect(0x201, 0x152, 0x24B, 0x18F);
-            onScreenItems[2].dest = Common::Rect(0x1B6, 0x18F, 0x200, 0x1CC);
-            onScreenItems[3].dest = Common::Rect(0x201, 0x18F, 0x24B, 0x1CC);
-        }
-
-        Item onScreenItems[4];
-        Common::Array<uint16> itemsOrder;
-        int16 blindsAnimFrame = 6;
-
-        Time nextFrameTime;
-    };
-
-    GraphicsManager(NancyEngine *engine);
-    virtual ~GraphicsManager();
+    GraphicsManager(NancyEngine *engine) : _engine(engine), _objects(objectComparator) {}
 
     void init();
+    void draw();
 
-    void clearZRenderStruct(Common::String name);
-    void clearZRenderStructs();
-    ZRenderStruct &getZRenderStruct(Common::String name);
-    Common::String &initZRenderStruct( char const *name,
-                            uint32 z,
-                            bool isActive,
-                            ZRenderStruct::BltType bltType,
-                            Graphics::Surface *surface = nullptr,
-                            RenderFunction *func = nullptr,
-                            Common::Rect *sourceRect = nullptr,
-                            Common::Rect *destRect = nullptr );
-
-    virtual void initSceneZRenderStructs(Common::Array<Common::String> &outNames);
-    virtual void initMapRenderStructs(Common::Array<Common::String> &outNames);
-
-    void renderDisplay();
-    void renderDisplay(Common::Array<Common::String> ids);
-
-    void loadBackgroundVideo(const Common::String &filename);
-    const Graphics::Surface *getBackgroundFrame(uint16 frameId);
-    uint32 getBackgroundFrameCount();
-    uint32 getBackgroundWidth();
-    uint32 getBackgroundHeight();
-
-    void loadSecondaryVideo(uint channel, Common::String &filename, PlaySecondaryVideo *record);
-    void setupSecondaryVideo(uint channel, uint16 begin, uint16 end, bool loop);
-    void playSecondaryVideo(uint channel);
-    void stopSecondaryVideo(uint channel);
-    void loadSecondaryMovie(Common::String &filename);
-    bool playSecondaryMovie(uint16 &outFrameNr);
-
-    void updateInvBox();
-
-    Graphics::Surface _background;
-    Graphics::Surface _primaryFrameSurface;
-    Graphics::Surface _object0Surface;
-    Graphics::Surface _inventoryBoxIconsSurface;
-    Graphics::Surface _inventoryCursorsSurface;
-    Graphics::Surface _inventoryBitmapSurface;
-    Graphics::Surface _genericSurface;
-
-    VideoChannel channels[2];
-    Graphics::Surface _primaryVideoSurface;
-    AVFDecoder _primaryVideoDecoder;
-    Graphics::Surface _secMovieSurface;
-    AVFDecoder _secMovieDecoder;
-    Textbox _textbox;
-
-    View viewportDesc;
-    InventoryBox inventoryBoxDesc;
+    void addObject(RenderObject *object);
+    void removeObject(RenderObject *object);
+    void clearObjects();
 
+    Font *getFont(uint id) { return id < _fonts.size() ? &_fonts[id] : nullptr; }
+
+    Graphics::ManagedSurface object0;
+    
     static const Graphics::PixelFormat pixelFormat;
     static const uint transColor;
 
 private:
-    NancyEngine *_engine;
-    Common::HashMap<Common::String, ZRenderStruct> _ZRender;
-    Graphics::Screen _screen;
-    AVFDecoder _videoDecoder;
+    void loadFonts();
+    void blitToScreen(const RenderObject &src, Common::Rect dest);
 
-    uint _startingZ = 1;
-    uint _numTimesRenderedFrame = 0; // no idea why we're doing this
+    static int objectComparator(const void *a, const void *b);
 
-public:
-    // custom render functions
-    void renderFrame();
-    void renderFrameInvBox();
-    void renderPrimaryVideo();
-    void renderSecVideo0();
-    void renderSecVideo1();
-    void renderSecMovie();
-    void renderOrderingPuzzle();
-    void renderRotatingLockPuzzle();
-    void renderLeverPuzzle();
-    void renderTelephone();
-    void renderSliderPuzzle();
-    void renderPasswordPuzzle();
+    NancyEngine *_engine;
+    Common::SortedArray<RenderObject *> _objects;
+
+    Graphics::Screen _screen;
+    Common::Array<Font> _fonts;
 };
 
 } // End of namespace Nancy
 
-#endif // NANCY_GRAPHICS_H
\ No newline at end of file
+#endif // NANCY_GRAPHICS_H
diff --git a/engines/nancy/iff.cpp b/engines/nancy/iff.cpp
index 3fd6ceadea..ffd86d9ec8 100644
--- a/engines/nancy/iff.cpp
+++ b/engines/nancy/iff.cpp
@@ -27,6 +27,7 @@
 #include "common/memstream.h"
 #include "common/iff_container.h"
 #include "common/debug.h"
+#include "common/endian.h"
 
 namespace Nancy {
 
@@ -148,4 +149,4 @@ void IFF::list(Common::Array<Common::String> &nameList) {
 	}
 }
 
-} // End of namespace Nancy
\ No newline at end of file
+} // End of namespace Nancy
diff --git a/engines/nancy/iff.h b/engines/nancy/iff.h
index 7e5e75035f..c3d11981c1 100644
--- a/engines/nancy/iff.h
+++ b/engines/nancy/iff.h
@@ -69,4 +69,4 @@ private:
 
 } // End of namespace Nancy
 
-#endif // NANCY_IFF_H
\ No newline at end of file
+#endif // NANCY_IFF_H
diff --git a/engines/nancy/input.cpp b/engines/nancy/input.cpp
index 1586b37b8c..bc5fbd6cea 100644
--- a/engines/nancy/input.cpp
+++ b/engines/nancy/input.cpp
@@ -21,12 +21,8 @@
  */
 
 #include "engines/nancy/input.h"
-#include "engines/nancy/nancy.h"
-#include "engines/nancy/scene.h"
-#include "engines/nancy/graphics.h"
 
-#include "common/events.h"
-#include "common/keyboard.h"
+#include "engines/nancy/nancy.h"
 
 #include "backends/keymapper/action.h"
 #include "backends/keymapper/keymap.h"
@@ -34,177 +30,95 @@
 
 namespace Nancy {
 
-const int16 InputManager::mapButtonID               = 10000;
-const int16 InputManager::textBoxID                 = 10002;
-const int16 InputManager::textBoxScrollbarID        = 10003;
-const int16 InputManager::helpButtonID              = 10004;
-const int16 InputManager::menuButtonID              = 10005;
-const int16 InputManager::inventoryScrollbarID      = 10006;
-const int16 InputManager::inventoryItemTakeID       = 10007;
-const int16 InputManager::inventoryItemReturnID     = 10008;
-const int16 InputManager::orderingPuzzleID          = 10009;
-const int16 InputManager::orderingPuzzleEndID       = 10010;
-const int16 InputManager::rotatingLockPuzzleUpID    = 10011;
-const int16 InputManager::rotatingLockPuzzleDownID  = 10012;
-const int16 InputManager::rotatingLockPuzzleEndID   = 10013;
-const int16 InputManager::leverPuzzleID             = 10014; // not sure abt the lever ones
-const int16 InputManager::leverPuzzleEndID          = 10015;
-const int16 InputManager::telephoneID               = 10016;
-const int16 InputManager::telephoneEndID            = 10017;
-const int16 InputManager::sliderPuzzleID            = 10018;
-const int16 InputManager::sliderPuzzleEndID         = 10019;
-const int16 InputManager::passwordPuzzleEndID       = 10020;
-
 void InputManager::processEvents() {
     using namespace Common;
-
-    _inputs &= ~(kLeftMouseButtonUp | kRightMouseButtonUp);
-
     Common::Event event;
 
-    // TODO consider adding a keymapper
-    // TODO add debug key combos
+    _inputs &= ~(NancyInput::kLeftMouseButtonDown | NancyInput::kLeftMouseButtonUp | NancyInput::kRightMouseButtonDown | NancyInput::kRightMouseButtonUp);
+
     while (_engine->getEventManager()->pollEvent(event)) {
         switch (event.type) {
+            case EVENT_KEYDOWN:
+                if (event.kbd.keycode == KEYCODE_d && event.kbd.flags & Common::KBD_CTRL) {
+                    // Launch debug console
+                    _engine->launchConsole = true;
+                } else if (event.kbd.keycode == KEYCODE_q && event.kbd.flags & Common::KBD_CTRL) {
+                    // Quit
+                    _engine->quitGame();
+                }
+                break;
             case EVENT_CUSTOM_ENGINE_ACTION_START:
-                // TODO add debug shortcuts
                 switch (event.customType) {
+                    case kNancyActionLeftClick:
+                        _inputs |= NancyInput::kLeftMouseButtonDown;
+                        _inputs |= NancyInput::kLeftMouseButtonHeld;
+                        break;
+                    case kNancyActionRightClick:
+                        _inputs |= NancyInput::kRightMouseButtonDown;
+                        _inputs |= NancyInput::kRightMouseButtonHeld;
+                        break;
                     case kNancyActionMoveUp:
-                        _inputs |= kMoveUp;
-                        _engine->sceneManager->movementDirection |= SceneManager::kUp;
+                        _inputs |= NancyInput::kMoveUp;
                         break;
                     case kNancyActionMoveDown:
-                        _inputs |= kMoveDown;
-                        _engine->sceneManager->movementDirection |= SceneManager::kDown;
+                        _inputs |= NancyInput::kMoveDown;
                         break;
                     case kNancyActionMoveLeft:
-                        _inputs |= kMoveLeft;
-                        _engine->sceneManager->movementDirection |= SceneManager::kLeft;
+                        _inputs |= NancyInput::kMoveLeft;
                         break;
                     case kNancyActionMoveRight:
-                        _inputs |= kMoveRight;
-                        _engine->sceneManager->movementDirection |= SceneManager::kRight;
+                        _inputs |= NancyInput::kMoveRight;
                         break;
                     case kNancyActionMoveFast:
-                        _inputs |= kMoveFastModifier;
-                        break;
-                    case kNancyActionLeftClick:
-                        _inputs |= kLeftMouseButtonDown;
-                        break;
-                    case kNancyActionRightClick:
-                        _inputs |= kRightMouseButtonDown;
+                        _inputs |= NancyInput::kMoveFastModifier;
                         break;
                     default:
+                        // TODO handle debug key combos
                         break;
                 }
+
                 break;
             case EVENT_CUSTOM_ENGINE_ACTION_END:
                 switch (event.customType) {
+                    case kNancyActionLeftClick:
+                        _inputs |= NancyInput::kLeftMouseButtonUp;
+                        _inputs &= ~NancyInput::kLeftMouseButtonHeld;
+                        break;
+                    case kNancyActionRightClick:
+                        _inputs |= NancyInput::kRightMouseButtonUp;
+                        _inputs &= ~NancyInput::kRightMouseButtonHeld;
+                        break;
                     case kNancyActionMoveUp:
-                        _inputs &= ~kMoveUp;
+                        _inputs &= ~NancyInput::kMoveUp;
                         break;
                     case kNancyActionMoveDown:
-                        _inputs &= ~kMoveDown;
+                        _inputs &= ~NancyInput::kMoveDown;
                         break;
                     case kNancyActionMoveLeft:
-                        _inputs &= ~kMoveLeft;
+                        _inputs &= ~NancyInput::kMoveLeft;
                         break;
                     case kNancyActionMoveRight:
-                        _inputs &= ~kMoveRight;
+                        _inputs &= ~NancyInput::kMoveRight;
                         break;
                     case kNancyActionMoveFast:
-                        _inputs &= ~kMoveFastModifier;
-                        break;
-                    case kNancyActionLeftClick:
-                        _inputs &= ~kLeftMouseButtonDown;
-                        _inputs |= kLeftMouseButtonUp;
-                        break;
-                    case kNancyActionRightClick:
-                        _inputs &= ~kRightMouseButtonDown;
-                        _inputs |= kRightMouseButtonUp;
+                        _inputs &= ~NancyInput::kMoveFastModifier;
                         break;
                     default:
                         break;
                 }
-                break;
-            case EVENT_KEYDOWN: 
-                switch (event.kbd.keycode) {
-                    // Launch debug console
-                    case KEYCODE_d:
-                        if (event.kbd.flags & Common::KBD_CTRL) 
-                            _engine->launchConsole = true;
-                        break;
-                    // Quit
-                    case KEYCODE_q:
-                        if (event.kbd.flags & Common::KBD_CTRL) {
-                            _engine->quitGame();
-                            break;
-                        }
-                        break;
-                    default:
-                        break;
-                }
-                break;
-            case EVENT_MOUSEMOVE:
-                // TODO add frameMousePos
+
                 break;
             default:
                 break;
         }
     }
-
-    // Discard conflicting directions
-    byte dir = _engine->sceneManager->movementDirection;
-    if ((dir & SceneManager::kUp) && (dir & SceneManager::kDown)) {
-        _engine->sceneManager->movementDirection &= !(SceneManager::kUp | SceneManager::kDown);
-    }
-    if ((dir & SceneManager::kLeft) && (dir & SceneManager::kRight)) {
-        _engine->sceneManager->movementDirection &= !(SceneManager::kLeft | SceneManager::kRight);
-    }
-}
-
-bool InputManager::getInput(InputManager::InputType type) {
-    return _inputs & type;
-}
-
-void InputManager::clearInput() {
-    _inputs = 0;
-    hoveredElementID = -1;
-}
-
-Common::Point InputManager::getMousePosition() {
-    return _engine->getEventManager()->getMousePos();
 }
 
-void InputManager::setMousePosition(const Common::Point &newPos) {
-    // Hopefully having two mouse move events in the queue won't break my handling code
-    Common::Event newEvent;
-    newEvent.mouse = newPos;
-    newEvent.type = Common::EventType::EVENT_MOUSEMOVE;
-    _engine->getEventManager()->pushEvent(newEvent);
-}
-
-// Passing -1 means keeping the previous value
-// Style 0 is regular, 1 is red highlight, 2 is blue highlight, 4 is red highlight again
-void InputManager::setPointerBitmap(int16 id, int16 style, int16 isHoldingItem) {
-    ZRenderStruct &zr = _engine->graphics->getZRenderStruct("CUR IMAGE CURSOR");
-    
-    if (id != -1)
-        pointerId = id;
-    
-    if (style != -1)
-        pointerStyle = style;
-
-    if (isHoldingItem != -1)
-        itemHeld = (bool)isHoldingItem;
-
-    if (itemHeld) {
-        zr.sourceSurface = &_engine->graphics->_inventoryCursorsSurface;
-    } else {
-        zr.sourceSurface = &_engine->graphics->_object0Surface;
-    }
-
-    zr.sourceRect = cursorsData.rects[pointerId * 4 + pointerStyle];
+NancyInput InputManager::getInput() const {
+    NancyInput ret;
+    ret.mousePos = _engine->getEventManager()->getMousePos();
+    ret.input = _inputs;
+    return ret;
 }
 
 void InputManager::initKeymaps(Common::KeymapArray &keymaps) {
@@ -306,4 +220,4 @@ void InputManager::initKeymaps(Common::KeymapArray &keymaps) {
     keymaps.push_back(debugKeymap);
 }
 
-} // End of namespace Nancy
\ No newline at end of file
+} // End of namespace Nancy
diff --git a/engines/nancy/input.h b/engines/nancy/input.h
index dbce570e20..c65a39a9c9 100644
--- a/engines/nancy/input.h
+++ b/engines/nancy/input.h
@@ -23,14 +23,13 @@
 #ifndef NANCY_INPUT_H
 #define NANCY_INPUT_H
 
-#include "engines/nancy/datatypes.h"
+#include "engines/nancy/commontypes.h"
 
 #include "common/rect.h"
 
 namespace Common {
 template <class T>
 class Array;
-
 class Keymap;
 typedef class Array<Keymap*> KeymapArray;
 }
@@ -39,6 +38,32 @@ namespace Nancy {
 
 class NancyEngine;
 
+struct NancyInput {
+    enum InputType : uint16 {
+        kLeftMouseButtonDown    = 1 << 0,
+        kLeftMouseButtonHeld    = 1 << 1, // True while button is held
+        kLeftMouseButtonUp      = 1 << 2,
+        kRightMouseButtonDown   = 1 << 3,
+        kRightMouseButtonHeld   = 1 << 4, // True while button is held
+        kRightMouseButtonUp     = 1 << 5,
+        kMoveUp                 = 1 << 6,
+        kMoveDown               = 1 << 7,
+        kMoveLeft               = 1 << 8,
+        kMoveRight              = 1 << 9,
+        kMoveFastModifier       = 1 << 10,
+
+        kLeftMouseButton        = kLeftMouseButtonDown | kLeftMouseButtonHeld | kLeftMouseButtonUp,
+        kRightMouseButton       = kRightMouseButtonDown | kRightMouseButtonHeld | kRightMouseButtonUp   
+    };
+
+    Common::Point mousePos;
+    uint16 input;
+    
+    void eatMouseInput() { mousePos.x = -1; input &= ~(kLeftMouseButton | kRightMouseButton); }
+};
+
+// This class handles collecting events and translating them to a NancyInput object,
+// which can then be pulled by interested classes through getInput()
 class InputManager {
 enum NancyAction {
     kNancyActionMoveUp,
@@ -59,69 +84,21 @@ enum NancyAction {
 };
 
 public:
-enum InputType : uint16 {
-    kLeftMouseButtonDown    = 1 << 0,
-    kRightMouseButtonDown   = 1 << 1,
-    kLeftMouseButtonUp    = 1 << 2,
-    kRightMouseButtonUp   = 1 << 3,
-    kMoveUp             = 1 << 4,
-    kMoveDown           = 1 << 5,
-    kMoveLeft           = 1 << 6,
-    kMoveRight          = 1 << 7,
-    kMoveFastModifier   = 1 << 8
-};
+    InputManager(NancyEngine *engine) :
+        _engine(engine),
+        _inputs(0) {}
 
-    InputManager(NancyEngine *engine) : _engine(engine), _inputs(0), hoveredElementID(-1) {}
     void processEvents();
 
-    bool getInput(InputType type);
-    byte getInput() { return _inputs; }
-    void clearInput();
-    bool isClickValidLMB() { return hoveredElementID != -1 && _inputs & kLeftMouseButtonUp; }
-    bool isClickValidRMB() { return hoveredElementID != -1 && _inputs & kRightMouseButtonUp; }
-
-    Common::Point getMousePosition();
-    void setMousePosition(const Common::Point &newPos);
-    void setPointerBitmap(int16 id, int16 style, int16 itemHeld = 0);
+    NancyInput getInput() const;
 
     static void initKeymaps(Common::KeymapArray &keymaps);
-
-    int16 hoveredElementID;
-    Cursors cursorsData;
     
-    // TODO consider using a namespace for these
-    static const int16 mapButtonID;
-    static const int16 textBoxID;
-    static const int16 textBoxScrollbarID;
-    static const int16 helpButtonID;
-    static const int16 menuButtonID;
-    static const int16 inventoryScrollbarID;
-    static const int16 inventoryItemTakeID;
-    static const int16 inventoryItemReturnID;
-
-    static const int16 orderingPuzzleID;
-    static const int16 orderingPuzzleEndID;
-    static const int16 rotatingLockPuzzleUpID;
-    static const int16 rotatingLockPuzzleDownID;
-    static const int16 rotatingLockPuzzleEndID;
-    static const int16 leverPuzzleID;
-    static const int16 leverPuzzleEndID;
-    static const int16 telephoneID;
-    static const int16 telephoneEndID;
-    static const int16 sliderPuzzleID;
-    static const int16 sliderPuzzleEndID;
-    static const int16 passwordPuzzleEndID;
-
 private:
     NancyEngine *_engine;
-
-    int16 pointerId = 0;
-    int16 pointerStyle = 0;
-    bool itemHeld = false;
-
     uint16 _inputs;
 };
 
 } // End of namespace Nancy
 
-#endif // NANCY_INPUT_H
\ No newline at end of file
+#endif // NANCY_INPUT_H
diff --git a/engines/nancy/logic.cpp b/engines/nancy/logic.cpp
deleted file mode 100644
index e04f49c7ef..0000000000
--- a/engines/nancy/logic.cpp
+++ /dev/null
@@ -1,280 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "engines/nancy/logic.h"
-#include "engines/nancy/nancy.h"
-#include "engines/nancy/playstate.h"
-
-#include "common/memstream.h"
-
-namespace Nancy {
-
-bool Logic::addNewActionRecord(Common::SeekableReadStream &inputData) {
-    inputData.seek(0x30);
-    byte ARType = inputData.readByte();
-    ActionRecord *newRecord = createActionRecord(ARType);
-
-    inputData.seek(0);
-    char *descBuf = new char[0x30];
-    inputData.read(descBuf, 0x30);
-    newRecord->description = Common::String(descBuf);
-    delete[] descBuf;
-
-    newRecord->type = inputData.readByte(); // redundant
-    newRecord->execType = inputData.readByte();
-
-    uint16 localChunkSize = newRecord->readData(inputData);
-    localChunkSize += 0x32;
-
-    // If the localChunkSize is less than the total data, there must be dependencies at the end of the chunk
-    uint16 depsDataSize = (uint16)inputData.size() - localChunkSize;
-    if (depsDataSize > 0) {
-        // Each dependency is 0x0C bytes long (in v1)
-        newRecord->numDependencies = depsDataSize / 0xC;
-        if (depsDataSize % 0xC) {
-            error("Invalid dependency data size!");
-        }
-
-        newRecord->dependencies = new DependencyRecord[newRecord->numDependencies]();
-        newRecord->satisfiedDependencies = new bool[newRecord->numDependencies]();
-        newRecord->timers = new Time[newRecord->numDependencies]();
-        newRecord->orFlags = new bool[newRecord->numDependencies]();
-
-        // Initialize the dependencies data
-        inputData.seek(/*0x32 + */localChunkSize);
-        for (uint16 i = 0; i < newRecord->numDependencies; ++i) {
-            newRecord->dependencies[i].type = (DependencyType)inputData.readByte();
-            newRecord->dependencies[i].label = inputData.readByte();
-            newRecord->dependencies[i].condition = inputData.readByte();
-            newRecord->dependencies[i].orFlag = inputData.readByte();
-            newRecord->dependencies[i].hours = inputData.readSint16LE();
-            newRecord->dependencies[i].minutes = inputData.readSint16LE();
-            newRecord->dependencies[i].seconds = inputData.readSint16LE();
-            newRecord->dependencies[i].milliseconds = inputData.readSint16LE();
-        }
-
-        for (uint16 i = 0; i < newRecord->numDependencies; ++i) {
-            DependencyRecord &current = newRecord->dependencies[i];
-            if (current.type != 9 || current.hours != -1 || current.minutes != -1 || current.seconds != -1) {
-                newRecord->timers[i] = ((current.hours * 60 + current.minutes) * 60 + current.seconds) * 1000 + current.milliseconds;
-            }
-        }
-
-        for (uint16 i = 0; i < newRecord->numDependencies; ++i) {
-            if (newRecord->dependencies[i].orFlag == 1) {
-                newRecord->orFlags[i] = true;
-            } else {
-                newRecord->orFlags[i] = false;
-            }
-        }
-    } else {
-        // Set new record to active if it doesn't depend on anything
-        newRecord->isActive = true;
-    }
-
-    _records.push_back(newRecord);
-    return true;
-}
-
-void Logic::processActionRecords() {
-    ignorePrimaryVideo = false;
-    
-    for (auto record : _records) {
-        if (record->isDone) {
-            continue;
-        }
-
-        if (!record->isActive) {
-            if (record->numDependencies > 0) {
-                for (uint i = 0; i < record->numDependencies; ++i) {
-                    if (record->satisfiedDependencies[i] == 0) {
-                        DependencyRecord &dep = record->dependencies[i];
-                        switch (dep.type) {
-                            case kNone:
-                                record->satisfiedDependencies[i] = true;
-                                break;
-                            case kInventory:
-                                switch (dep.condition) {
-                                    case 1:
-                                        // Item not in possession or held
-                                        if (_engine->playState.inventory.items[dep.label] == PlayState::kFalse &&
-                                            dep.label != _engine->playState.inventory.heldItem) {
-                                            record->satisfiedDependencies[i] = true;
-                                        }
-                                        break;
-                                    case 2:
-                                        if (_engine->playState.inventory.items[dep.label] == PlayState::kTrue ||
-                                            dep.label == _engine->playState.inventory.heldItem) {
-                                            record->satisfiedDependencies[i] = true;
-                                        }
-                                        break;
-                                    default:
-                                        break;
-                                }
-                                break;
-                            case kEventFlag:
-                                if (_engine->playState.eventFlags[dep.label] == dep.condition)
-                                    // nancy1 has code for some timer array that never gets used
-                                    // and is discarded from nancy2 onward
-                                    record->satisfiedDependencies[i] = true;
-                                break;
-                            case kLogicCondition:
-                                if (_engine->playState.logicConditions[dep.label] == dep.condition) {
-                                    // Wait for specified time before satisfying dependency condition
-                                    Time elapsed = _engine->playState.totalTime - _engine->playState.logicConditionsTimestamps[dep.label];
-                                    if (elapsed >= record->timers[i])
-                                        record->satisfiedDependencies[i] = true;
-                                }
-                                break;
-                            case kTotalTime:
-                                if (_engine->playState.totalTime >= record->timers[i])
-                                    record->satisfiedDependencies[i] = true;
-                                break;
-                            case kSceneTime:
-                                if (_engine->playState.sceneTime >= record->timers[i])
-                                    record->satisfiedDependencies[i] = true;
-                                break;
-                            case kPlayerTime:
-                                if (_engine->playState.playerTime >= record->timers[i])
-                                    record->satisfiedDependencies[i] = true;
-                                break;
-                            /*case 7:
-                                // TODO
-                                break;
-                            case 8:
-                                // TODO
-                                break;*/
-                            case kSceneCount:
-                                // This dependency type keeps its data in the time variables
-                                // Also, I'm pretty sure it never gets used
-                                switch (dep.milliseconds) {
-                                    case 1:
-                                        if (dep.seconds < _engine->playState.sceneHitCount[dep.hours])
-                                            record->satisfiedDependencies[i] = true;
-                                        break;
-                                    case 2:
-                                        if (dep.seconds > _engine->playState.sceneHitCount[dep.hours])
-                                            record->satisfiedDependencies[i] = true;
-                                        break;
-                                    case 3:
-                                        if (dep.seconds == _engine->playState.sceneHitCount[dep.hours])
-                                            record->satisfiedDependencies[i] = true;
-                                        break;
-                                }
-                                break;
-                            case kResetOnNewDay:
-                                if (record->days == -1) {
-                                    record->days = _engine->playState.playerTime.getDays();
-                                    record->satisfiedDependencies[i] = true;
-                                    break;
-                                }
-
-                                if (record->days < _engine->playState.playerTime.getDays()) {
-                                    record->days = _engine->playState.playerTime.getDays();
-                                    for (uint j = 0; j < record->numDependencies; ++j) {
-                                        if (record->dependencies[j].type == kPlayerTime) {
-                                            record->satisfiedDependencies[j] = false;
-                                        }
-                                    }
-                                }
-                                break;
-                            case kUseItem: {
-                                bool hasUnsatisfiedDeps = false;
-                                if (record->numDependencies > 1) {
-                                    for (uint j = 0; j < record->numDependencies; ++j) {
-                                        if (j != i && record->satisfiedDependencies[j] == false) {
-                                            hasUnsatisfiedDeps = true;
-                                        }
-                                    }
-                                }
-
-                                if (hasUnsatisfiedDeps) {
-                                    break;
-                                }
-
-                                record->itemRequired = dep.label;
-
-                                if (dep.condition == 1) {
-                                    record->itemRequired += 100;
-                                }
-                                
-                                record->satisfiedDependencies[i] = true;
-                                break;
-                            }
-                            case kTimeOfDay:
-                                if (dep.label == (byte)_engine->playState.timeOfDay)
-                                    record->satisfiedDependencies[i] = true;
-                                break;
-                            case kTimerNotDone:
-                                if (_engine->playState.timerTime <= record->timers[i])
-                                    record->satisfiedDependencies[i] = true;
-                                break;
-                            case kTimerDone:
-                                if (_engine->playState.timerTime > record->timers[i])
-                                    record->satisfiedDependencies[i] = true;
-                                break;
-                            case kDifficultyLevel:
-                                if (dep.condition == _engine->playState.difficulty)
-                                    record->satisfiedDependencies[i] = true;
-                                break;
-                            default:
-                                break;
-                        }
-                    }
-                }
-
-                // An orFlag marks that its corresponding dependency and the one after it
-                // mutually satisfy each other; if one is satisfied, so is the other
-                for (int i = 1; i < record->numDependencies; ++i) {
-                    if (record->orFlags[i-1]) {
-                        if (record->satisfiedDependencies[i-1])
-                            record->satisfiedDependencies[i] = true;
-                        if (record->satisfiedDependencies[i])
-                            record->satisfiedDependencies[i-1] = true;
-                    }
-                }
-
-                // Check if all dependencies have been satisfied, and activate the record if they have
-                uint satisfied = 0;
-                for (uint i = 0; i < record->numDependencies; ++i) {
-                    if (record->satisfiedDependencies[i])
-                        ++satisfied;
-                }
-
-                if (satisfied == record->numDependencies)
-                    record->isActive = true;
-            }
-        }
-
-        if (record->isActive) {
-            if (record->type != 0x32 || !ignorePrimaryVideo) { 
-                record->execute(_engine);
-            }
-        }
-    }
-}
-
-void Logic::clearActionRecords() {
-    _records.clear();
-}
-
-} // End of namespace Nancy
\ No newline at end of file
diff --git a/engines/nancy/map.cpp b/engines/nancy/map.cpp
deleted file mode 100644
index 5e0c62f87a..0000000000
--- a/engines/nancy/map.cpp
+++ /dev/null
@@ -1,188 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "engines/nancy/map.h"
-#include "engines/nancy/graphics.h"
-#include "engines/nancy/resource.h"
-#include "engines/nancy/audio.h"
-#include "engines/nancy/scene.h"
-#include "engines/nancy/input.h"
-
-namespace Nancy {
-
-void Map::process() {
-    switch (_state) {
-        case kInit:
-            init();
-            break;
-        case kRun:
-            run();
-            break;
-    }
-}
-
-void Map::init() {
-    Common::SeekableReadStream *chunk = _engine->getBootChunkStream("MAP");
-    if (_decoder.isVideoLoaded()) {
-        _decoder.close();
-    }
-
-    if (_engine->playState.eventFlags[40] == PlayState::kTrue &&
-        _engine->playState.eventFlags[95] == PlayState::kTrue) {
-        _mapID = 1;
-    } else {
-        _mapID = 0;
-    }
-
-    // Load the video
-    chunk->seek(_mapID * 10, SEEK_SET);
-    char name[10];
-    chunk->read(name, 10);
-    Common::String n(name);
-    _decoder.loadFile(n + ".avf");
-    _mapImage = *_decoder.decodeNextFrame();
-
-    // Load the audio
-    chunk->seek(0x18 + _mapID * 0x20, SEEK_SET);
-    chunk->read(name, 10);
-    n = Common::String(n);
-    uint16 channel = chunk->readUint16LE();
-    chunk->skip(0xA);
-    uint16 volume = chunk->readUint16LE();
-    _engine->sound->loadSound(n, channel, 0, volume);
-    _engine->sound->pauseSound(channel, false);
-
-    for (uint i = 0; i < 4; ++i) {
-        chunk->seek(0x162 + i * 16, SEEK_SET);
-        _locations.push_back(Location());
-        Location &h = _locations[i];
-        h.hotspot.left = chunk->readUint32LE();
-        h.hotspot.top = chunk->readUint32LE();
-        h.hotspot.right = chunk->readUint32LE();
-        h.hotspot.bottom = chunk->readUint32LE();
-
-        if (_mapID == 1 && (i % 2) != 0) {
-            h.isActive = false;
-        } else {
-            h.isActive = true;
-        }
-
-        for (uint j = 0; j < 2; ++j) {
-            h.scenes.push_back(Location::SceneChange());
-            Location::SceneChange &sc = h.scenes[j];
-            chunk->seek(0x1BE + 6 * i * (j + 1), SEEK_SET);
-            sc.sceneID = chunk->readUint16LE();
-            sc.frameID = chunk->readUint16LE();
-            sc.verticalOffset = chunk->readUint16LE();
-        }
-
-        chunk->seek(0x9A + i * 16, SEEK_SET);
-        h.labelSrc.left = chunk->readUint32LE();
-        h.labelSrc.top = chunk->readUint32LE();
-        h.labelSrc.right = chunk->readUint32LE();
-        h.labelSrc.bottom = chunk->readUint32LE();
-
-        // TODO this gets initialized using MAP and the textbox's on-screen location
-        // but the code is annoyingly long so fpr now i just directly write the result
-        h.labelDest = Common::Rect(0x56, 0x166, 0x15E, 0x19B);
-        
-    }
-
-    ZRenderStruct &zr = _engine->graphics->getZRenderStruct("VIEWPORT AVF");
-    zr.sourceSurface = &_mapImage;
-
-    _engine->graphics->initMapRenderStructs(_ZRenderFilter);
-	_engine->_gameFlow.previousGameState = NancyEngine::kMap;
-    _state = kRun;
-}
-
-void Map::run() {
-    handleMouse();
-    int16 hover = _engine->input->hoveredElementID;
-    ZRenderStruct &labels = _engine->graphics->getZRenderStruct("MAP LABELS");
-    if (hover != -1 && hover < 10000) {
-        labels.isActive = true;
-        labels.sourceRect = _locations[hover].labelSrc;
-        labels.destRect = _locations[hover].labelDest;
-    } else {
-        labels.isActive = false;
-    }
-
-    if (_engine->input->isClickValidLMB()) {
-        if (_engine->sceneManager->stateChangeRequests & SceneManager::kMap ||
-                hover == InputManager::mapButtonID) {
-            _state = kInit;
-            _engine->_gameFlow.minGameState = NancyEngine::kScene;
-            _engine->_gameFlow.previousGameState = NancyEngine::kMap;
-            _engine->sceneManager->stateChangeRequests &= ~NancyEngine::kMap;
-            return;
-        }
-
-        Location::SceneChange &sc = _locations[hover].scenes[_mapID];
-        _engine->sceneManager->changeScene(sc.sceneID, sc.frameID, sc.verticalOffset, false);
-        _state = kInit;
-        _engine->_gameFlow.minGameState = NancyEngine::kScene;
-        _engine->_gameFlow.previousGameState = NancyEngine::kMap;
-    }
-
-    _engine->graphics->renderDisplay(_ZRenderFilter);
-}
-
-void Map::handleMouse() {
-    ZRenderStruct &zr = _engine->graphics->getZRenderStruct("CUR IMAGE CURSOR");
-    Common::Point mousePos = _engine->input->getMousePosition();
-    zr.destRect.left = zr.destRect.right = mousePos.x;
-    zr.destRect.top = zr.destRect.bottom = mousePos.y;
-    _engine->input->hoveredElementID = -1;
-
-    View &view = _engine->graphics->viewportDesc;
-
-    // TODO incorrect magic number, figure out where this comes from
-    Common::Point viewportMouse = mousePos + Common::Point(10, 10);
-
-    if (view.destination.contains(viewportMouse)) {
-        _engine->input->setPointerBitmap(0, 0, 0);
-        for (uint i = 0; i < _locations.size(); ++i) {
-            // Adjust the hotspot coordinates
-            Common::Rect hs = _locations[i].hotspot;
-            hs.left += view.destination.left;
-            hs.top += view.destination.top;
-            hs.right += view.destination.left;
-            hs.bottom += view.destination.top;
-
-            if (_locations[i].isActive && hs.contains(viewportMouse)) {
-                _engine->input->hoveredElementID = i;
-                _engine->input->setPointerBitmap(-1, 1, 0);   
-                break;             
-            }
-        }
-    } else {
-        if (_engine->graphics->getZRenderStruct("MAP ANIM").destRect.contains(mousePos)) {
-            _engine->input->hoveredElementID = InputManager::mapButtonID;
-            _engine->input->setPointerBitmap(1, 2, -1);
-        } else {
-            _engine->input->setPointerBitmap(1, 1, 0);
-        }
-    }
-}
-
-} // End of namespace Nancy
\ No newline at end of file
diff --git a/engines/nancy/metaengine.cpp b/engines/nancy/metaengine.cpp
index a4c4571349..16d4016d22 100644
--- a/engines/nancy/metaengine.cpp
+++ b/engines/nancy/metaengine.cpp
@@ -206,4 +206,4 @@ void NancyMetaEngine::removeSaveState(const char *target, int slot) const {
     REGISTER_PLUGIN_DYNAMIC(NANCY, PLUGIN_TYPE_ENGINE, NancyMetaEngine);
 #else
     REGISTER_PLUGIN_STATIC(NANCY, PLUGIN_TYPE_ENGINE, NancyMetaEngine);
-#endif
\ No newline at end of file
+#endif
diff --git a/engines/nancy/module.mk b/engines/nancy/module.mk
index c7ecfabf0c..0447cf32b7 100644
--- a/engines/nancy/module.mk
+++ b/engines/nancy/module.mk
@@ -1,24 +1,32 @@
 MODULE := engines/nancy
 
 MODULE_OBJS = \
-  action/recordtypes.o \
+  action/actionmanager.o \
   action/arfactory_v1.o \
   action/primaryvideo.o \
+  action/recordtypes.o \
+  action/secondaryvideo.o \
+  action/staticbitmapanim.o \
+  ui/frame.o \
+  ui/inventorybox.o \
+  ui/scrollbar.o \
+  ui/textbox.o \
+  ui/viewport.o \
+  state/logo.o \
+  state/map.o \
+  state/scene.o\
   audio.o \
   console.o \
-  datatypes.o \
+  cursor.o \
   decompress.o \
+  font.o \
   graphics.o \
   iff.o \
   input.o \
-  logic.o \
-  logo.o \
-  map.o \
   metaengine.o \
   nancy.o \
+  renderobject.o \
   resource.o \
-  scene.o \
-  textbox.o \
   video.o
 
 # This module can be built as a plugin
diff --git a/engines/nancy/nancy.cpp b/engines/nancy/nancy.cpp
index 2f43eb3164..e64f147ea0 100644
--- a/engines/nancy/nancy.cpp
+++ b/engines/nancy/nancy.cpp
@@ -20,17 +20,18 @@
  *
  */
 
+#include "engines/nancy/state/logo.h"
+#include "engines/nancy/state/scene.h"
+
 #include "engines/nancy/nancy.h"
 #include "engines/nancy/resource.h"
 #include "engines/nancy/iff.h"
 #include "engines/nancy/audio.h"
-#include "engines/nancy/logic.h"
-#include "engines/nancy/logo.h"
-#include "engines/nancy/scene.h"
-#include "engines/nancy/graphics.h"
 #include "engines/nancy/input.h"
 #include "engines/nancy/audio.h"
-#include "engines/nancy/map.h"
+#include "engines/nancy/state/map.h"
+#include "engines/nancy/graphics.h"
+#include "engines/nancy/cursor.h"
 
 #include "common/system.h"
 #include "common/random.h"
@@ -75,13 +76,13 @@ NancyEngine::NancyEngine(OSystem *syst, const NancyGameDescription *gd) :
 	_rnd = new Common::RandomSource("Nancy");
 	_rnd->setSeed(_rnd->getSeed());
 
-	_logoSequence = new LogoSequence(this);
-	logic = new Logic(this);
-	sceneManager = new SceneManager(this);
-	map = new Map(this);
-	graphics = new GraphicsManager(this);
+	logo = new State::Logo(this);
+	scene = new State::Scene(this);
+	map = new State::Map(this);
 	input = new InputManager(this);
 	sound = new SoundManager(this);
+	graphicsManager = new GraphicsManager(this);
+	cursorManager = new CursorManager(this);
 
 	launchConsole = false;
 }
@@ -92,10 +93,9 @@ NancyEngine::~NancyEngine() {
 	delete _console;
 	delete _rnd;
 	
-	delete logic;
-	delete sceneManager;
+	delete scene;
 	delete map;
-	delete graphics;
+	delete graphicsManager;
 	delete input;
 	delete sound;
 }
@@ -142,7 +142,6 @@ Common::Error NancyEngine::run() {
 	if (cab)
 		SearchMan.add("data1.hdr", cab);
 
-//	_mouse = new MouseHandler(this);
 	_res = new ResourceManager(this);
 	_res->initialize();
 
@@ -152,25 +151,23 @@ Common::Error NancyEngine::run() {
 	_gameFlow.minGameState = kBoot;
 
 	while (!shouldQuit()) {
+		cursorManager->setCursorType(CursorManager::kNormalArrow);
 		input->processEvents();
-		if (_gameFlow.minGameState != _gameFlow.previousGameState) {
-			input->clearInput();
-		}
-
 		switch (_gameFlow.minGameState) {
 		case kBoot:
 			bootGameEngine();
-			graphics->init();
-			_gameFlow.minGameState = kLogo;
+			graphicsManager->init();
+			cursorManager->init();
+			setGameState(kLogo);
 			break;
 		case kLogo:
-			_logoSequence->process();
+			logo->process();
 			break;
 		case kMainMenu:
 			// TODO
 			break;
 		case kScene:
-			sceneManager->process();
+			scene->process();
 			break;
 		case kMap:
 			map->process();
@@ -180,6 +177,13 @@ Common::Error NancyEngine::run() {
 			break;
 		}
 
+		graphicsManager->draw();
+
+		if (_gameFlow.justChanged) {
+			_gameFlow.justChanged = false;
+		} else {
+			_gameFlow.previousGameState = _gameFlow.minGameState;
+		}
 
 		if (launchConsole) {
 			_console->attach();
@@ -217,8 +221,6 @@ void NancyEngine::bootGameEngine() {
 		addBootChunk(n, boot->getChunkStream(n));
 	}
 
-	input->cursorsData.read(*getBootChunkStream("CURS"));
-
 	// The FR, LG and OB chunks get added here	
 
 	Common::SeekableReadStream *font = getBootChunkStream("FONT");
@@ -227,9 +229,6 @@ void NancyEngine::bootGameEngine() {
 	}
 	
 	// TODO reset some vars
-
-	graphics->clearZRenderStructs();
-
 	// TODO reset some more vars
 
 	delete boot;
@@ -330,6 +329,16 @@ void NancyEngine::readImageList(const IFF &boot, const Common::String &prefix, I
 	}
 }
 
+void NancyEngine::setGameState(GameState state) {
+	_gameFlow.previousGameState = _gameFlow.minGameState;
+	_gameFlow.minGameState = state;
+	_gameFlow.justChanged = true;
+
+	// Do not erase the frame if we're switching to the map
+	// This makes the labels not crash the game
+	graphicsManager->clearObjects();
+}
+
 class NancyEngine_v0 : public NancyEngine {
 public:
 	NancyEngine_v0(OSystem *syst, const NancyGameDescription *gd) : NancyEngine(syst, gd) { }
@@ -342,7 +351,9 @@ private:
 void NancyEngine_v0::readBootSummary(const IFF &boot) {
 	Common::SeekableReadStream *bsum = getBootChunkStream("BSUM");
 	bsum->seek(0xa3);
-	_firstSceneID = bsum->readUint16LE();
+	firstSceneID = bsum->readUint16LE();
+	bsum->skip(4);
+	startTimeHours = bsum->readUint16LE(); // this is a whole Time struct but we just take the hours for now
 	bsum->seek(0x151);
 	readImageList(boot, "FR", _frames);
 	readImageList(boot, "LG", _logos);
@@ -350,7 +361,13 @@ void NancyEngine_v0::readBootSummary(const IFF &boot) {
 	bsum->seek(0x1D1);
 	_fontSize = bsum->readSint32LE() * 1346;
 	bsum->seek(0x1ED);
-	sceneManager->playerTimeMinuteLength = bsum->readSint16LE();
+	scene->playerTimeMinuteLength = bsum->readSint16LE();
+	bsum->seek(0x1F1);
+    overrideMovementTimeDeltas = bsum->readByte();
+    if (overrideMovementTimeDeltas) {
+        slowMovementTimeDelta = bsum->readUint16LE();
+        fastMovementTimeDelta = bsum->readUint16LE();
+    }
 }
 
 class NancyEngine_v1 : public NancyEngine_v0 {
@@ -364,7 +381,7 @@ private:
 void NancyEngine_v1::readBootSummary(const IFF &boot) {
 	Common::SeekableReadStream *bsum = getBootChunkStream("BSUM");
 	bsum->seek(0xa3);
-	_firstSceneID = bsum->readUint16LE();
+	firstSceneID = bsum->readUint16LE();
 	bsum->seek(0x14b);
 	readImageList(boot, "FR", _frames);
 	readImageList(boot, "LG", _logos);
@@ -399,4 +416,4 @@ NancyEngine *NancyEngine::create(GameType type, OSystem *syst, const NancyGameDe
 	}
 }
 
-} // End of namespace Nancy
\ No newline at end of file
+} // End of namespace Nancy
diff --git a/engines/nancy/nancy.h b/engines/nancy/nancy.h
index 5c89143429..1e5f1c80fc 100644
--- a/engines/nancy/nancy.h
+++ b/engines/nancy/nancy.h
@@ -25,10 +25,11 @@
 
 #include "nancy/console.h"
 #include "nancy/detection.h"
-#include "nancy/playstate.h"
+#include "nancy/time.h"
 
 #include "engines/engine.h"
 #include "common/file.h"
+#include "common/str.h"
 
 namespace Common {
 class RandomSource;
@@ -64,33 +65,47 @@ struct NancyGameDescription;
 
 class ResourceManager;
 class IFF;
-class LogoSequence;
-class SceneManager;
-class Map;
-class Logic;
-class GraphicsManager;
 class InputManager;
 class SoundManager;
+class GraphicsManager;
+class CursorManager;
+
+namespace State {
+class Logo;
+class Scene;
+class Map;
+}
 
 class NancyEngine : public Engine {
 public:
-	friend class Logic;
-	friend class LogoSequence;
-	friend class SceneManager;
 	friend class NancyConsole;
-	friend class Map;
+	friend class State::Logo; // TODO
+
+	enum GameState {
+		kBoot,
+		kPartnerLogo,
+		kLogo,
+		kCredits,
+		kMap,
+		kMainMenu,
+		kLoadSave,
+		kSetup,
+		// unknown/invalid
+		kHelp,
+		kScene,
+		// CD change
+		kCheat,
+		kQuit,
+		// regain focus
+		kIdle,
+		kReloadSave
+	};
 
 	NancyEngine(OSystem *syst, const NancyGameDescription *gd);
 	~NancyEngine();
 
-	OSystem *_system;
-
 	GUI::Debugger *getDebugger();
 
-	Common::RandomSource *_rnd;
-
-
-	const NancyGameDescription *_gameDescription;
 	uint32 getFeatures() const;
 	const char *getGameId() const;
 
@@ -107,22 +122,36 @@ public:
 
 	static NancyEngine *create(GameType type, OSystem *syst, const NancyGameDescription *gd);
 
-	bool launchConsole;
-	
 	// Chunks found in BOOT get extracted and cached at startup, this function lets other classes access them
 	Common::SeekableReadStream *getBootChunkStream(const Common::String &name);
+	
+	void setGameState(GameState state);
+	GameState getGameState() const { return _gameFlow.minGameState; }
+	GameState getPreviousGameState() const { return _gameFlow.previousGameState; }
 
 	// Managers
 	ResourceManager *_res;
-	Logic *logic;
-	SceneManager *sceneManager;
-	Map *map;
-	GraphicsManager *graphics;
+	GraphicsManager *graphicsManager;
+	CursorManager *cursorManager;
 	InputManager *input;
 	SoundManager *sound;
+
+	// States
+	State::Logo *logo;
+	State::Scene *scene;
+	State::Map *map;
+	
+	OSystem *_system;
+	Common::RandomSource *_rnd;
+	const NancyGameDescription *_gameDescription;
+	bool launchConsole;
 	
-	// Contains all player data
-    PlayState playState;
+	uint16 firstSceneID;
+	uint16 startTimeHours;
+
+	bool overrideMovementTimeDeltas;
+	Time slowMovementTimeDelta;
+	Time fastMovementTimeDelta;
 
 protected:
 	// Engine APIs
@@ -143,28 +172,11 @@ protected:
 		uint16 height;
 	};
 
-	enum GameState {
-		kBoot,
-		kPartnerLogo, // v2 only
-		kLogo,
-		kCredits,
-		kMap, // v0, v1 only
-		kMainMenu,
-		kLoadSave,
-		kSetup,
-		// unknown/invalid
-		kHelp,
-		kScene,
-		// CD change
-		kCheat,
-		kQuit,
-		// regain focus
-		kIdle
-	};
 
 	struct GameFlow {
 		GameState minGameState;
 		GameState previousGameState;
+		bool justChanged = false;
 	};
 
 	typedef Common::Array<Image> ImageList;
@@ -172,9 +184,7 @@ protected:
 	ImageList _logos;
 	ImageList _frames;
 	ImageList _objects;
-	uint16 _firstSceneID;
 	int32 _fontSize;
-	GameFlow _gameFlow;
 
 	void preloadCals(const IFF &boot);
 	void readImageList(const IFF &boot, const Common::String &prefix, ImageList &list);
@@ -183,17 +193,19 @@ protected:
 	virtual uint getFilenameLen() const = 0;
 	virtual void readBootSummary(const IFF &boot) = 0;
 
+
 private:
 	static NancyEngine *s_Engine;
 
+	GameFlow _gameFlow;
+
 	NancyConsole *_console;
 	GameType _gameType;
 	Common::Platform _platform;
 
-	LogoSequence *_logoSequence;
 	Common::HashMap<Common::String, Common::SeekableReadStream *> _bootChunks;
 };
 
 } // End of namespace Nancy
 
-#endif // NANCY_H
\ No newline at end of file
+#endif // NANCY_H
diff --git a/engines/nancy/playstate.h b/engines/nancy/playstate.h
deleted file mode 100644
index 9a352757e5..0000000000
--- a/engines/nancy/playstate.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef NANCY_PLAYSTATE_H
-#define NANCY_PLAYSTATE_H
-
-#include "engines/nancy/time.h"
-
-namespace Nancy {
-
-// A catch-all struct for storing all player progress and related variables
-// TODO split to PlayerState/SceneState
-struct PlayState {
-    enum Flag { kFalse = 1, kTrue = 2 }; // Could be the other way around
-    enum TimeOfDay { kDay, kNight, kDuskDawn };
-
-    struct Inventory {
-        Inventory() { for (uint i = 0; i < 11; ++i) items[i] = kFalse; }
-        Flag items[11];
-        int16 heldItem = -1;
-    };
-
-    // These two are used to keep track of what options the player picked in the
-    // current conversation. Since they don't get stored they probably shouldn't
-    // be here
-    Flag logicConditions[30];
-    Time logicConditionsTimestamps[30]; // Stores when the condition got satisfied
-    Inventory inventory;
-    Flag eventFlags[168];
-    byte sceneHitCount[1000];
-    uint16 difficulty; // 0, 1, 2
-    Time totalTime;
-    Time sceneTime;
-    Time timerTime;
-    bool timerIsActive = false;
-    Time playerTime; // Nancy's in-game time of day, adds a minute every 5 seconds
-    Time playerTimeNextMinute; // Stores the next tick count until we add a minute to playerTime
-    TimeOfDay timeOfDay = kDay;
-    int16 currentViewFrame = 0;
-    int16 lastDrawnViewFrame = -1; 
-    int16 queuedViewFrame = 0; // Used when changing scenes
-    uint16 currentMaxVerticalScroll = 0;
-    uint16 queuedMaxVerticalScroll = 0;
-    uint16 verticalScroll = 0; // This replaces rDisplayed
-    uint16 lastVerticalScroll = 0;
-    uint16 verticalScrollDelta = 0;
-};
-
-} // End of namespace Nancy
-
-#endif // NANCY_PLAYSTATE_H
\ No newline at end of file
diff --git a/engines/nancy/renderobject.cpp b/engines/nancy/renderobject.cpp
new file mode 100644
index 0000000000..311c32c9ec
--- /dev/null
+++ b/engines/nancy/renderobject.cpp
@@ -0,0 +1,118 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/nancy/renderobject.h"
+
+#include "engines/nancy/nancy.h"
+#include "engines/nancy/graphics.h"
+#include "engines/nancy/input.h"
+#include "engines/nancy/state/scene.h"
+#include "engines/nancy/ui/viewport.h"
+
+#include "common/stream.h"
+
+namespace Nancy {
+
+void RenderObject::init() {
+    _previousScreenPosition = _screenPosition;
+}
+
+void RenderObject::registerGraphics() {
+    _engine->graphicsManager->addObject(this);
+}
+
+RenderObject::~RenderObject() {
+    _engine->graphicsManager->removeObject(this);
+    if (_drawSurface.getPixels()) {
+        _drawSurface.free();
+    }
+}
+
+void RenderObject::moveTo(Common::Point position) {
+    _previousScreenPosition = _screenPosition;
+    _screenPosition.moveTo(position);
+    _needsRedraw = true;
+}
+
+void RenderObject::setVisible(bool visible) {
+    _isVisible = visible;
+    _needsRedraw = true;
+}
+
+Common::Rect RenderObject::getScreenPosition() const {
+    if (isViewportRelative()) {
+        return _engine->scene->getViewport().convertViewportToScreen(_screenPosition);
+    } else {
+        return _screenPosition;
+    }
+}
+
+Common::Rect RenderObject::getPreviousScreenPosition() const {
+    if (isViewportRelative()) {
+        return _engine->scene->getViewport().convertViewportToScreen(_previousScreenPosition);
+    } else {
+        return _previousScreenPosition;
+    }
+}
+
+// Convert from screen to local space. Does NOT take _drawSurface's offset into account
+Common::Rect RenderObject::convertToLocal(const Common::Rect &screen) const {
+    Common::Rect ret = screen;
+    Common::Point offset;
+
+    if (isViewportRelative()) {
+        Common::Rect viewportScreenPos = _engine->scene->getViewport().getScreenPosition();
+        offset.x -= viewportScreenPos.left;
+        offset.y -= viewportScreenPos.top;
+        uint viewportScroll = _engine->scene->getViewport().getCurVerticalScroll();
+        offset.y += viewportScroll;
+    }
+
+    offset.x -= _screenPosition.left;
+    offset.y -= _screenPosition.top;
+
+    ret.translate(offset.x, offset.y);
+    return ret;
+}
+
+// Convert from local to screen space. Does NOT take _drawSurface's offset into account
+Common::Rect RenderObject::convertToScreen(const Common::Rect &rect) const {
+
+    Common::Rect ret = rect;
+    Common::Point offset;
+
+    if (isViewportRelative()) {
+        Common::Rect viewportScreenPos = _engine->scene->getViewport().getScreenPosition();
+        offset.x += viewportScreenPos.left;
+        offset.y += viewportScreenPos.top;
+        uint viewportScroll = _engine->scene->getViewport().getCurVerticalScroll();
+        offset.y -= viewportScroll;
+    }
+
+    offset.x += _screenPosition.left;
+    offset.y += _screenPosition.top;
+
+    ret.translate(offset.x, offset.y);
+    return ret;
+}
+
+} // End of namespace Nancy
diff --git a/engines/nancy/renderobject.h b/engines/nancy/renderobject.h
new file mode 100644
index 0000000000..c9e7641bb9
--- /dev/null
+++ b/engines/nancy/renderobject.h
@@ -0,0 +1,100 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef NANCY_RENDEROBJECT_H
+#define NANCY_RENDEROBJECT_H
+
+#include "engines/nancy/commontypes.h"
+
+#include "common/rect.h"
+#include "common/keyboard.h"
+
+#include "graphics/managed_surface.h"
+
+namespace Nancy {
+
+class NancyEngine;
+
+// Loosely equivalent to the original engine's ZRenderStructs.
+// A subclass of this will be automatically updated and drawn from the graphics manager,
+// but initialization needs to be done manually. Objects are expected to know which
+// object is below them at creation.
+class RenderObject {
+    friend class GraphicsManager;
+public:
+    enum BlitType { kNoTrans, kTrans };
+
+    RenderObject(NancyEngine *engine) :
+        _engine(engine),
+        _needsRedraw(true),
+        _isVisible(true),
+        _redrawFrom(nullptr) {}
+
+    RenderObject(RenderObject &redrawFrom) :
+        _engine(redrawFrom._engine),
+        _needsRedraw(true),
+        _isVisible(true),
+        _redrawFrom(&redrawFrom) {}
+
+    virtual ~RenderObject();
+
+    virtual void init(); // Does _not_ get called automatically
+    virtual void registerGraphics();
+    virtual void updateGraphics() {};
+
+    void moveTo(Common::Point position);
+    void setVisible(bool visible);
+
+    bool hasMoved() const { return _previousScreenPosition != _screenPosition; }
+    Common::Rect getScreenPosition() const;
+    Common::Rect getPreviousScreenPosition() const;
+
+    // Given a screen-space rect, convert it to the source surface's local space
+    Common::Rect convertToLocal(const Common::Rect &screen) const;
+    // Given a local space rect, convert it to screen space
+    Common::Rect convertToScreen(const Common::Rect &rect) const;
+
+	Common::Rect getBounds() const { return Common::Rect(_drawSurface.w, _drawSurface.h); }
+
+protected:
+    // Z order and blit type are extracted directly from the corresponding
+    // ZRenderStruct from the original engine
+    virtual uint16 getZOrder() const =0;
+    virtual BlitType getBlitType() const = 0;
+
+    // Needed for proper handling of objects inside the viewport
+    virtual bool isViewportRelative() const { return false; }
+
+    NancyEngine *_engine;
+
+    RenderObject *_redrawFrom;
+
+    Graphics::ManagedSurface _drawSurface;
+    Common::Rect _screenPosition;
+    bool _needsRedraw;
+    bool _isVisible;
+    Common::Rect _previousScreenPosition;
+};
+
+} // End of namespace Nancy
+
+#endif // NANCY_RENDEROBJECT_H
diff --git a/engines/nancy/resource.cpp b/engines/nancy/resource.cpp
index 1fe57e1373..3aa25c958c 100644
--- a/engines/nancy/resource.cpp
+++ b/engines/nancy/resource.cpp
@@ -719,9 +719,6 @@ bool ResourceManager::loadImage(const Common::String &treeName, const Common::St
 	surf.w = info.width;
 	surf.h = info.height;
 	surf.pitch = info.pitch;
-	// Surface's conversion functions do not work the exact way as this
-	//use after fixing pixel format
-	//colorCorrect(buf, info.size);
 	surf.setPixels(buf);
 	surf.format = GraphicsManager::pixelFormat;
 	return true;
@@ -758,14 +755,4 @@ Common::String ResourceManager::getCifDescription(const Common::String &treeName
 	return desc;
 }
 
-void ResourceManager::colorCorrect(byte *buf, uint size) {
-	byte *last = buf + size;
-	for (byte *cur = buf; cur < last; cur += 2) {
-		uint16 pre = *cur | (*(cur+1) << 8);
-		uint16 post = ((pre & 0xFFE0) << 1) | (pre & 0x1F);
-		*cur = post & 0xFF;
-		*(cur+1) = post >> 8;
-	}
-}
-
-} // End of namespace Nancy
\ No newline at end of file
+} // End of namespace Nancy
diff --git a/engines/nancy/resource.h b/engines/nancy/resource.h
index 121cc8ce7d..eb35c7e379 100644
--- a/engines/nancy/resource.h
+++ b/engines/nancy/resource.h
@@ -81,11 +81,9 @@ private:
 	bool getCifInfo(const Common::String &treeName, const Common::String &name, CifInfo &info);
 	const CifTree *findCifTree(const Common::String &name) const;
 
-	void colorCorrect(byte *buf, uint size);
-
 	Common::Array<const CifTree *> _cifTrees;
 };
 
 } // End of namespace Nancy
 
-#endif // NANCY_RESOURCE_H
\ No newline at end of file
+#endif // NANCY_RESOURCE_H
diff --git a/engines/nancy/scene.cpp b/engines/nancy/scene.cpp
deleted file mode 100644
index 8ed4b87708..0000000000
--- a/engines/nancy/scene.cpp
+++ /dev/null
@@ -1,816 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "engines/nancy/scene.h"
-#include "engines/nancy/nancy.h"
-#include "engines/nancy/resource.h"
-#include "engines/nancy/iff.h"
-#include "engines/nancy/logic.h"
-#include "engines/nancy/graphics.h"
-#include "engines/nancy/input.h"
-#include "engines/nancy/audio.h"
-
-#include "common/memstream.h"
-#include "common/rect.h"
-#include "common/func.h"
-
-#include "graphics/surface.h"
-
-namespace Nancy {
-
-void SceneManager::process() {
-    switch (_state) {
-    case kInit:
-        init();
-        break;
-    case kLoad:
-        load();
-        break;
-    case kStartSound:
-        _state = kRun;
-        if (!doNotStartSound) {
-            _engine->sound->stopAllSounds();
-            _engine->sound->loadSound(currentScene.audioFile, currentScene.audioID, 0, currentScene.audioVolume);
-            _engine->sound->pauseSound(currentScene.audioID, false);
-        }
-        // fall through
-    case kRun:
-        run();
-        break;
-    case kLoadNew:
-        _state = kLoad;
-        break;
-    }
-}
-
-void SceneManager::changeScene(uint16 id, uint16 frame, uint16 verticalOffset, bool noSound) {
-    _sceneID = id;
-    _engine->playState.queuedViewFrame = frame;
-    _engine->playState.queuedMaxVerticalScroll = verticalOffset;
-    doNotStartSound = noSound;
-    _state = kLoadNew;
-}
-
-void SceneManager::pushScene() {
-    _pushedSceneID = _sceneID;
-    _pushedFrameID = _engine->playState.currentViewFrame;
-    _pushedVerticalScroll = _engine->playState.verticalScroll;
-}
-
-void SceneManager::popScene() {
-    changeScene(_pushedSceneID, _pushedFrameID, _pushedVerticalScroll, true);
-    _pushedSceneID = 10000;
-}
-
-void SceneManager::addObjectToInventory(uint16 id) {
-    if (_engine->playState.inventory.heldItem == id) {
-        _engine->playState.inventory.heldItem = -1;
-    }
-
-    GraphicsManager::InventoryBox &box = _engine->graphics->inventoryBoxDesc;
-    _engine->playState.inventory.items[id] = PlayState::kTrue;
-    box.itemsOrder.push_back(id);
-
-    // Update the inventory box
-    _engine->graphics->updateInvBox();
-}
-
-void SceneManager::removeObjectFromInventory(uint16 id, bool pickUp) {
-    Common::Array<uint16> &order = _engine->graphics->inventoryBoxDesc.itemsOrder;
-    Common::Array<uint16> temp;
-
-    _engine->playState.inventory.items[id] = PlayState::kFalse;
-
-    // Pop elements from the order array until we find the correct one
-    for (int i = order.size() - 1; i >= 0; --i) {
-        uint16 thisElem = order.back();
-        order.pop_back();
-        if (thisElem == id) {
-            if (pickUp) {
-                _engine->playState.inventory.heldItem = id;
-            }
-            break;
-        } else {
-            temp.push_back(thisElem);
-        }
-    }
-
-    // Return all elements in the same order except the one we removed
-    for (int i = temp.size() - 1; i >= 0; --i) {
-        order.push_back(temp[i]);
-    }
-
-    // Update the inventory box
-    _engine->graphics->updateInvBox();
-}
-
-void SceneManager::init() {
-    for (uint i = 0; i < 168; ++i) {
-        _engine->playState.eventFlags[i] = PlayState::Flag::kFalse;
-    }
-
-    for (uint i = 0; i < 1000; ++i) {
-        _engine->playState.sceneHitCount[i] = 0;
-    }
-
-    _sceneID = _engine->_firstSceneID;
-    _engine->_gameFlow.previousGameState = NancyEngine::kScene;
-    
-    _engine->graphics->clearZRenderStructs();
-
-    // Load the primary frame
-    if (!_engine->_res->loadImage("ciftree", _engine->_frames[0].name, _engine->graphics->_primaryFrameSurface)) {
-        error("Failed to load %s", _engine->_frames[0].name.c_str());
-    }
-
-    char *name = new char[10];
-
-    // Load the Object 0 image
-    if (!_engine->_res->loadImage("ciftree", _engine->_objects[0].name, _engine->graphics->_object0Surface)) {
-        error("Failed to load %s", _engine->_objects[0].name.c_str());
-    }
-
-    // Load inventory
-    inventoryDesc.read(*_engine->getBootChunkStream("INV"));
-    if (!_engine->_res->loadImage("ciftree", inventoryDesc.inventoryBoxIconsImageName, _engine->graphics->_inventoryBoxIconsSurface)) {
-        error("Failed to load inventory icons (TOOL)");
-    }
-
-    // Load the cursors
-    if (!_engine->_res->loadImage("ciftree", inventoryDesc.inventoryCursorsImageName, _engine->graphics->_inventoryCursorsSurface)) {
-        error("Failed to load cursors (TOOLCUR1)");
-    }
-
-    delete[] name;
-
-    _engine->graphics->initSceneZRenderStructs(_ZRenderFilter);
-
-    // Set the scroll bar destinations
-    Common::SeekableReadStream *tbox = _engine->getBootChunkStream("TBOX");
-    tbox->seek(0x30);
-    ZRenderStruct &tbzr = _engine->graphics->getZRenderStruct("CUR TB BAT SLIDER");
-    tbzr.destRect.left =  tbox->readUint16LE() - (tbzr.sourceRect.width() / 2); // coords in file are for center position
-    tbzr.destRect.top =  tbox->readUint16LE();
-    tbzr.destRect.right = tbzr.destRect.left + tbzr.sourceRect.width();
-    tbzr.destRect.bottom = tbzr.destRect.top + tbzr.sourceRect.height();
-
-    ZRenderStruct &invzr = _engine->graphics->getZRenderStruct("CUR INV SLIDER");
-    invzr.destRect.left = inventoryDesc.sliderDefaultDest.x - (invzr.sourceRect.width() / 2); // coords in file are for center position
-    invzr.destRect.top = inventoryDesc.sliderDefaultDest.y;
-    invzr.destRect.right = invzr.destRect.left + invzr.sourceRect.width();
-    invzr.destRect.bottom = invzr.destRect.top + invzr.sourceRect.height();
-
-    _state = kLoad;
-}
-
-void SceneManager::load() {
-    clearSceneData();
-
-    // Scene IDs are prefixed with S inside the cif tree; e.g 100 -> S100                                                                                    
-    Common::String sceneName = Common::String::format("S%u", _sceneID);
-    IFF sceneIFF(_engine, sceneName);
-	if (!sceneIFF.load()) {
-		error("Faled to load IFF %s", sceneName.c_str());
-	}
-
-    Common::SeekableReadStream *sceneSummaryChunk = sceneIFF.getChunkStream("SSUM");
-    if (!sceneSummaryChunk) {
-        error("Invalid IFF Chunk SSUM");
-    }
-
-    currentScene = SceneSummary();
-    currentScene.read(*sceneSummaryChunk);
-
-    // The check to see if we need to switch the CD is performed here
-
-    Common::SeekableReadStream *bsum = _engine->getBootChunkStream("BSUM");
-    bsum->seek(0x1F1);
-    byte overrideMovementDeltas = bsum->readByte();
-    if (overrideMovementDeltas) {
-        currentScene.slowMoveTimeDelta = bsum->readUint16LE();
-        currentScene.fastMoveTimeDelta = bsum->readUint16LE();
-    }
-
-    // Search for Action Records, maximum for a scene is 30
-    Common::SeekableReadStream *actionRecordChunk = nullptr;
-
-    while (actionRecordChunk = sceneIFF.getChunkStream("ACT", _engine->logic->_records.size()), actionRecordChunk != nullptr)
-    {
-        if (_engine->logic->_records.size() >= 30) {
-            error("Invalid number of Action Records");
-        }
-
-        _engine->logic->addNewActionRecord(*actionRecordChunk);
-    }
-    _engine->graphics->loadBackgroundVideo(currentScene.videoFile);
-    if (_engine->graphics->getBackgroundFrameCount() == 1) {
-        currentScene.horizontalEdgeSize = 0;
-    }
-
-    View &viewportDesc = _engine->graphics->viewportDesc;
-
-    if (!hasLoadedFromSavefile) {
-        _engine->playState.currentMaxVerticalScroll = _engine->playState.queuedMaxVerticalScroll;
-        _engine->playState.currentViewFrame = _engine->playState.queuedViewFrame;
-
-        if (currentScene.videoFormat == 1) {
-            // TODO not sure this ever gets hit
-        } else if (currentScene.videoFormat == 2) {
-            // always start from the bottom
-            _engine->playState.verticalScroll = _engine->playState.currentMaxVerticalScroll;
-            _engine->playState.verticalScrollDelta = currentScene.verticalScrollDelta;
-        } else {
-            error("Unrecognized Scene summary chunk video file format");
-        }
-
-        // Some checks against rFrame
-
-        if (currentScene.videoFormat == 1) {
-            // TODO not sure this ever gets hit
-        } else if (currentScene.videoFormat == 2) {
-            if (_engine->graphics->getBackgroundHeight() == (uint32)viewportDesc.f2Dest.bottom + 1) {
-                currentScene.verticalEdgeSize = 0;
-            }
-        }
-    }
-
-    // Redraw the viewport
-    _engine->playState.lastDrawnViewFrame = -1;
-    _engine->input->setPointerBitmap(0, 0, false);
-
-    _state = kStartSound;
-}
-
-void SceneManager::run() {
-    if (isComingFromMenu) {
-        // TODO
-    }
-    isComingFromMenu = false;
-
-    if (orderingPuzzleIsActive)
-        _engine->graphics->getZRenderStruct("ORDERING PUZZLE").isActive = true;
-    if (rotatingLockPuzzleIsActive)
-        _engine->graphics->getZRenderStruct("ROTATING LOCK PUZZLE").isActive = true;
-    if (leverPuzzleIsActive)
-        _engine->graphics->getZRenderStruct("LEVER PUZZLE").isActive = true;
-    if (sliderPuzzleIsActive)
-        _engine->graphics->getZRenderStruct("SLIDER PUZZLE").isActive = true;
-    if (passwordPuzzleIsActive)
-        _engine->graphics->getZRenderStruct("PASSWORD PUZZLE").isActive = true;
-    if (telephoneIsActive)
-        _engine->graphics->getZRenderStruct("TELEPHONE").isActive = true;
-
-    if (stateChangeRequests & kHelpMenu) {
-        _stashedTickCount = _engine->getTotalPlayTime();
-        _engine->_gameFlow.previousGameState = NancyEngine::GameState::kScene;
-        _engine->_gameFlow.minGameState = NancyEngine::GameState::kHelp;
-        // _engine->helpMenu->state = HelpMenu::State::kInit;
-        stateChangeRequests &= ~kHelpMenu;
-        // _engine->sound->stopSceneSpecificSounds();
-        return;
-    }
-
-    if (stateChangeRequests & kMainMenu) {
-        _stashedTickCount = _engine->getTotalPlayTime();
-        _engine->_gameFlow.previousGameState = NancyEngine::GameState::kScene;
-        _engine->_gameFlow.minGameState = NancyEngine::GameState::kMainMenu;
-        // _engine->mainMenu->state = MainMenu::State::kInit;
-        stateChangeRequests &= ~kMainMenu;
-        // _engine->sound->stopSceneSpecificSounds();
-        return;
-    }
-
-    if (stateChangeRequests & kSaveLoad) {
-        _stashedTickCount = _engine->getTotalPlayTime();
-        _engine->_gameFlow.previousGameState = NancyEngine::GameState::kScene;
-        _engine->_gameFlow.minGameState = NancyEngine::GameState::kLoadSave;
-        // _engine->loadSaveMenu->state = LoadSaveMenu::State::kInit;
-        stateChangeRequests &= ~kSaveLoad;
-        // _engine->sound->stopSceneSpecificSounds();
-        return;
-    }
-
-    if (stateChangeRequests & kReloadSave) {
-        // TODO
-    }
-
-    if (stateChangeRequests & kSetupMenu) {
-        _stashedTickCount = _engine->getTotalPlayTime();
-        _engine->_gameFlow.previousGameState = NancyEngine::GameState::kScene;
-        _engine->_gameFlow.minGameState = NancyEngine::GameState::kSetup;
-        // _engine->setupMenu->state = setupMenu::State::kInit;
-        stateChangeRequests &= ~kSetupMenu;
-        // _engine->sound->stopSceneSpecificSounds();
-        return;
-    }
-
-    if (stateChangeRequests & kCredits) {
-        _stashedTickCount = _engine->getTotalPlayTime();
-        _engine->_gameFlow.previousGameState = NancyEngine::GameState::kScene;
-        _engine->_gameFlow.minGameState = NancyEngine::GameState::kHelp;
-        // _engine->credits->state = CreditsSequence::State::kInit;
-        stateChangeRequests &= ~kCredits;
-        // _engine->sound->stopSceneSpecificSounds();
-        return;
-    }
-
-    if (stateChangeRequests & kMap) {
-        _stashedTickCount = _engine->getTotalPlayTime();
-        _engine->_gameFlow.previousGameState = NancyEngine::GameState::kScene;
-        _engine->_gameFlow.minGameState = NancyEngine::GameState::kMap;
-        // _engine->map->state = Map::State::kInit;
-        stateChangeRequests &= ~kMap;
-        _engine->sound->stopAllSounds();
-        return;
-    }
-
-    // Cheat menu, will not implement
-
-    uint32 playTimeThisFrame = _engine->getTotalPlayTime();
-
-    // Do some work if we're coming from a different game state
-    if (_engine->_gameFlow.previousGameState != NancyEngine::GameState::kScene) {
-        if (hasLoadedFromSavefile) {
-            if (playTimeThisFrame > _stashedTickCount) {
-                playTimeThisFrame -= _stashedTickCount;
-                _engine->playState.totalTime -= playTimeThisFrame;
-                _engine->playState.sceneTime -= playTimeThisFrame;
-                if (_engine->playState.timerIsActive)
-                    _engine->playState.timerTime -= playTimeThisFrame;
-            }
-        }
-            
-        _engine->graphics->getZRenderStruct("MENU BTN DN").isActive = false;
-        _engine->graphics->getZRenderStruct("HELP BTN DN").isActive = false;
-        _engine->input->hoveredElementID = -1;
-        // TODO a bunch of function calls
-        _engine->_gameFlow.previousGameState = NancyEngine::GameState::kScene;
-
-        // Viewport gets reused, set it back to the correct surface
-        ZRenderStruct &zr = _engine->graphics->getZRenderStruct("VIEWPORT AVF");
-        zr.sourceSurface = &_engine->graphics->_background;
-        zr.sourceRect = _engine->graphics->viewportDesc.source;
-        _engine->playState.lastDrawnViewFrame = -1;
-        return;
-    }
-
-    uint32 diff = 0;
-    if (_tickCount < playTimeThisFrame) {
-        diff = playTimeThisFrame - _tickCount;
-        _tickCount = playTimeThisFrame;
-    }
-    _engine->playState.totalTime += diff;
-    if (_engine->playState.timerIsActive)
-        _engine->playState.timerTime += diff;
-    _engine->playState.sceneTime =+ diff;
-
-    // Calculate the in-game time (playerTime)
-    if (playTimeThisFrame > _engine->playState.playerTimeNextMinute) {
-        _engine->playState.playerTime += 60000; // Add a minute
-        _engine->playState.playerTimeNextMinute = playTimeThisFrame + playerTimeMinuteLength; // Set when we're going to add the next minute
-    }
-
-    // Set the time of day according to playerTime
-    if (_engine->playState.playerTime.getHours_alt() >= 7 && _engine->playState.playerTime.getHours_alt() < 18) {
-        _engine->playState.timeOfDay = _engine->playState.kDay;
-    } else if ((_engine->playState.playerTime.getHours_alt() >= 19 || _engine->playState.playerTime.getHours_alt() < 6)) {
-        _engine->playState.timeOfDay = _engine->playState.kNight;
-    } else {
-        _engine->playState.timeOfDay = _engine->playState.kDuskDawn;
-    }
-
-    if (_engine->input->isClickValidLMB()) {
-        if (orderingPuzzleIsActive) {
-            // TODO
-        }
-
-        if (sliderPuzzleIsActive) {
-            // TODO
-        }
-
-        if (orderingPuzzleIsActive) {
-            // TODO
-        }
-
-        if (telephoneIsActive) {
-            // TODO
-        }
-
-        if (leverPuzzleIsActive) {
-            // TODO   
-        }
-
-        if (passwordPuzzleIsActive) {
-
-        }
-
-        int16 &hovered = _engine->input->hoveredElementID;
-
-        if (hovered == InputManager::mapButtonID) {
-            // TODO another if
-            stateChangeRequests |= kMap;
-            return;
-        } else if (hovered == InputManager::textBoxID) {
-            int16 picked = _engine->graphics->_textbox.getHovered(_engine->input->getMousePosition());
-            if (picked != -1) {
-                // clear logic conditions and timestamps
-                for (uint i = 0; i < 30; ++i) {
-                    _engine->playState.logicConditions[i] = PlayState::kFalse;
-                    _engine->playState.logicConditionsTimestamps[i] = 0;
-                }
-
-                _engine->playState.logicConditions[picked] = PlayState::kTrue;
-                _engine->playState.logicConditionsTimestamps[picked] = playTimeThisFrame;
-            }
-        } else if (hovered == InputManager::inventoryItemTakeID) {
-            GraphicsManager::InventoryBox &box = _engine->graphics->inventoryBoxDesc;
-            Common::Point mousePos = _engine->input->getMousePosition();
-            for (uint i = 0; i < 4; ++i) {
-                if (box.onScreenItems[i].dest.contains(mousePos)) {
-                    removeObjectFromInventory(box.onScreenItems[i].itemId, true);
-                    break;
-                }
-            }
-        } else if (hovered == InputManager::inventoryItemReturnID) {
-            addObjectToInventory(_engine->playState.inventory.heldItem);
-        } else if (hovered == InputManager::textBoxScrollbarID) {
-            handleScrollbar(0);
-        } else if (hovered == InputManager::inventoryScrollbarID) {
-            handleScrollbar(1);
-        } else if (hovered == InputManager::menuButtonID) {
-            // TODO
-        } else if (hovered == InputManager::helpButtonID) {
-            // TODO
-        } else if (hovered == InputManager::orderingPuzzleID ||
-                    hovered == InputManager::orderingPuzzleEndID ||
-                    hovered == InputManager::rotatingLockPuzzleUpID ||
-                    hovered == InputManager::rotatingLockPuzzleDownID ||
-                    hovered == InputManager::rotatingLockPuzzleEndID ||
-                    hovered == InputManager::leverPuzzleID ||
-                    hovered == InputManager::leverPuzzleEndID ||
-                    hovered == InputManager::telephoneID ||
-                    hovered == InputManager::telephoneEndID ||
-                    hovered == InputManager::sliderPuzzleID ||
-                    hovered == InputManager::sliderPuzzleEndID ||
-                    hovered == InputManager::passwordPuzzleEndID) {
-            // TODO
-        } else {
-            // Not a UI element, ID must be an action record's
-            ActionRecord *rec = _engine->logic->getActionRecord(hovered);
-            if (rec->isActive /*&& another condition !- 0*/) {
-                bool shouldTrigger = false;
-                int16 &heldItem = _engine->playState.inventory.heldItem;
-                if (rec->itemRequired != -1) {
-                    if (heldItem == -1 && rec->itemRequired == -2) {
-                        shouldTrigger = true;
-                    } else {
-                        if (rec->itemRequired <= 100) {
-                            if (heldItem == rec->itemRequired) {
-                                shouldTrigger = true;
-                            }
-                        } else if (rec->itemRequired <= 110 && rec->itemRequired - 100 != heldItem) {
-                            // IDs 100 - 110 mean the record will activate when the object is _not_ the specified one
-                            shouldTrigger = true;
-                        }
-                    }
-                } else {
-                    shouldTrigger = true;
-                }
-                if (shouldTrigger) {
-                    rec->state = ActionRecord::ExecutionState::kActionTrigger;
-                    
-                    if (rec->itemRequired > 100 && rec->itemRequired <= 110) {
-                        rec->itemRequired -= 100;
-                    }
-
-                    // Re-add the object to the inventory unless it's marked as a one-time use
-                    if (rec->itemRequired == heldItem && rec->itemRequired != -1) {
-                        if (inventoryDesc.items[heldItem].oneTimeUse != 0) {
-                            addObjectToInventory(heldItem);
-                        }
-
-                        heldItem = -1;
-                    }
-                }
-                
-            }
-        }
-
-
-    } else if (_engine->input->isClickValidRMB()) {
-        if (_engine->input->hoveredElementID == InputManager::textBoxScrollbarID) {
-            _engine->graphics->_textbox.setPosition(handleScrollbar(0, true));
-        } else if (_engine->input->hoveredElementID == InputManager::inventoryScrollbarID) {
-            // TODO, moves scrollbar one line up
-        } else if (_engine->input->hoveredElementID == InputManager::textBoxID) {
-            // TODO
-        }
-    } else {
-        // Perform movement
-        byte inputs = _engine->input->getInput();
-        if  ( ( (
-                    (inputs & InputManager::kLeftMouseButtonDown) != (inputs & InputManager::kRightMouseButtonDown) 
-                ) ||
-                (inputs & (InputManager::kMoveUp | InputManager::kMoveDown | InputManager::kMoveLeft | InputManager::kMoveRight) )
-              ) &&
-                movementDirection != 0 &&
-                playTimeThisFrame > _nextBackgroundMovement
-            ) {
-            switch(movementDirection) {
-                case kLeft:
-                    _engine->playState.currentViewFrame += 1;
-                    if (_engine->playState.currentViewFrame >= (int16)_engine->graphics->getBackgroundFrameCount()) {
-                        _engine->playState.currentViewFrame = 0;
-                    }
-                    break;
-                case kRight:
-                    _engine->playState.currentViewFrame -= 1;
-                    if (_engine->playState.currentViewFrame < 0) {
-                        _engine->playState.currentViewFrame = (int16)_engine->graphics->getBackgroundFrameCount() -1;
-                    }
-                    break;
-                case kUp:
-                    if (_engine->playState.verticalScroll != 0) {
-                        int16 newScroll = _engine->playState.verticalScroll - _engine->playState.verticalScrollDelta;
-                        _engine->playState.verticalScroll = MAX(newScroll, (int16)0);
-                    }
-                    break;
-                case kDown:
-                    if (_engine->playState.verticalScroll != _engine->playState.currentMaxVerticalScroll) {
-                        uint16 newScroll = _engine->playState.verticalScroll + _engine->playState.verticalScrollDelta;
-                        _engine->playState.verticalScroll = MIN(newScroll, _engine->playState.currentMaxVerticalScroll);
-                    }
-                    break;
-                case kUp | kLeft:
-                    if (_engine->playState.verticalScroll != 0) {
-                        int16 newScroll = _engine->playState.verticalScroll - _engine->playState.verticalScrollDelta;
-                        _engine->playState.verticalScroll = MAX(newScroll, (int16)0);
-                    }
-                    _engine->playState.currentViewFrame += 1;
-                    if (_engine->playState.currentViewFrame >= (int16)_engine->graphics->getBackgroundFrameCount()) {
-                        _engine->playState.currentViewFrame = 0;
-                    }
-                    break;
-                case kUp | kRight:
-                    if (_engine->playState.verticalScroll != 0) {
-                        int16 newScroll = _engine->playState.verticalScroll - _engine->playState.verticalScrollDelta;
-                        _engine->playState.verticalScroll = MAX(newScroll, (int16)0);
-                    }
-                    _engine->playState.currentViewFrame -= 1;
-                    if (_engine->playState.currentViewFrame < 0) {
-                        _engine->playState.currentViewFrame = _engine->graphics->getBackgroundFrameCount() - 1;
-                    }
-                    break;
-                case kDown | kLeft:
-                    if (_engine->playState.verticalScroll != _engine->playState.currentMaxVerticalScroll) {
-                        uint16 newScroll = _engine->playState.verticalScroll + _engine->playState.verticalScrollDelta;
-                        _engine->playState.verticalScroll = MIN(newScroll, _engine->playState.currentMaxVerticalScroll);
-                    }
-                    _engine->playState.currentViewFrame += 1;
-                    if (_engine->playState.currentViewFrame >= (int16)_engine->graphics->getBackgroundFrameCount()) {
-                        _engine->playState.currentViewFrame = 0;
-                    }
-                    break;
-                case kDown | kRight:
-                    if (_engine->playState.verticalScroll != _engine->playState.currentMaxVerticalScroll) {
-                        uint16 newScroll = _engine->playState.verticalScroll + _engine->playState.verticalScrollDelta;
-                        _engine->playState.verticalScroll = MIN(newScroll, _engine->playState.currentMaxVerticalScroll);
-                    }
-                    _engine->playState.currentViewFrame -= 1;
-                    if (_engine->playState.currentViewFrame < 0) {
-                        _engine->playState.currentViewFrame = _engine->graphics->getBackgroundFrameCount() -1;
-                    }
-                    break;
-            }
-            if (_engine->input->getInput(InputManager::kMoveFastModifier) ||
-                _engine->input->getInput(InputManager::kRightMouseButtonDown)) {
-                _nextBackgroundMovement = playTimeThisFrame + currentScene.fastMoveTimeDelta;
-
-            } else {
-                _nextBackgroundMovement = playTimeThisFrame + currentScene.slowMoveTimeDelta;
-            }
-        }
-    }
-
-    // Redraw the Background surface if we've moved
-    if (_engine->playState.currentViewFrame != _engine->playState.lastDrawnViewFrame ||
-        _engine->playState.verticalScroll != _engine->playState.lastVerticalScroll) {
-        if (currentScene.videoFormat == 1) {
-            // TODO if it ever gets hit
-        } else if (currentScene.videoFormat == 2) {
-            _engine->graphics->_background.copyRectToSurface(
-            *_engine->graphics->getBackgroundFrame(_engine->playState.currentViewFrame),
-            0, 0,
-            Common::Rect(0, _engine->playState.verticalScroll, _engine->graphics->getBackgroundWidth() - 1,
-                _engine->playState.verticalScroll + _engine->graphics->viewportDesc.source.bottom));
-        }
-        // TODO some if related to PlaySoundPanFrameAnchorAndDie
-        _engine->playState.lastDrawnViewFrame = _engine->playState.currentViewFrame;
-        // TODO function call that sets a val to 1 and 3 others to 0
-    }
-
-    _engine->logic->processActionRecords();
-
-    // code that skips rendering for the first 12 frames??? why
-
-    handleMouse();
-
-    // TODO
-    _engine->playState.lastVerticalScroll = _engine->playState.verticalScroll;
-    _engine->graphics->renderDisplay(_ZRenderFilter);
-}
-
-void SceneManager::handleMouse() {
-    ZRenderStruct &zr = _engine->graphics->getZRenderStruct("CUR IMAGE CURSOR");
-    Common::Point mousePos = _engine->input->getMousePosition();
-    zr.destRect.left = zr.destRect.right = mousePos.x;
-    zr.destRect.top = zr.destRect.bottom = mousePos.y;
-    movementDirection = 0;
-    _engine->input->hoveredElementID = -1;
-    bool returnCursorSelected = false;
-
-    View &view = _engine->graphics->viewportDesc;
-
-    // TODO incorrect magic number, figure out where this comes from
-    Common::Point viewportMouse = mousePos + Common::Point(10, 10);
-    
-    // Check if the mouse is within the viewport
-    if (view.destination.contains(viewportMouse)){
-        _engine->input->setPointerBitmap(0, 0, 0);
-
-        // We can scroll left and right
-        if (currentScene.horizontalEdgeSize > 0) {
-            if (viewportMouse.x < view.destination.left + currentScene.horizontalEdgeSize) {
-                movementDirection |= kLeft;
-            } else if (viewportMouse.x > view.destination.right - currentScene.horizontalEdgeSize) {
-                movementDirection |= kRight;
-            }
-        }
-        if (currentScene.verticalEdgeSize > 0) {
-            if (viewportMouse.y < view.destination.top + currentScene.verticalEdgeSize) {
-                movementDirection |= kUp;
-            } else if (viewportMouse.y > view.destination.bottom - currentScene.verticalEdgeSize) {
-                movementDirection |= kDown;
-            }
-        }
-
-        if (movementDirection != 0) {
-            _engine->input->setPointerBitmap(-1, 2, 0);
-        } else {
-            // Go through all action records and find hotspots
-            Common::Array<ActionRecord *> &records = _engine->logic->getActionRecords();
-            for (uint i = 0; i < records.size(); ++i) {
-                ActionRecord *r = records[i];
-                if (r->isActive && r->hasHotspot) {
-                    // Adjust the hotspot coordinates relative to the viewport
-                    // taking into account the vertical scroll as well
-                    Common::Rect hotspot = r->hotspot;
-                    hotspot.left += view.destination.left;
-                    hotspot.top += view.destination.top - _engine->playState.verticalScroll;
-                    hotspot.right += view.destination.left;
-                    hotspot.bottom += view.destination.top - _engine->playState.verticalScroll;
-
-                    if (hotspot.contains(viewportMouse)) {
-                        if (r->type == 0xE || r->type == 0x3D || r->type == 0x3E) {
-                            // Set the pointer to the U-shaped arrow if
-                            // the type is Hot1FrExitSceneChange, MapCallHot1Fr, or MapCallHotMultiframe
-                            _engine->input->setPointerBitmap(0, 3, 0);
-                            returnCursorSelected = true;
-                        } else {
-                            _engine->input->setPointerBitmap(-1, 1, 0);
-                        }
-                        _engine->input->hoveredElementID = i;
-                    }
-                }
-            }
-        }
-
-        // If we're holding an item we need to show it, but only in the viewport
-        if (_engine->playState.inventory.heldItem != -1 && !returnCursorSelected) {
-            _engine->input->setPointerBitmap(_engine->playState.inventory.heldItem + 3, -1, 1);
-        }
-    } else {
-        // Check if we're hovering above a UI element
-        ZRenderStruct *uizr = &_engine->graphics->getZRenderStruct("CUR TB BAT SLIDER");
-        if (uizr->destRect.contains(mousePos)) {
-            _engine->input->hoveredElementID = InputManager::textBoxScrollbarID;
-            _engine->input->setPointerBitmap(1, 2, -1);
-            _engine->graphics->_textbox.setPosition(handleScrollbar(0));
-        } else if (uizr = &_engine->graphics->getZRenderStruct("CUR INV SLIDER"), uizr->destRect.contains(mousePos)) {
-            _engine->input->hoveredElementID = InputManager::inventoryScrollbarID;
-            _engine->input->setPointerBitmap(1, 2, -1);
-            handleScrollbar(1);
-        } else if (uizr = &_engine->graphics->getZRenderStruct("FRAME TB SURF"), uizr->destRect.contains(mousePos)) {
-            _engine->input->hoveredElementID = InputManager::textBoxID;
-            if (_engine->graphics->_textbox.getHovered(mousePos) != -1) {
-                _engine->input->setPointerBitmap(1, 2, -1);
-            } else {
-                _engine->input->setPointerBitmap(1, 0, -1);
-            }
-        } else if (_engine->sceneManager->inventoryDesc.shadesDst.contains(mousePos)) {
-            if (_engine->playState.inventory.heldItem != -1) {
-                _engine->input->hoveredElementID = InputManager::inventoryItemReturnID;
-            } else {
-                _engine->input->hoveredElementID = InputManager::inventoryItemTakeID;
-            }
-        } else {
-            _engine->input->setPointerBitmap(1, 0, 0);
-        }
-    }     
-}
-
-void SceneManager::clearSceneData() {
-    // only clear select flags
-    for (uint i = 44; i < 54; ++i) {
-        _engine->playState.eventFlags[i] = PlayState::kFalse;
-    }
-    for (uint i = 63; i < 74; ++i) {
-        _engine->playState.eventFlags[i] = PlayState::kFalse;
-    }
-    for (uint i = 75; i < 85; ++i) {
-        _engine->playState.eventFlags[i] = PlayState::kFalse;
-    }
-
-    // clear all logic flags and timestamps
-    for (uint i = 0; i < 30; ++i) {
-        _engine->playState.logicConditions[i] = PlayState::kFalse;
-        _engine->playState.logicConditionsTimestamps[i] = 0;
-    }
-
-    _engine->logic->clearActionRecords();
-
-    _engine->graphics->getZRenderStruct("PRIMARY VIDEO").isActive = false;
-    _engine->graphics->getZRenderStruct("SEC VIDEO 0").isActive = false;
-    _engine->graphics->getZRenderStruct("SEC VIDEO 1").isActive = false;
-    _engine->graphics->getZRenderStruct("SEC MOVIE").isActive = false;
-    _engine->graphics->getZRenderStruct("FRAME TB SURF").isActive = false;
-    _engine->graphics->getZRenderStruct("STATIC BITMAP ANIMATION").isActive = false;
-}
-
-// 0 is textbox, 1 is inventory, returns -1 when movement is stopped/disabled
-float SceneManager::handleScrollbar(uint id, bool reset) {
-    Common::SeekableReadStream *chunk;
-    ZRenderStruct *zr;
-    if (id == 0) {
-        chunk = _engine->getBootChunkStream("TBOX");
-        chunk->seek(0x30);
-        zr = &_engine->graphics->getZRenderStruct("CUR TB BAT SLIDER");
-    } else if (id == 1) {
-        chunk = _engine->getBootChunkStream("INV");
-        chunk->seek(0x10);
-        zr = &_engine->graphics->getZRenderStruct("CUR INV SLIDER");
-    }
-    Common::Point origDest;
-    origDest.x = chunk->readUint16LE() - (zr->sourceRect.width() / 2); // coords in file are for center position
-    origDest.y = chunk->readUint16LE();
-
-    Common::Rect &tboxRect = _engine->graphics->getZRenderStruct("FRAME TB SURF").destRect;
-    Common::Rect &scrollRect = zr->destRect;
-    Common::Point newMousePos = _engine->input->getMousePosition();
-
-    if (reset) {
-        scrollRect.moveTo(origDest);
-        return 0;
-    }
-
-    if (_engine->input->getInput() & InputManager::kLeftMouseButtonDown) {
-        if (scrollbarMouse.x == -1 && scrollbarMouse.y == -1) {
-            scrollbarMouse = newMousePos - Common::Point(scrollRect.left, scrollRect.top);
-        }
-        newMousePos -= Common::Point(scrollRect.left, scrollRect.top);
-
-        uint16 minY = origDest.y;
-        uint16 maxY = tboxRect.bottom - scrollRect.height() + tboxRect.top - origDest.y; // TODO goes a little out of bounds
-        uint16 newTop = CLIP<uint16>((scrollRect.top + newMousePos.y - scrollbarMouse.y), minY, maxY);
-        scrollRect.bottom += newTop - scrollRect.top;
-        scrollRect.top = newTop;
-
-        return newTop == minY ? 0 : 1 - (float(maxY - newTop) / float(maxY - minY));
-    } else {
-        scrollbarMouse.x = -1;
-        scrollbarMouse.y = -1;
-    }
-
-    return -1;
-}
-
-} // End of namespace Nancy
\ No newline at end of file
diff --git a/engines/nancy/scene.h b/engines/nancy/scene.h
deleted file mode 100644
index 633b6da90b..0000000000
--- a/engines/nancy/scene.h
+++ /dev/null
@@ -1,134 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef NANCY_SCENE_H
-#define NANCY_SCENE_H
-
-#include "engines/nancy/time.h"
-#include "engines/nancy/playstate.h"
-#include "engines/nancy/datatypes.h"
-
-#include "common/scummsys.h"
-#include "common/array.h"
-#include "common/str.h"
-
-namespace Graphics {
-	struct Surface;
-}
-
-namespace Common {
-    class SeekableReadStream;
-}
-
-namespace Nancy {
-
-class NancyEngine;
-
-class SceneManager {
-    friend class ActionRecord;
-public:
-    enum MovementDirection : byte { kUp = 1, kDown = 2, kLeft = 4, kRight = 8 };
-    SceneManager(NancyEngine *engine) :
-        _engine (engine),
-        _state (kInit),
-        _sceneID (0),
-        movementDirection(0),
-        stateChangeRequests(0),
-        scrollbarMouse(-1, -1) { }
-
-    void process();
-
-    void changeScene(uint16 id, uint16 frame, uint16 verticalOffset, bool noSound);
-    void pushScene();
-    void popScene();
-    void addObjectToInventory(uint16 id);
-    void removeObjectFromInventory(uint16 id, bool pickUp = false);
-    float handleScrollbar(uint id, bool reset = false);
-
-private:
-    void init();
-    void load();
-    void run();
-
-    void handleMouse();
-    void clearSceneData();
-
-
-public:
-    enum State {
-        kInit,
-        kLoad,
-        kStartSound,
-        kRun,
-        kLoadNew
-    };
-
-    enum GameStateChange : byte {
-        kHelpMenu = 1 << 0,
-        kMainMenu = 1 << 1,
-        kSaveLoad = 1 << 2,
-        kReloadSave = 1 << 3,
-        kSetupMenu = 1 << 4,
-        kCredits = 1 << 5,
-        kMap = 1 << 6
-    };
-    
-    byte stateChangeRequests; // GameStateChange
-
-    int32 playerTimeMinuteLength;
-    byte movementDirection;
-    State _state;
-    uint16 _sceneID;
-    bool doNotStartSound = false;
-
-    Inventory inventoryDesc;
-
-private:
-    NancyEngine *_engine;
-    SceneSummary currentScene;
-    // TODO these two can be Time
-    uint32 _tickCount;
-    uint32 _stashedTickCount;
-    Time _nextBackgroundMovement;
-
-    uint16 _pushedSceneID = 10000;
-    uint16 _pushedFrameID = 0;
-    uint16 _pushedVerticalScroll = 0;
-
-    Common::Point scrollbarMouse;
-
-    bool isComingFromMenu = true;
-    bool hasLoadedFromSavefile = false;
-
-    bool orderingPuzzleIsActive = false;
-    bool rotatingLockPuzzleIsActive = false;
-    bool leverPuzzleIsActive = false;
-    bool sliderPuzzleIsActive = false;
-    bool passwordPuzzleIsActive = false;
-    bool telephoneIsActive = false;
-
-    Common::Array<Common::String> _ZRenderFilter;
-};
-
-} // End of namespace Nancy
-
-#endif // NANCY_SCENE_H
\ No newline at end of file
diff --git a/engines/nancy/logo.cpp b/engines/nancy/state/logo.cpp
similarity index 86%
rename from engines/nancy/logo.cpp
rename to engines/nancy/state/logo.cpp
index a48f16b7d6..35e22d9e35 100644
--- a/engines/nancy/logo.cpp
+++ b/engines/nancy/state/logo.cpp
@@ -20,7 +20,8 @@
  *
  */
 
-#include "engines/nancy/logo.h"
+#include "engines/nancy/state/logo.h"
+
 #include "engines/nancy/nancy.h"
 #include "engines/nancy/resource.h"
 #include "engines/nancy/audio.h"
@@ -29,6 +30,7 @@
 #include "common/error.h"
 #include "common/system.h"
 #include "common/events.h"
+#include "common/str.h"
 
 #include "audio/audiostream.h"
 #include "audio/mixer.h"
@@ -36,8 +38,9 @@
 #include "graphics/surface.h"
 
 namespace Nancy {
+namespace State {
 
-void LogoSequence::process() {
+void Logo::process() {
 	switch (_state) {
 	case kInit:
 		init();
@@ -53,17 +56,16 @@ void LogoSequence::process() {
 	}
 }
 
-void LogoSequence::init() {
+void Logo::init() {
 	_surf = new Graphics::Surface;
 
 	if (!_engine->_res->loadImage("ciftree", _engine->_logos[0].name, *_surf))
 		error("Failed to load %s", _engine->_logos[0].name.c_str());
 
 	_state = kStartSound;
-	_engine->_gameFlow.previousGameState = NancyEngine::kLogo;
 }
 
-void LogoSequence::startSound() {
+void Logo::startSound() {
 	Common::SeekableReadStream *msnd = _engine->getBootChunkStream("MSND");
 	char name[10];
 	msnd->seek(0);
@@ -76,19 +78,19 @@ void LogoSequence::startSound() {
 	_state = kRun;
 }
 
-void LogoSequence::run() {
+void Logo::run() {
 	switch (_runState) {
 	case kBlit:
 		_engine->_system->copyRectToScreen(_surf->getPixels(), _surf->pitch, 0, 0, _surf->w, _surf->h);
 		_runState = kWait;
 		break;
 	case kWait:
-		if (_engine->_system->getMillis() - _startTicks >= 7000 || (_engine->input->getInput(InputManager::kLeftMouseButtonDown)))
+		if (_engine->_system->getMillis() - _startTicks >= 7000 || (_engine->input->getInput().input & NancyInput::kLeftMouseButtonDown))
 			_state = kStop;
 	}
 }
 
-void LogoSequence::stop() {
+void Logo::stop() {
 	_surf->free();
 	delete _surf;
 
@@ -96,8 +98,9 @@ void LogoSequence::stop() {
 	// For the N+C key combo it looks for some kind of cheat file
 	// to initialize the game state with.
 
-	_engine->_gameFlow.minGameState = NancyEngine::kScene;
+	_engine->setGameState(NancyEngine::kScene);
 	_engine->_system->fillScreen(0);
 }
 
-} // End of namespace Nancy
\ No newline at end of file
+} // End of namespace State
+} // End of namespace Nancy
diff --git a/engines/nancy/logo.h b/engines/nancy/state/logo.h
similarity index 89%
rename from engines/nancy/logo.h
rename to engines/nancy/state/logo.h
index a6c849e103..592295d4c0 100644
--- a/engines/nancy/logo.h
+++ b/engines/nancy/state/logo.h
@@ -20,8 +20,8 @@
  *
  */
 
-#ifndef NANCY_LOGO_H
-#define NANCY_LOGO_H
+#ifndef NANCY_STATE_LOGO_H
+#define NANCY_STATE_LOGO_H
 
 #include "common/scummsys.h"
 
@@ -33,9 +33,11 @@ namespace Nancy {
 
 class NancyEngine;
 
-class LogoSequence {
+namespace State {
+
+class Logo {
 public:
-	LogoSequence(NancyEngine *engine) :
+	Logo(NancyEngine *engine) :
 		_engine(engine),
 		_state(kInit),
 		_runState(kBlit),
@@ -70,6 +72,7 @@ private:
 	Graphics::Surface *_surf;
 };
 
+} // end of namespace State
 } // End of namespace Nancy
 
-#endif // NANCY_LOGO_H
\ No newline at end of file
+#endif // NANCY_STATE_LOGO_H
diff --git a/engines/nancy/state/map.cpp b/engines/nancy/state/map.cpp
new file mode 100644
index 0000000000..685d38e7aa
--- /dev/null
+++ b/engines/nancy/state/map.cpp
@@ -0,0 +1,167 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/nancy/state/map.h"
+
+#include "engines/nancy/state/scene.h"
+
+#include "engines/nancy/resource.h"
+#include "engines/nancy/audio.h"
+#include "engines/nancy/input.h"
+#include "engines/nancy/nancy.h"
+#include "engines/nancy/util.h"
+#include "engines/nancy/cursor.h"
+#include "engines/nancy/graphics.h"
+
+#include "common/stream.h"
+#include "common/str.h"
+
+namespace Nancy {
+namespace State {
+
+void Map::process() {
+    switch (_state) {
+        case kInit:
+            init();
+            break;
+        case kRun:
+            run();
+            break;
+    }
+}
+
+void Map::init() {
+    Common::SeekableReadStream *chunk = _engine->getBootChunkStream("MAP");
+
+    _viewport.init();
+    _label.init();
+
+    if (_engine->scene->getEventFlag(40, kTrue) &&
+        _engine->scene->getEventFlag(95, kTrue)) {
+        _mapID = 1;
+    } else {
+        _mapID = 0;
+    }
+
+    // Load the video
+    chunk->seek(_mapID * 10, SEEK_SET);
+    char name[10];
+    chunk->read(name, 10);
+    Common::String n(name);
+
+    _viewport.loadVideo(n, 0, 0);
+    _viewport.setEdges(0, 0, 0, 0);
+
+    // Load the audio
+    chunk->seek(0x18 + _mapID * 0x20, SEEK_SET);
+    chunk->read(name, 10);
+    n = Common::String(name);
+    uint16 channel = chunk->readUint16LE();
+    chunk->skip(0xA);
+    uint16 volume = chunk->readUint16LE();
+    _engine->sound->loadSound(n, channel, 0, volume);
+    _engine->sound->pauseSound(channel, false);
+
+    for (uint i = 0; i < 4; ++i) {
+        chunk->seek(0x162 + i * 16, SEEK_SET);
+        _locations.push_back(Location());
+        Location &loc = _locations[i];
+        readRect(*chunk, loc.hotspot);
+
+        // HARDCODED, TODO
+        if (_mapID == 1 && (i % 2) != 0) {
+            loc.isActive = false;
+        } else {
+            loc.isActive = true;
+        }
+
+        for (uint j = 0; j < 2; ++j) {
+            loc.scenes.push_back(Location::SceneChange());
+            Location::SceneChange &sc = loc.scenes[j];
+            chunk->seek(0x1BE + 6 * i * (j + 1), SEEK_SET);
+            sc.sceneID = chunk->readUint16LE();
+            sc.frameID = chunk->readUint16LE();
+            sc.verticalOffset = chunk->readUint16LE();
+        }
+
+        chunk->seek(0x9A + i * 16, SEEK_SET);
+        readRect(*chunk, loc.labelSrc);
+
+        // TODO this gets initialized using MAP and the textbox's on-screen location
+        // but the code is annoyingly long so fpr now i just directly write the result
+        loc.labelDest = Common::Rect(0x56, 0x166, 0x15E, 0x19B);
+        
+    }
+
+    registerGraphics();
+    _engine->cursorManager->setCursorItemID(-1);
+
+    _state = kRun;
+}
+
+void Map::run() {
+    NancyInput input = _engine->input->getInput();
+
+    _label.setLabel(-1);
+
+    for (uint i = 0; i < 4; ++i) {
+        auto &loc = _locations[i];
+        if (loc.isActive && _viewport.convertToScreen(loc.hotspot).contains(input.mousePos)) {
+            _engine->cursorManager->setCursorType(CursorManager::kHotspotArrow);
+
+            _label.setLabel(i);
+
+            // TODO handle map button as well
+
+            if (input.input & NancyInput::kLeftMouseButtonUp) {
+                _engine->setGameState(NancyEngine::kScene);
+                _engine->scene->changeScene(loc.scenes[_mapID].sceneID, loc.scenes[_mapID].frameID, loc.scenes[_mapID].verticalOffset, false);
+                _state = kInit;
+            }
+            return;
+        }
+    }
+}
+
+void Map::registerGraphics() {
+    _viewport.registerGraphics();
+    _label.registerGraphics();
+}
+
+void Map::MapLabel::init() {
+    setLabel(-1);
+    
+    RenderObject::init();
+}
+
+void Map::MapLabel::setLabel(int labelID) {
+    if (labelID == -1) {
+        setVisible(false);
+    } else {
+        _screenPosition = _parent->_locations[labelID].labelDest;
+        _drawSurface.create(_engine->graphicsManager->object0, _parent->_locations[labelID].labelSrc);
+        setVisible(true);
+    }
+}
+
+} // End of namespace State
+} // End of namespace Nancy
diff --git a/engines/nancy/map.h b/engines/nancy/state/map.h
similarity index 61%
rename from engines/nancy/map.h
rename to engines/nancy/state/map.h
index 5b11a8d6ab..19aed87877 100644
--- a/engines/nancy/map.h
+++ b/engines/nancy/state/map.h
@@ -20,10 +20,15 @@
  *
  */
 
-#ifndef NANCY_MAP_H
-#define NANCY_MAP_H
+#ifndef NANCY_STATE_MAP_H
+#define NANCY_STATE_MAP_H
+
+#include "engines/nancy/ui/viewport.h"
+
+#include "engines/nancy/state/scene.h"
 
 #include "engines/nancy/video.h"
+#include "engines/nancy/renderobject.h"
 
 #include "common/str.h"
 #include "common/array.h"
@@ -35,10 +40,18 @@ namespace Nancy {
 
 class NancyEngine;
 
+namespace State {
+
 class Map {
+    friend class MapLabel;
 public:
     enum State { kInit, kRun };
-    Map(NancyEngine *engine) : _engine(engine), _state(kInit), _mapID(0) {}
+    Map(Nancy::NancyEngine *engine) :
+        _engine(engine),
+        _state(kInit),
+        _mapID(0),
+        _viewport(engine),
+        _label(engine->scene->getFrame(), this) {}
 
     void process();
 
@@ -58,21 +71,38 @@ private:
         Common::Rect labelDest;
     };
 
+    class MapLabel : public Nancy::RenderObject {
+    public:
+        MapLabel(RenderObject &redrawFrom, Map *parent) : Nancy::RenderObject(redrawFrom), _parent(parent) {}
+        virtual ~MapLabel() =default;
+
+        virtual void init() override;
+        
+        void setLabel(int labelID);
+
+    protected:
+        virtual uint16 getZOrder() const override { return 7; }
+        virtual BlitType getBlitType() const override { return kNoTrans; }
+
+        Map *_parent;
+    };
+
     void init();
     void run();
 
-    void handleMouse();
+    void registerGraphics();
+
+    Nancy::NancyEngine *_engine;
+
+    Nancy::UI::Viewport _viewport;
+    MapLabel _label;
 
-    NancyEngine *_engine;
     State _state;
     uint16 _mapID;
-    AVFDecoder _decoder;
-    Graphics::Surface _mapImage;
     Common::Array<Location> _locations;
-
-    Common::Array<Common::String> _ZRenderFilter;
 };
 
+} // End of namespace State
 } // End of namespace Nancy
 
-#endif
\ No newline at end of file
+#endif // NANCY_STATE_MAP_H
diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
new file mode 100644
index 0000000000..4ec0c99790
--- /dev/null
+++ b/engines/nancy/state/scene.cpp
@@ -0,0 +1,397 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/nancy/state/scene.h"
+#include "engines/nancy/nancy.h"
+#include "engines/nancy/resource.h"
+#include "engines/nancy/iff.h"
+#include "engines/nancy/action/actionmanager.h"
+#include "engines/nancy/input.h"
+#include "engines/nancy/audio.h"
+#include "engines/nancy/graphics.h"
+#include "engines/nancy/cursor.h"
+#include "engines/nancy/time.h"
+
+#include "common/memstream.h"
+#include "common/rect.h"
+#include "common/func.h"
+
+#include "graphics/surface.h"
+
+namespace Nancy {
+namespace State{
+
+void Scene::process() {
+    switch (_state) {
+    case kInit:
+        init();
+        _state = kLoad;
+        // fall through
+    case kLoadNew:
+        _state = kLoad;
+        // fall through
+    case kLoad:
+        load();
+        // fall through
+    case kStartSound:
+        _state = kRun;
+        if (!_sceneState._doNotStartSound) {
+            _engine->sound->stopAllSounds();
+            _engine->sound->loadSound(_sceneState.summary.audioFile, _sceneState.summary.audioID, 0, _sceneState.summary.audioVolume);
+            _engine->sound->pauseSound(_sceneState.summary.audioID, false);
+        }
+        // fall through
+    case kRun:
+        run();
+        break;
+    
+    }
+}
+
+void Scene::changeScene(uint16 id, uint16 frame, uint16 verticalOffset, bool noSound) {
+    _sceneState.nextScene.sceneID = id;
+    _sceneState.nextScene.frameID = frame;
+    _sceneState.nextScene.verticalOffset = verticalOffset;
+    _sceneState._doNotStartSound = noSound;
+    _state = kLoadNew;
+}
+
+void Scene::pushScene() {
+    _sceneState.pushedScene = _sceneState.currentScene;
+    _sceneState.isScenePushed = true;
+}
+
+void Scene::popScene() {
+    changeScene(_sceneState.pushedScene.sceneID, _sceneState.pushedScene.frameID, _sceneState.pushedScene.verticalOffset, true);
+    _sceneState.isScenePushed = false;
+}
+
+void Scene::addItemToInventory(uint16 id) {
+    if (_flags.heldItem == id) {
+        setHeldItem(-1);
+    }
+
+    _inventoryBox.addItem(id);
+}
+
+void Scene::removeItemFromInventory(uint16 id, bool pickUp) {
+    _flags.items[id] = kFalse;
+
+    if (pickUp) {
+        setHeldItem(id);
+    }
+
+    _inventoryBox.removeItem(id, pickUp);
+}
+
+void Scene::registerGraphics() {
+    _frame.registerGraphics();
+    _viewport.registerGraphics();
+    _textbox.registerGraphics();
+    _inventoryBox.registerGraphics();
+
+    // Used to clear the map label
+    _textbox.setVisible(false);
+}
+
+void Scene::init() {
+    for (uint i = 0; i < 168; ++i) {
+        _flags.eventFlags[i] = kFalse;
+    }
+
+    // Does this ever get used?
+    for (uint i = 0; i < 1000; ++i) {
+        _sceneState.sceneHitCount[i] = 0;
+    }
+
+    for (uint i = 0; i < 11; ++i) {
+        _flags.items[i] = kFalse;
+    }
+
+    _timers.playerTime = _engine->startTimeHours * 3600000;
+    _timers.sceneTime = 0;
+    _timers.timerTime = 0;
+    _timers.timerIsActive = false;
+    _timers.tickCount = 0;
+    _timers.playerTimeNextMinute = 0;
+    _timers.pushedPlayTime = 0;
+    _timers.timeOfDay = Timers::kDay;
+
+    _sceneState.nextScene.sceneID = _engine->firstSceneID;
+
+    _frame.init();
+    _viewport.init();
+    _textbox.init();
+    _inventoryBox.init();
+    _engine->cursorManager->showCursor(true);
+
+    _state = kLoad;
+}
+
+void Scene::load() {
+    clearSceneData();
+
+    // Scene IDs are prefixed with S inside the cif tree; e.g 100 -> S100                                                                                    
+    Common::String sceneName = Common::String::format("S%u", _sceneState.nextScene.sceneID);
+    IFF sceneIFF(_engine, sceneName);
+	if (!sceneIFF.load()) {
+		error("Faled to load IFF %s", sceneName.c_str());
+	}
+
+    Common::SeekableReadStream *sceneSummaryChunk = sceneIFF.getChunkStream("SSUM");
+    if (!sceneSummaryChunk) {
+        error("Invalid IFF Chunk SSUM");
+    }
+
+    readSceneSummary(*sceneSummaryChunk);
+
+    // Search for Action Records, maximum for a scene is 30
+    Common::SeekableReadStream *actionRecordChunk = nullptr;
+
+    while (actionRecordChunk = sceneIFF.getChunkStream("ACT", _actionManager._records.size()), actionRecordChunk != nullptr)
+    {
+        if (_actionManager._records.size() >= 30) {
+            error("Invalid number of Action Records");
+        }
+
+        _actionManager.addNewActionRecord(*actionRecordChunk);
+    }
+
+    _viewport.loadVideo(_sceneState.summary.videoFile, _sceneState.nextScene.frameID, _sceneState.nextScene.verticalOffset);
+
+    if (_viewport.getFrameCount() <= 1) {
+        _viewport.setEdges(-1, -1, 0, 0);
+    } else {
+        // TODO TEMPORARY
+        _viewport.setEdges(-1, -1, 25, 25);
+    }
+
+    if (!hasLoadedFromSavefile) {
+        _sceneState.currentScene.verticalOffset = _sceneState.nextScene.verticalOffset;
+        _sceneState.currentScene.frameID = _sceneState.nextScene.frameID;
+
+        if (_sceneState.summary.videoFormat == 1) {
+            // TODO not sure this ever gets hit
+        } else if (_sceneState.summary.videoFormat == 2) {
+            // always start from the bottom
+            _sceneState.currentScene.verticalOffset = _viewport.getMaxScroll();
+        } else {
+            error("Unrecognized Scene summary chunk video file format");
+        }
+
+        // Some checks against rFrame
+
+        if (_sceneState.summary.videoFormat == 1) {
+            // TODO not sure this ever gets hit
+        } else if (_sceneState.summary.videoFormat == 2) {
+            if (_viewport.getMaxScroll() == 0) {
+                _viewport.setEdges(0, 0, -1, -1);
+            } else {
+                // TODO TEMPORARY
+                _viewport.setEdges(25, 25, -1, -1);
+            }
+        }
+    }
+
+    _sceneState.currentScene = _sceneState.nextScene;
+    _timers.sceneTime = 0;
+
+    if (_engine->getGameState() != _engine->getPreviousGameState()) {
+        registerGraphics();
+    }
+
+    _state = kStartSound;
+}
+
+void Scene::run() {
+    if (isComingFromMenu) {
+        // TODO
+    }
+    isComingFromMenu = false;
+
+    Time playTime = _engine->getTotalPlayTime();
+
+    if (changeGameState()) {
+        return;
+    }
+
+    // Do some work if we're coming from a different game state
+    if (_engine->getGameState() != _engine->getPreviousGameState()) {
+        if (hasLoadedFromSavefile) {
+            if (playTime > _timers.pushedPlayTime) {
+                playTime -= _timers.pushedPlayTime;
+                _timers.totalTime -= playTime;
+                _timers.sceneTime -= playTime;
+                if (_timers.timerIsActive)
+                    _timers.timerTime -= playTime;
+            }
+        }
+
+        registerGraphics();
+
+        return;
+    }
+
+    Time deltaTime = 0;
+
+    if (_timers.tickCount < playTime) {
+        deltaTime = playTime - _timers.tickCount;
+        _timers.tickCount = playTime;
+    }
+
+    _timers.totalTime += deltaTime;
+
+    if (_timers.timerIsActive)
+        _timers.timerTime += deltaTime;
+    _timers.sceneTime += deltaTime;
+
+    // Calculate the in-game time (playerTime)
+    if (playTime > _timers.playerTimeNextMinute) {
+        _timers.playerTime += 60000; // Add a minute
+        _timers.playerTimeNextMinute = playTime + playerTimeMinuteLength; // Set when we're going to add the next minute
+    }
+
+    // Set the time of day according to playerTime
+    if (_timers.playerTime.getHours() >= 7 && _timers.playerTime.getHours() < 18) {
+        _timers.timeOfDay = Timers::kDay;
+    } else if (_timers.playerTime.getHours() >= 19 || _timers.playerTime.getHours() < 6) {
+        _timers.timeOfDay = Timers::kNight;
+    } else {
+        _timers.timeOfDay = Timers::kDuskDawn;
+    }
+
+
+    // Update the UI elements and handle input
+    NancyInput input = _engine->input->getInput();
+    _viewport.handleInput(input);
+    _textbox.handleInput(input);
+    _inventoryBox.handleInput(input);
+    _actionManager.handleInput(input);
+
+    _actionManager.processActionRecords();
+}
+
+void Scene::readSceneSummary(Common::SeekableReadStream &stream) {
+    char *buf = new char[0x32];
+
+    stream.seek(0);
+    stream.read(buf, 0x31);
+    buf[32] = 0;
+    _sceneState.summary.description = Common::String(buf);
+
+    stream.seek(1, SEEK_CUR);
+    stream.read(buf, 9);
+    buf[9] = 0;
+    _sceneState.summary.videoFile = Common::String(buf);
+
+    // skip 1 extra byte & 2 unknown bytes
+    stream.seek(3, SEEK_CUR);
+    _sceneState.summary.videoFormat = stream.readUint16LE();
+
+    stream.read(buf, 10);
+    buf[9] = 0;
+    _sceneState.summary.audioFile = Common::String(buf);
+    _sceneState.summary.audioID = stream.readSint16LE();
+    stream.skip(0xE);
+    _sceneState.summary.audioVolume = stream.readUint16LE();
+
+    stream.seek(0x72);
+    _sceneState.summary.verticalScrollDelta = stream.readUint16LE();
+    _sceneState.summary.horizontalEdgeSize = stream.readUint16LE();
+    _sceneState.summary.verticalEdgeSize = stream.readUint16LE();
+    _sceneState.summary.slowMoveTimeDelta = stream.readUint16LE();
+    _sceneState.summary.fastMoveTimeDelta = stream.readUint16LE();
+
+    if (_engine->overrideMovementTimeDeltas) {
+        _sceneState.summary.slowMoveTimeDelta = _engine->slowMovementTimeDelta;
+        _sceneState.summary.fastMoveTimeDelta = _engine->fastMovementTimeDelta;
+    }
+
+    delete[] buf;
+}
+
+bool Scene::changeGameState() {
+    if (_gameStateRequested != NancyEngine::kScene) {
+        _timers.pushedPlayTime = _engine->getTotalPlayTime();
+        _engine->setGameState(_gameStateRequested);
+        _gameStateRequested = NancyEngine::kScene;
+
+        return true;
+    }
+
+    return false;
+}
+
+void Scene::clearSceneData() {
+    // only clear select flags
+    for (uint i = 44; i < 54; ++i) {
+        _flags.eventFlags[i] = kFalse;
+    }
+    for (uint i = 63; i < 74; ++i) {
+        _flags.eventFlags[i] = kFalse;
+    }
+    for (uint i = 75; i < 85; ++i) {
+        _flags.eventFlags[i] = kFalse;
+    }
+
+    clearLogicConditions();
+    _actionManager.clearActionRecords();
+}
+
+void Scene::setEventFlag(int16 label, NancyFlag flag) {
+    if (label > -1) {
+        _flags.eventFlags[label] = flag;
+    }
+}
+
+bool Scene::getEventFlag(int16 label, NancyFlag flag) {
+    if (label > -1) {
+        return _flags.eventFlags[label] == flag;
+    } else {
+        return false;
+    }
+}
+
+void Scene::setLogicCondition(int16 label, NancyFlag flag) {
+    if (label > -1) {
+        _flags.logicConditions[label].flag = flag;
+        _flags.logicConditions[label].timestamp = _engine->getTotalPlayTime();
+    }
+}
+
+bool Scene::getLogicCondition(int16 label, NancyFlag flag) {
+    if (label > -1) {
+        return _flags.logicConditions[label].flag == flag;
+    } else {
+        return false;
+    }
+}
+
+void Scene::clearLogicConditions() {
+    for (auto &cond : _flags.logicConditions) {
+        cond.flag = kFalse;
+        cond.timestamp = 0;
+    }
+}
+
+} // End of namespace State
+} // End of namespace Nancy
diff --git a/engines/nancy/state/scene.h b/engines/nancy/state/scene.h
new file mode 100644
index 0000000000..72ef3a4665
--- /dev/null
+++ b/engines/nancy/state/scene.h
@@ -0,0 +1,239 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef NANCY_STATE_SCENE_H
+#define NANCY_STATE_SCENE_H
+
+#include "engines/nancy/action/actionmanager.h"
+
+#include "engines/nancy/ui/frame.h"
+#include "engines/nancy/ui/viewport.h"
+#include "engines/nancy/ui/textbox.h"
+#include "engines/nancy/ui/inventorybox.h"
+
+#include "engines/nancy/time.h"
+#include "engines/nancy/commontypes.h"
+#include "engines/nancy/nancy.h"
+
+#include "common/scummsys.h"
+#include "common/array.h"
+#include "common/str.h"
+
+namespace Graphics {
+	struct Surface;
+}
+
+namespace Common {
+    class SeekableReadStream;
+}
+
+namespace Nancy {
+
+class NancyEngine;
+
+namespace State {
+
+struct SceneInfo {
+    uint16 sceneID = 0;
+    uint16 frameID = 0;
+    uint16 verticalOffset = 0;
+};
+
+class Scene {
+    friend class Nancy::Action::ActionRecord;
+    friend class Nancy::Action::ActionManager;
+    friend class Nancy::NancyConsole;
+public:
+    struct SceneSummary { // SSUM
+        Common::String description; // 0x00
+        Common::String videoFile;   // 0x32
+        //
+        uint16 videoFormat;         // 0x3E, value is 1 or 2
+        Common::String audioFile;   // 0x40
+        int16 audioID;              // 0x4A
+        uint16 audioVolume;         // 0x5A
+        //
+        uint16 verticalScrollDelta; // 0x72
+        uint16 horizontalEdgeSize;  // 0x74
+        uint16 verticalEdgeSize;    // 0x76
+        Time slowMoveTimeDelta;   // 0x78
+        Time fastMoveTimeDelta;   // 0x7A
+        // byte unknown7C enum with 4 values
+        //
+    };
+
+    Scene(Nancy::NancyEngine *engine) :
+        _engine (engine),
+        _state (kInit),
+        _frame(engine),
+        _gameStateRequested(NancyEngine::kScene),
+        _viewport(engine),
+        _textbox(_frame),
+        _inventoryBox(_frame),
+        _actionManager(engine) {}
+
+    void process();
+
+    void changeScene(uint16 id, uint16 frame, uint16 verticalOffset, bool noSound);
+    void pushScene();
+    void popScene();
+
+    void addItemToInventory(uint16 id);
+    void removeItemFromInventory(uint16 id, bool pickUp = true);
+    int16 getHeldItem() { return _flags.heldItem; }
+    void setHeldItem(int16 id) { _flags.heldItem = id; _engine->cursorManager->setCursorItemID(id); }
+
+    void setEventFlag(int16 label, NancyFlag flag = kTrue);
+    bool getEventFlag(int16 label, NancyFlag flag = kTrue);
+
+    void setLogicCondition(int16 label, NancyFlag flag = kTrue);
+    bool getLogicCondition(int16 label, NancyFlag flag = kTrue);
+    void clearLogicConditions();
+
+    void setDifficulty(uint difficulty) { _difficulty = difficulty; }
+
+    void requestStateChange(NancyEngine::GameState state) { _gameStateRequested = state; }
+
+    void resetAndStartTimer() { _timers.timerIsActive = true; _timers.timerTime = 0; }
+    void stopTimer() { _timers.timerIsActive = false; _timers.timerTime = 0; }
+
+    Time getMovementTimeDelta(bool fast) { return fast ? _sceneState.summary.fastMoveTimeDelta : _sceneState.summary.slowMoveTimeDelta; }
+
+    void registerGraphics();
+
+    UI::Frame &getFrame() { return _frame; }
+    UI::Viewport &getViewport() { return _viewport; }
+    UI::Textbox &getTextbox() { return _textbox; }
+    UI::InventoryBox &getInventoryBox() { return _inventoryBox; }
+
+    Action::ActionManager &getActionManager() { return _actionManager; }
+
+    SceneInfo &getSceneInfo() { return _sceneState.currentScene; }
+    const SceneSummary &getSceneSummary() const { return _sceneState.summary; }
+
+private:
+    void init();
+    void load();
+    void run();
+
+    void readSceneSummary(Common::SeekableReadStream &stream);
+
+    bool changeGameState();
+
+    void clearSceneData();
+
+public:
+    enum State {
+        kInit,
+        kLoad,
+        kStartSound,
+        kRun,
+        kLoadNew
+    };
+
+    enum GameStateChange : byte {
+        kHelpMenu = 1 << 0,
+        kMainMenu = 1 << 1,
+        kSaveLoad = 1 << 2,
+        kReloadSave = 1 << 3,
+        kSetupMenu = 1 << 4,
+        kCredits = 1 << 5,
+        kMap = 1 << 6
+    };
+    
+    // TODO move 
+    Time playerTimeMinuteLength;
+
+protected:
+    struct SceneState {
+        SceneSummary summary;
+        SceneInfo currentScene;
+        SceneInfo nextScene;
+        SceneInfo pushedScene;
+        bool isScenePushed;
+        byte sceneHitCount[1000];
+
+        bool _doNotStartSound = false;
+    };
+
+    struct Timers {
+        enum TimeOfDay { kDay, kNight, kDuskDawn };
+
+        Time tickCount;
+        Time pushedPlayTime;
+
+        Time totalTime;
+        Time sceneTime;
+        Time timerTime;
+        bool timerIsActive = false;
+        Time playerTime; // In-game time of day, adds a minute every 5 seconds
+        Time playerTimeNextMinute; // Stores the next tick count until we add a minute to playerTime
+        TimeOfDay timeOfDay = kDay;
+    };
+
+    struct PlayFlags {
+        struct LogicCondition {
+            NancyFlag flag = NancyFlag::kFalse;
+            Time timestamp;
+        };
+
+        LogicCondition logicConditions[30];
+        NancyFlag eventFlags[168];
+        NancyFlag items[11];
+        int16 heldItem = -1;
+        int16 primaryVideoResponsePicked = -1;
+    };
+
+    Nancy::NancyEngine *_engine;
+
+    // RenderObjects
+    UI::Frame _frame;
+    UI::Viewport _viewport;
+    UI::Textbox _textbox;
+    UI::InventoryBox _inventoryBox;
+
+    // Data
+    SceneState _sceneState;
+    PlayFlags _flags;
+    Timers _timers;
+    uint16 _difficulty;
+    NancyEngine::GameState _gameStateRequested;
+
+    Action::ActionManager _actionManager;
+
+    State _state;
+
+    bool isComingFromMenu = true;
+    bool hasLoadedFromSavefile = false;
+
+    bool orderingPuzzleIsActive = false;
+    bool rotatingLockPuzzleIsActive = false;
+    bool leverPuzzleIsActive = false;
+    bool sliderPuzzleIsActive = false;
+    bool passwordPuzzleIsActive = false;
+    bool telephoneIsActive = false;
+};
+
+} // End of namespace State
+} // End of namespace Nancy
+
+#endif // NANCY_STATE_SCENE_H
diff --git a/engines/nancy/textbox.cpp b/engines/nancy/textbox.cpp
deleted file mode 100644
index 7e185c15ad..0000000000
--- a/engines/nancy/textbox.cpp
+++ /dev/null
@@ -1,314 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "engines/nancy/textbox.h"
-
-#include "engines/nancy/nancy.h"
-#include "engines/nancy/resource.h"
-#include "engines/nancy/graphics.h"
-#include "engines/nancy/scene.h"
-
-#include "common/error.h"
-#include "common/util.h"
-
-namespace Nancy {
-
-const Common::String Textbox::beginToken = Common::String("<i>");
-const Common::String Textbox::endToken = Common::String("<o>");
-const Common::String Textbox::colorBeginToken = Common::String("<c1>");
-const Common::String Textbox::colorEndToken = Common::String("<c0>");
-const Common::String Textbox::hotspotToken = Common::String("<h>");
-const Common::String Textbox::newLineToken = Common::String("<n>");
-
-void Textbox::init() {
-    Common::SeekableReadStream *chunk = _engine->getBootChunkStream("FONT");
-    
-    chunk->seek(0);
-    while (chunk->pos() < chunk->size() - 1) {
-        _fonts.push_back(Font());
-        _fonts.back().read(*chunk);
-
-        _engine->_res->loadImage("ciftree", _fonts.back().imageName, _fonts.back().image);
-    }
-    
-    chunk = _engine->getBootChunkStream("TBOX");
-    chunk->seek(0x28);
-    uint width = chunk->readUint32LE();
-    uint height = chunk->readUint32LE();
-    chunk->seek(0x20);
-    width -= chunk->readUint32LE();
-    height -= chunk->readUint32LE();
-    _surface.create(width, height, GraphicsManager::pixelFormat);
-
-    chunk->seek(0x36);
-    _firstLineOffset = chunk->readUint16LE();
-    _lineHeight = chunk->readUint16LE();
-    _borderWidth = chunk->readUint16LE();
-}
-
-void Textbox::clear() {
-    _surface.clear();
-    lineNr = 0;
-    _responses.clear();
-    setPosition(_engine->sceneManager->handleScrollbar(0, true));
-}
-
-Common::Rect Textbox::processTextLine(const Common::String &text, uint16 fontID) {
-    ZRenderStruct &zr = _engine->graphics->getZRenderStruct("FRAME TB SURF");
-    uint textWidth = 0;
-    Common::String line;
-    uint maxWidth = zr.destRect.width() - _borderWidth;
-    uint lineDist = _lineHeight + _lineHeight / 4;
-    Common::Rect ret;
-    ret.left = _borderWidth;
-    ret.top = _firstLineOffset - _lineHeight + lineNr * lineDist;
-    ret.right = ret.left;
-    ret.bottom = ret.top + _lineHeight;
-    
-    bool colorTokenIsOpen = false;
-    uint16 colorOffset = 0;
-
-    for (uint i = 0; i < text.size(); ++i) {
-        if (text[i] == '<') {
-            Common::String token = text.substr(i, 4);
-            if (token.hasPrefix(beginToken)) {
-                // Ignore and continue
-                i += beginToken.size() - 1;
-            } else if (token.hasPrefix(endToken)) {
-                // Caption has ended, return
-                return ret;
-            } else if (token.hasPrefix(colorBeginToken)) {
-                // Assumes color is always at beginning of line
-                colorTokenIsOpen = true;
-                i += colorBeginToken.size() - 1;
-                colorOffset = 0;
-            } else if (token.hasPrefix(colorEndToken)) {
-                // Draw color line (always a single letter?)
-                colorTokenIsOpen = false;
-                drawText(line, 1, _borderWidth, _firstLineOffset + lineNr * lineDist, true);
-                line.clear();
-                i += colorEndToken.size() - 1;
-            } else if (token.hasPrefix(hotspotToken)) {
-                // TODO ADD HOTSPOT
-                i += hotspotToken.size() - 1;
-            } else if (token.hasPrefix(newLineToken)) {
-                // Draw finished line
-                drawText(line, fontID, colorOffset + _borderWidth, _firstLineOffset + lineNr * lineDist);
-                line.clear();
-                ret.setWidth(MAX<int16>(ret.width(), textWidth));
-                textWidth = 0;
-                ++lineNr;
-                i += newLineToken.size() - 1;
-            }
-        } else {
-            if (colorTokenIsOpen) {
-                colorOffset += getCharacterWidth(text[i], fontID) + 1;
-            }
-            line += text[i];
-            textWidth += getCharacterWidth(text[i], fontID) + 1;
-
-            // Wrap text
-            if (textWidth > maxWidth) {
-                while (line.size() > 0) {
-                    if (line.lastChar() != ' ') {
-                        line.deleteLastChar();
-                        --i;
-                    } else {
-                        drawText(line, fontID, colorOffset + _borderWidth, _firstLineOffset + lineNr * lineDist);
-                        line.clear();
-                        ret.setWidth(MAX<int16>(ret.width(), textWidth));
-                        ret.bottom += lineDist;
-                        textWidth = 0;
-                        colorOffset = 0;
-                        ++lineNr;
-                        break;
-                    }
-        }
-            }
-        }
-    }
-    
-    if (line.size()) {
-        drawText(line, fontID, colorOffset + _borderWidth, _firstLineOffset + lineNr * lineDist);
-        ret.setWidth(MAX<int16>(ret.width(), textWidth));
-    }
-
-    return ret;
-}
-
-
-void Textbox::processResponse(const Common::String &text, uint16 fontID, uint16 id, Common::String soundName) {
-    ++lineNr;
-    _responses.push_back(Response());
-    Response &res = _responses.back();
-
-    res.hotspot = processTextLine(text, fontID);
-    res.soundName = soundName;
-}
-
-int16 Textbox::getHovered(Common::Point mousePos) {
-    ZRenderStruct &zr = _engine->graphics->getZRenderStruct("FRAME TB SURF");
-    int16 pickedResponse = -1;
-
-    // Adjust the mouse to local coordinates
-    mousePos.x -= zr.destRect.left;
-    mousePos.y -= zr.destRect.top;
-
-    // Adjust for scroll
-    mousePos.y += zr.sourceRect.top;
-
-    for (uint i = 0; i < _responses.size(); ++i) {
-        if (_responses[i].hotspot.contains(mousePos)) {
-            pickedResponse = i;
-        }
-    }
-
-    return pickedResponse;
-}
-
-void Textbox::setPosition(float pos) {
-    if (pos == -1)
-        return;
-
-    pos = CLIP<float>(pos, 0, 1);
-    ZRenderStruct &zr = _engine->graphics->getZRenderStruct("FRAME TB SURF");
-
-    uint16 inner = getInnerHeight();
-    uint16 outer = zr.destRect.height();
-
-    if (inner > outer) {
-        zr.sourceRect.moveTo(zr.sourceRect.left, (inner - outer) * pos);
-    }
-}
-
-uint16 Textbox::getInnerHeight() {
-    uint lineDist = _lineHeight + _lineHeight / 4;
-    return lineNr * lineDist + 2 * _firstLineOffset;
-}
-
-Common::Rect Textbox::getCharacterSourceRect(char c, uint16 fontID) {
-    using namespace Common;
-    Font &font = _fonts[fontID];
-    uint offset = 0;
-    Common::Rect ret;
-
-    if (isUpper(c)) {
-        offset = c + font.uppercaseOffset - 0x41;
-    } else if (isLower(c)) {
-        offset = c + font.lowercaseOffset - 0x61;
-    } else if (isDigit(c)) {
-        offset = c + font.digitOffset - 0x30;
-    } else if (isSpace(c)) {
-        ret.setWidth(font.spaceWidth);
-        return ret;
-    } else if (isPunct(c)) {
-        switch (c) {
-            case '.':
-                offset = font.periodOffset;
-                break;
-            case ',':
-                offset = font.commaOffset;
-                break;
-            case '=':
-                offset = font.equalitySignOffset;
-                break;
-            case ':':
-                offset = font.colonOffset;
-                break;
-            case '-':
-                offset = font.dashOffset;
-                break;
-            case '?':
-                offset = font.questionMarkOffset;
-                break;
-            case '!':
-                offset = font.exclamationMarkOffset;
-                break;
-            case '%':
-                offset = font.percentOffset;
-                break;
-            case '&':
-                offset = font.ampersandOffset;
-                break;
-            case '*':
-                offset = font.asteriskOffset;
-                break;
-            case '(':
-                offset = font.leftBracketOffset;
-                break;
-            case ')':
-                offset = font.rightBracketOffset;
-                break;
-            case '+':
-                offset = font.plusOffset;
-                break;
-            case '\'':
-                offset = font.apostropheOffset;
-                break;
-            case ';':
-                offset = font.semicolonOffset;
-                break;
-            case '/':
-                offset = font.slashOffset;
-                break;
-            default:
-                error("Unsupported FONT character: %c", c);
-        }
-    }
-    ret = font.symbolRects[offset];
-    ret.right += 1;
-    return ret;
-}
-
-uint16 Textbox::getCharacterWidth(char c, uint16 fontID) {
-    return getCharacterSourceRect(c, fontID).width();
-}
-
-void Textbox::drawText(const Common::String &text, uint16 fontID, uint16 left, uint16 bottom, bool color) {
-    Common::Rect source;
-    Common::Rect dest;
-    dest.left = left;
-    dest.bottom = bottom;
-
-    for (char c : text) {
-        source = getCharacterSourceRect(c, fontID);
-        if (color) {
-            source.left += _fonts[fontID].colorCoordsOffset.x;
-            source.right += _fonts[fontID].colorCoordsOffset.x;
-            source.top += _fonts[fontID].colorCoordsOffset.y;
-            source.bottom += _fonts[fontID].colorCoordsOffset.y;
-        }
-
-        if (Common::isSpace(c)) {
-            dest.left += source.width();
-            continue;
-        }
-
-        dest.right = dest.left + source.width();
-        dest.top = dest.bottom - source.height();
-
-        _surface.transBlitFrom(_fonts[fontID].image, source, dest, GraphicsManager::transColor);
-        dest.left = dest.right + 1;
-    }
-}
-
-} // End of namespace Nancy
\ No newline at end of file
diff --git a/engines/nancy/time.h b/engines/nancy/time.h
index e9bee710de..cb762b7dda 100644
--- a/engines/nancy/time.h
+++ b/engines/nancy/time.h
@@ -31,7 +31,7 @@ namespace Nancy {
 struct Time {
 public:
     Time() { _milliseconds = 0; }
-    Time(uint32 &t) { _milliseconds = t; }
+    Time(const uint32 &t) { _milliseconds = t; }
     Time(const Time &t) =default;
     ~Time() =default;
     Time &operator=(const Time &t)                          { if (this != &t) _milliseconds = t._milliseconds; return *this; }
@@ -67,11 +67,11 @@ public:
     friend bool operator>= (const Time &l, const uint32 &r) { return !(l < r); }
     friend bool operator>= (const uint32 &l, const Time &r) { return !(l < r); }
 
-    uint16 getSeconds()     { return (_milliseconds / 1000) % 60; }
-    uint16 getMinutes()     { return (_milliseconds / 60000) % 60; }
-    uint16 getHours()       { return _milliseconds / 3600000; }
-    // Used for player time
-    uint16 getHours_alt()   { return (_milliseconds / 3600000) % 24; }
+    uint16 getSecondsOnly()     { return (_milliseconds / 1000) % 60; }
+    uint16 getMinutesOnly()     { return (_milliseconds / 60000) % 60; }
+    uint16 getHoursOnly()       { return _milliseconds / 3600000; }
+    
+    uint16 getHours()   { return (_milliseconds / 3600000) % 24; } // Used for player time
     uint16 getDays()        { return _milliseconds / 86400000; } // up to 49.7 days
 
 private:
@@ -80,4 +80,4 @@ private:
 
 } // End of namespace Nancy
 
-#endif // NANCY_TIME_H
\ No newline at end of file
+#endif // NANCY_TIME_H
diff --git a/engines/nancy/ui/frame.cpp b/engines/nancy/ui/frame.cpp
new file mode 100644
index 0000000000..7959b584e7
--- /dev/null
+++ b/engines/nancy/ui/frame.cpp
@@ -0,0 +1,47 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/nancy/ui/frame.h"
+
+#include "engines/nancy/nancy.h"
+#include "engines/nancy/resource.h"
+#include "engines/nancy/graphics.h"
+
+namespace Nancy {
+namespace UI {
+
+void Frame::init() {
+    // TODO
+    Graphics::Surface surf;
+    _engine->_res->loadImage("ciftree", "FRAME", surf);
+
+    Common::Rect srcBounds = Common::Rect(0,0, surf.w, surf.h);
+    _screenPosition = srcBounds;
+    
+    _drawSurface.create(surf.w, surf.h, surf.format);
+    _drawSurface.blitFrom(surf, Common::Point(0, 0));
+
+    RenderObject::init();
+}
+
+} // End of namespace UI
+} // End of namespace Nancy
diff --git a/engines/nancy/menu.h b/engines/nancy/ui/frame.h
similarity index 64%
rename from engines/nancy/menu.h
rename to engines/nancy/ui/frame.h
index f31681afb6..5047128b97 100644
--- a/engines/nancy/menu.h
+++ b/engines/nancy/ui/frame.h
@@ -20,43 +20,27 @@
  *
  */
 
-#ifndef NANCY_MENU_H
-#define NANCY_MENU_H
+#ifndef NANCY_UI_FRAME_H
+#define NANCY_UI_FRAME_H
 
-namespace Graphics {
-struct Surface;
-}
+#include "engines/nancy/renderobject.h"
 
 namespace Nancy {
+namespace UI {
 
-class NancyEngine;
-
-class MainMenu {
+class Frame : public RenderObject {
 public:
-    MainMenu(NancyEngine *engine) :
-        _engine(engine),
-        _state(kInit),
-        _surf(nullptr) {}
-
-    void process();
-
-private:
-    void init();
-    void startSound();
-    void run();
-
-    enum State {
-        kInit,
-        kStartSound,
-        kRun
-    };
-
-    NancyEngine *_engine;
-    State _state;
-    Graphics::Surface *_surf;
-    Graphics::Surface *_menuImage;
-    Graphics::Surface *_cursor;
+    Frame(NancyEngine *engine) : RenderObject(engine) {}
+    virtual ~Frame() =default;
+
+    virtual void init() override;
+
+protected:
+    virtual uint16 getZOrder() const override { return 0; }
+    virtual BlitType getBlitType() const override { return kNoTrans; }
 };
+
+} // End of namespace UI
 } // End of namespace Nancy
 
-#endif // NANCY_MENU_H
\ No newline at end of file
+#endif // NANCY_UI_FRAME_H
diff --git a/engines/nancy/ui/inventorybox.cpp b/engines/nancy/ui/inventorybox.cpp
new file mode 100644
index 0000000000..8ac17da5b4
--- /dev/null
+++ b/engines/nancy/ui/inventorybox.cpp
@@ -0,0 +1,266 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/nancy/ui/inventorybox.h"
+
+#include "engines/nancy/nancy.h"
+#include "engines/nancy/graphics.h"
+#include "engines/nancy/resource.h"
+#include "engines/nancy/util.h"
+#include "engines/nancy/cursor.h"
+#include "engines/nancy/state/scene.h"
+#include "engines/nancy/input.h"
+
+#include "common/events.h"
+
+namespace Nancy {
+namespace UI {
+
+void InventoryBox::init() {
+    Common::SeekableReadStream &stream = *_engine->getBootChunkStream("INV");
+    stream.seek(0, SEEK_SET);
+
+    readRect(stream, _sliderSource);
+    _sliderDefaultDest.x = stream.readUint16LE();
+    _sliderDefaultDest.y = stream.readUint16LE();
+
+    stream.seek(0xD6, SEEK_SET);
+    for (uint i = 0; i < 14; ++i) {
+        readRect(stream, _shadesSrc[i]);
+    }
+    readRect(stream, _screenPosition);
+    _shadesFrameTime = stream.readUint16LE();
+
+    char name[10];
+    stream.read(name, 10);
+    Common::String inventoryBoxIconsImageName = Common::String(name);
+    stream.read(name, 10);
+    _inventoryCursorsImageName = Common::String(name);
+
+    stream.skip(8);
+    readRect(stream, _emptySpace);
+
+    char itemName[0x14];
+    for (uint i = 0; i < 11; ++i) {
+        stream.read(itemName, 0x14);
+        _itemDescriptions[i].name = Common::String(itemName);
+        _itemDescriptions[i].oneTimeUse = stream.readUint16LE();
+        readRect(stream, _itemDescriptions[i].sourceRect);
+    }
+
+    _engine->_res->loadImage("ciftree", inventoryBoxIconsImageName, _iconsSurface);
+    
+    uint numItems = 11; // TODO
+    _fullInventorySurface.create(_screenPosition.width(), _screenPosition.height() * ((numItems / 4) + 1), GraphicsManager::pixelFormat);
+    Common::Rect sourceRect = _screenPosition;
+    sourceRect.moveTo(0, 0);
+    _drawSurface.create(_fullInventorySurface, sourceRect);
+
+    for (uint i = 0; i < 4; ++i) {
+        Common::Rect &r = _itemHotspots[i].hotspot;
+        r = _screenPosition;
+        r.setWidth(r.width() / 2);
+        r.setHeight(r.height() / 2);
+        r.translate((i % 2) * r.width(), (i / 2) * r.height());
+    }
+
+    RenderObject::init();
+
+    _scrollbar.init();
+    _shades.init();
+}
+
+void InventoryBox::registerGraphics() {
+    RenderObject::registerGraphics();
+    _scrollbar.registerGraphics();
+    _shades.registerGraphics();
+}
+
+void InventoryBox::handleInput(NancyInput &input) {
+    _scrollbar.handleInput(input);
+    
+    for (uint i = 0; i < 4; ++i) {
+        if (_itemHotspots[i].hotspot.contains(input.mousePos)) {
+            if (_engine->scene->getHeldItem() != -1) {
+                _engine->cursorManager->setCursorType(CursorManager::kHotspotArrow);
+                if (input.input & NancyInput::kLeftMouseButtonUp) {
+                    _engine->scene->addItemToInventory(_engine->scene->getHeldItem());
+                }                
+            } else if (_itemHotspots[i].itemID != -1) {
+                _engine->cursorManager->setCursorType(CursorManager::kHotspotArrow);
+                if (input.input & NancyInput::kLeftMouseButtonUp) {
+                    _engine->scene->removeItemFromInventory(_itemHotspots[i].itemID);
+                }
+            }
+            break;
+        }
+    }
+}
+
+void InventoryBox::addItem(uint itemID) {
+    if (_order.size() == 0) {
+        // Adds first item, start shades animation
+        _shades.setOpen(true);
+    }
+    Common::Array<uint> back = _order;
+    _order.clear();
+    _order.push_back(itemID);
+    _order.push_back(back);
+
+    onReorder();
+}
+
+void InventoryBox::removeItem(uint itemID, bool hold) {
+    for (auto &i : _order) {
+        if (i == itemID) {
+            if (_order.size() == 1) {
+                // Removes last item, start shades animation
+                _shades.setOpen(false);
+            }
+            _order.erase(&i);
+            onReorder();
+            break;
+        }
+    }
+}
+
+void InventoryBox::onReorder() {
+    _fullInventorySurface.clear();
+    for (uint i = 0; i < _order.size(); ++i) {
+        Common::Rect dest;
+        dest.setWidth(_screenPosition.width() / 2);
+        dest.setHeight(_screenPosition.height() / 2);
+        dest.moveTo((i % 2) * dest.width(), (i / 2) * dest.height());
+        Common::Point destPoint = Common::Point (dest.left, dest.top);
+
+        _fullInventorySurface.blitFrom(_iconsSurface, _itemDescriptions[_order[i]].sourceRect, destPoint);
+    }
+    
+    // Scroll one page up if current on becomes empty
+    uint curPage = _drawSurface.getOffsetFromOwner().y / _screenPosition.height();
+    if (_order.size() < curPage * 4) {
+        --curPage;
+        onScrollbarMove();
+    }
+
+    setHotspots(curPage);
+
+    _needsRedraw = true;
+}
+
+void InventoryBox::setHotspots(uint pageNr) {
+    for (uint i = 0; i < 4; ++i) {
+        if (i + pageNr * 4 < _order.size()) {
+            _itemHotspots[i].itemID = _order[i +  pageNr * 4];
+        } else {
+            _itemHotspots[i].itemID = -1;
+        }
+    }
+}
+
+void InventoryBox::onScrollbarMove() {
+    float scrollPos = _scrollbar.getPos();
+
+    uint numPages = _order.size() / 4 + 1;
+    float pageFrac = 1 / numPages;
+    uint curPage = scrollPos / pageFrac;
+
+    Common::Rect sourceRect = _screenPosition;
+    sourceRect.moveTo(0, curPage * sourceRect.height());
+    _drawSurface.create(_fullInventorySurface, sourceRect);
+
+    setHotspots(curPage);
+
+    _needsRedraw = true;
+}
+
+void InventoryBox::InventoryScrollbar::init() {
+    Common::Rect &srcBounds = _parent->_sliderSource;
+    Common::Point &topPosition = _parent->_sliderDefaultDest;
+
+    _drawSurface.create(_engine->graphicsManager->object0, srcBounds);
+
+    _startPosition = topPosition;
+    _startPosition.x -= srcBounds.width() / 2;
+
+    _screenPosition = srcBounds;
+    _screenPosition.moveTo(_startPosition);
+    
+    _maxDist = _parent->getBounds().height() - _drawSurface.h;
+    
+    RenderObject::init();
+}
+
+void InventoryBox::Shades::init() {
+    Common::Rect bounds = _parent->getBounds();
+    _drawSurface.create(bounds.width(), bounds.height(), GraphicsManager::pixelFormat);
+    _screenPosition = _parent->getScreenPosition();
+    _curFrame = 0;
+    _areOpen = false;
+    setAnimationFrame(_curFrame);
+
+    RenderObject::init();
+}
+
+void InventoryBox::Shades::updateGraphics() {
+    Time time = _engine->getTotalPlayTime();
+    if (_areOpen) {
+        if (_curFrame < 7 && time > _nextFrameTime) {
+            setAnimationFrame(++_curFrame);
+            _nextFrameTime = time + _parent->_shadesFrameTime;
+        }
+    } else {
+        if (_curFrame > 0 && time > _nextFrameTime) {
+            setAnimationFrame(--_curFrame);
+            _nextFrameTime = time + _parent->_shadesFrameTime;
+        }
+    }
+}
+
+void InventoryBox::Shades::setAnimationFrame(uint frame) {
+    Graphics::ManagedSurface &object0 = _engine->graphicsManager->object0;
+    Common::Rect srcRect;
+    Common::Point destPoint;
+
+    if (frame > 6) {
+        setVisible(false);
+        return;
+    } else {
+        setVisible(true);
+    }
+
+    _drawSurface.clear(GraphicsManager::transColor);
+
+    // Draw left shade
+    srcRect = _parent->_shadesSrc[frame * 2];
+    _drawSurface.blitFrom(object0, srcRect, destPoint);
+
+    // Draw right shade
+    srcRect = _parent->_shadesSrc[frame * 2 + 1];
+    destPoint.x = getBounds().width() - srcRect.width();
+    _drawSurface.blitFrom(object0, srcRect, destPoint);
+
+    _needsRedraw = true;
+}
+
+} // End of namespace UI
+} // End of namespace Nancy
diff --git a/engines/nancy/ui/inventorybox.h b/engines/nancy/ui/inventorybox.h
new file mode 100644
index 0000000000..8673713b5d
--- /dev/null
+++ b/engines/nancy/ui/inventorybox.h
@@ -0,0 +1,149 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef NANCY_UI_INVENTORYBOX_H
+#define NANCY_UI_INVENTORYBOX_H
+
+#include "engines/nancy/renderobject.h"
+
+#include "engines/nancy/ui/scrollbar.h"
+#include "engines/nancy/time.h"
+
+#include "common/array.h"
+#include "common/str.h"
+#include "common/rect.h"
+
+namespace Nancy {
+
+class NancyEngine;
+struct NancyInput;
+
+namespace UI {
+
+class InventoryBox : public RenderObject {
+	friend class InventoryScrollbar;
+    friend class Shades;
+
+public:
+    struct ItemDescription {
+        Common::String name; // 0x00
+        byte oneTimeUse = 0; // 0x14
+        Common::Rect sourceRect; // 0x16
+    };
+
+    InventoryBox(RenderObject &redrawFrom) :
+        RenderObject(redrawFrom),
+        _scrollbar(redrawFrom, this),
+        _shades(*this, this) {}
+
+    virtual ~InventoryBox() { _fullInventorySurface.free(); _iconsSurface.free(); }
+
+    virtual void init() override;
+    virtual void registerGraphics() override;
+    void handleInput(NancyInput &input);
+
+    // To be called from Scene
+    void addItem(uint itemID);
+    void removeItem(uint itemID, bool hold = true);
+
+    ItemDescription getItemDescription(uint id) { return _itemDescriptions[id]; }
+
+protected:
+    virtual uint16 getZOrder() const override { return 6; }
+    virtual BlitType getBlitType() const override { return kNoTrans; }
+
+    void onScrollbarMove();
+
+private:
+    void onReorder();
+    void setHotspots(uint pageNr);
+
+    class InventoryScrollbar : public Scrollbar {
+    public:
+        InventoryScrollbar(RenderObject &redrawFrom, InventoryBox *parent) :
+            Scrollbar(redrawFrom),
+            _parent(parent) {}
+        virtual ~InventoryScrollbar() =default;
+
+        virtual void init() override;
+
+    protected:
+        InventoryBox *_parent;
+    };
+
+    class Shades : public RenderObject {
+    public:
+        Shades(RenderObject &redrawFrom, InventoryBox *parent) :
+            RenderObject(redrawFrom),
+            _parent(parent) {}
+        virtual ~Shades() =default;
+
+        virtual void init() override;
+        virtual void updateGraphics() override;
+
+        void setOpen(bool open) { _areOpen = open; }
+
+    protected:
+        virtual uint16 getZOrder() const override { return 9; }
+        virtual BlitType getBlitType() const override { return kTrans; }
+
+        void setAnimationFrame(uint frame);
+
+        InventoryBox *_parent;
+
+        uint _curFrame;
+        Time _nextFrameTime;
+        bool _areOpen;      
+    };
+
+    struct ItemHotspot {
+        int16 itemID = -1;
+        Common::Rect hotspot; // in screen coordinates
+    };
+
+    Graphics::Surface _iconsSurface;
+    Graphics::ManagedSurface _fullInventorySurface;
+
+    InventoryScrollbar _scrollbar;
+    Shades _shades;
+
+    Common::Array<uint> _order;
+    ItemHotspot _itemHotspots[4]; 
+
+    // INV contents
+    Common::Rect _sliderSource; // 0x00
+    Common::Point _sliderDefaultDest; // 0x10
+    //...
+    Common::Rect _shadesSrc[14]; // 0xD6
+    // _screenPosition 0x1B6
+    uint16 _shadesFrameTime; // 0x1C6
+    Common::String _inventoryCursorsImageName; // 0x1D2, should this be here?
+
+    Common::Rect _emptySpace; // 0x1E4
+    ItemDescription _itemDescriptions[11]; // 0x1F4
+
+};
+
+} // End of namespace UI
+} // End of namespace Nancy
+
+#endif // NANCY_UI_INVENTORYBOX_H
diff --git a/engines/nancy/ui/scrollbar.cpp b/engines/nancy/ui/scrollbar.cpp
new file mode 100644
index 0000000000..8de7a7dfd8
--- /dev/null
+++ b/engines/nancy/ui/scrollbar.cpp
@@ -0,0 +1,84 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/nancy/ui/scrollbar.h"
+
+#include "engines/nancy/nancy.h"
+#include "engines/nancy/commontypes.h"
+#include "engines/nancy/input.h"
+#include "engines/nancy/cursor.h"
+
+#include "common/stream.h"
+#include "common/random.h"
+#include "common/events.h"
+
+namespace Nancy {
+namespace UI {
+
+void Scrollbar::handleInput(NancyInput &input) {
+    if (_screenPosition.contains(input.mousePos)) {
+        _engine->cursorManager->setCursorType(CursorManager::kHotspotArrow);
+
+        if (input.input & NancyInput::kLeftMouseButtonDown && !_isClicked) {
+            // Begin click and hold
+            _isClicked = true;
+            _mousePosOnClick = input.mousePos - Common::Point(_screenPosition.left, _screenPosition.top);
+        }
+
+        if (input.input & NancyInput::kRightMouseButtonUp) {
+            // Right click, reset position
+            resetPosition();
+        }
+
+        if (_isClicked) {
+            // Is currently clicked, handle movement
+            Common::Point newMousePos = input.mousePos - Common::Point(_screenPosition.left, _screenPosition.top);
+
+            if (newMousePos != _mousePosOnClick) {
+                uint16 minY = _startPosition.y;
+                uint16 maxY = minY + _maxDist;
+                uint16 newTop = CLIP<uint16>((_screenPosition.top + newMousePos.y - _mousePosOnClick.y), minY, maxY);
+                moveTo(Common::Point(_screenPosition.left, newTop));
+
+                calculatePosition();
+            }   
+        }
+    }
+
+    if (input.input & NancyInput::kLeftMouseButtonUp) {
+        _isClicked = false;
+    }
+}
+
+void Scrollbar::calculatePosition() {
+    uint16 scrollY = _screenPosition.top - _startPosition.y;
+
+    _currentPosition = scrollY != 0 ? (float)scrollY / (float)_maxDist : 0;
+}
+
+void Scrollbar::resetPosition() {
+    moveTo(Common::Point(_screenPosition.left, _startPosition.y));
+    calculatePosition();
+}
+
+} // End of namespace UI
+} // End of namespace Nancy
diff --git a/engines/nancy/ui/scrollbar.h b/engines/nancy/ui/scrollbar.h
new file mode 100644
index 0000000000..44783c5da9
--- /dev/null
+++ b/engines/nancy/ui/scrollbar.h
@@ -0,0 +1,68 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef NANCY_UI_SCROLLBAR_H
+#define NANCY_UI_SCROLLBAR_H
+
+#include "engines/nancy/renderobject.h"
+
+#include "common/rect.h"
+
+namespace Nancy {
+
+struct NancyInput;
+
+namespace UI {
+
+class Scrollbar : public RenderObject {
+public:
+    Scrollbar(RenderObject &redrawFrom) :
+        RenderObject(redrawFrom),
+        _isClicked(false),
+        _currentPosition(0),
+        _maxDist(0) {}
+    virtual ~Scrollbar() =default;
+
+    void handleInput(NancyInput &input);
+
+    void resetPosition();
+    float getPos() const { return _currentPosition; }
+
+protected:
+    virtual uint16 getZOrder() const override { return 9; }
+    virtual BlitType getBlitType() const override { return kTrans; }
+
+    void calculatePosition();
+
+    Common::Point _startPosition;
+    uint _maxDist;
+
+    float _currentPosition;
+
+    bool _isClicked;
+    Common::Point _mousePosOnClick;
+};
+
+} // End of namespace UI
+} // End of namespace Nancy
+
+#endif // NANCY_UI_SCROLLBAR_H
diff --git a/engines/nancy/ui/textbox.cpp b/engines/nancy/ui/textbox.cpp
new file mode 100644
index 0000000000..3df6b940d4
--- /dev/null
+++ b/engines/nancy/ui/textbox.cpp
@@ -0,0 +1,288 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/nancy/ui/textbox.h"
+
+#include "engines/nancy/nancy.h"
+#include "engines/nancy/resource.h"
+#include "engines/nancy/state/scene.h"
+#include "engines/nancy/graphics.h"
+#include "engines/nancy/util.h"
+#include "engines/nancy/cursor.h"
+#include "engines/nancy/input.h"
+
+#include "common/error.h"
+#include "common/util.h"
+#include "common/events.h"
+
+namespace Nancy {
+namespace UI {
+
+const Common::String Textbox::beginToken = Common::String("<i>");
+const Common::String Textbox::endToken = Common::String("<o>");
+const Common::String Textbox::colorBeginToken = Common::String("<c1>");
+const Common::String Textbox::colorEndToken = Common::String("<c0>");
+const Common::String Textbox::hotspotToken = Common::String("<h>");
+const Common::String Textbox::newLineToken = Common::String("<n>");
+
+void Textbox::init() {    
+    Common::SeekableReadStream *chunk = _engine->getBootChunkStream("TBOX");
+    chunk->seek(0);
+    readRect(*chunk, _scrollbarSourceBounds);
+
+    chunk->seek(0x20);
+    Common::Rect innerBoundingBox;
+    readRect(*chunk, innerBoundingBox);
+    _fullSurface.create(innerBoundingBox.width(), innerBoundingBox.height(), GraphicsManager::pixelFormat);
+    
+    _scrollbarDefaultDest.x = chunk->readUint16LE();
+    _scrollbarDefaultDest.y = chunk->readUint16LE();
+
+    chunk->seek(0x36);
+    _firstLineOffset = chunk->readUint16LE();
+    _lineHeight = chunk->readUint16LE();
+    // Not sure why but to get exact results we subtract 1
+    _borderWidth = chunk->readUint16LE() - 1;
+
+    chunk = _engine->getBootChunkStream("BSUM");
+    chunk->seek(0x164);
+    readRect(*chunk, _screenPosition);
+
+    Common::Rect outerBoundingBox = _screenPosition;
+    outerBoundingBox.moveTo(0, 0);
+    _drawSurface.create(_fullSurface, outerBoundingBox);
+
+    RenderObject::init();
+
+    _scrollbar.init();
+}
+
+void Textbox::registerGraphics() {
+    RenderObject::registerGraphics();
+    _scrollbar.registerGraphics();
+}
+
+void Textbox::updateGraphics() {
+    if (_needsTextRedraw) {
+        drawTextbox();
+    }
+
+    if (_scrollbarPos != _scrollbar.getPos()) {
+        _scrollbarPos = _scrollbar.getPos();
+
+        onScrollbarMove();
+    }
+
+    RenderObject::updateGraphics();
+}
+void Textbox::handleInput(NancyInput &input) {
+    _scrollbar.handleInput(input);
+
+    for (uint i = 0; i < _responses.size(); ++i) {
+        Common::Rect hotspot = _responses[i].hotspot;
+        hotspot.translate(0, -_drawSurface.getOffsetFromOwner().y);
+        if (convertToScreen(hotspot).findIntersectingRect(_screenPosition).contains(input.mousePos)) {
+            _engine->cursorManager->setCursorType(CursorManager::kHotspotArrow);
+
+            if (input.input & NancyInput::kLeftMouseButtonUp) {
+                input.input &= ~NancyInput::kLeftMouseButtonUp;
+                _engine->scene->clearLogicConditions();
+                _engine->scene->setLogicCondition(i);
+            }
+            
+            break;
+        }
+    }
+}
+
+void Textbox::drawTextbox() {
+    Common::Array<Common::String> wrappedLines;
+    Common::String tokenlessString;
+    _numLines = 0;
+    // Hardcode to 1 until I figure out the proper logic
+    Font *font = _engine->graphicsManager->getFont(1);
+
+    uint maxWidth = _fullSurface.w - _borderWidth;
+    uint lineDist = _lineHeight + _lineHeight / 4;
+
+    Common::String line;
+
+    _mainString.trim();
+
+    // Scan for and remove tokens from main string
+    if (_mainString.hasPrefix(beginToken) && _mainString.hasSuffix(endToken)) {
+        tokenlessString = _mainString.substr(beginToken.size(), _mainString.size() - beginToken.size() - endToken.size());
+        if (tokenlessString.hasSuffix(newLineToken)) {
+            tokenlessString = tokenlessString.substr(0, tokenlessString.size() - newLineToken.size());
+        }
+    }
+
+    // Wrap text for main string
+    font->wordWrapText(tokenlessString, maxWidth, wrappedLines);
+
+    // Draw main string
+    for (auto &str : wrappedLines) {
+        font->drawString(&_fullSurface, str, _borderWidth, _firstLineOffset - font->getFontHeight() + _numLines * lineDist, maxWidth, 0);
+        ++_numLines;
+    }
+
+    for (auto &res : _responses) {
+        // Scan for tokens
+        ++_numLines;
+        bool newLineAtEnd = false;
+        uint colorCharOffset = 0;
+        uint colorPixelOffset = 0;
+
+        res.text.trim();
+
+        tokenlessString.clear();
+        wrappedLines.clear();
+
+        if (res.text.hasPrefix(colorBeginToken)) {
+            // Create a substring with all (usually just one) of the colored characters
+            for (uint i = colorBeginToken.size(); res.text.substr(i, colorEndToken.size()) != colorEndToken; ++i) {
+                tokenlessString += res.text[i];
+            }
+
+            // Draw the color string
+            font->drawString(&_fullSurface, tokenlessString, _borderWidth, _firstLineOffset - font->getFontHeight() + _numLines * lineDist, maxWidth, 1);
+            colorCharOffset = tokenlessString.size() + colorBeginToken.size() + colorEndToken.size();
+            colorPixelOffset = font->getStringWidth(tokenlessString);
+        }
+
+        tokenlessString = res.text.substr(colorCharOffset, res.text.size() - colorCharOffset);
+
+
+        // Remove the new line token and add a line break
+        if (tokenlessString.hasSuffix(newLineToken)) {
+            newLineAtEnd = true;
+            tokenlessString = tokenlessString.substr(0, tokenlessString.size() - newLineToken.size());
+        }
+
+        // Remove the hotspot token
+        if (tokenlessString.hasSuffix(hotspotToken)) {
+            tokenlessString = tokenlessString.substr(0, tokenlessString.size() - hotspotToken.size());
+        }
+
+        // Word wrap the response
+        font->wordWrapText(tokenlessString, maxWidth, wrappedLines, colorPixelOffset);
+
+        res.hotspot.left = _borderWidth;
+        res.hotspot.top = _firstLineOffset - font->getFontHeight() + (_numLines + 1) * lineDist;
+        res.hotspot.setHeight((wrappedLines.size() - 1) * lineDist + _lineHeight);
+        res.hotspot.setWidth(0);
+
+        // Draw the response
+        for (uint i = 0; i < wrappedLines.size(); ++i) {
+            font->drawString(&_fullSurface, wrappedLines[i], _borderWidth + (i == 0 ? colorPixelOffset : 0), _firstLineOffset - font->getFontHeight() + _numLines * lineDist, maxWidth, 0);
+            // Set the response's hotspot width
+            res.hotspot.setWidth(MAX<int16>(res.hotspot.width(), font->getStringWidth(wrappedLines[i]) + (i == 0 ? colorPixelOffset : 0)));
+            ++_numLines;
+        }
+
+        // Add a line break if there was a new line token
+        if (!newLineAtEnd) {
+            --_numLines;
+        }
+    }
+
+    setVisible(true);
+    _needsTextRedraw = false;
+}
+
+void Textbox::clear() {
+    _fullSurface.clear();
+    _mainString.clear();
+    _responses.clear();
+    _scrollbar.resetPosition();
+    _numLines = 0;
+    onScrollbarMove();
+    _needsRedraw = true;
+}
+
+void Textbox::addTextLine(const Common::String &text) {
+    // Scan for the hotspot token and assume the text is the main text if not found
+    if (text.contains(hotspotToken)) {
+        _responses.push_back(Response());
+        _responses.back().text = text;
+    } else {
+        _mainString = text;
+    }
+    
+    _needsTextRedraw = true;
+}
+
+// A text line will often be broken up into chunks separated by nulls, use
+// this function to put it back together as a Common::String
+void Textbox::assembleTextLine(char *rawCaption, Common::String &output, uint size) {
+    for (uint i = 0; i < size; ++i) {
+        // A single line can be broken up into bits, look for them and
+        // concatenate them when we're done
+        if (rawCaption[i] != 0) {
+            Common::String newBit(rawCaption + i);
+            output += newBit;
+            i += newBit.size();
+        }
+    }
+}
+
+void Textbox::onScrollbarMove() {
+    _scrollbarPos = CLIP<float>(_scrollbarPos, 0, 1);
+
+    uint16 inner = getInnerHeight();
+    uint16 outer = _screenPosition.height();
+
+    if (inner > outer) {
+        Common::Rect bounds = getBounds();
+        bounds.moveTo(0, (inner - outer) * _scrollbarPos);
+        _drawSurface.create(_fullSurface, bounds);
+    } else {
+        _drawSurface.create(_fullSurface, getBounds());
+    }
+
+    _needsRedraw = true;


Commit: ebe99bc27194efb413e8a2f145c460ba63f5d04b
    https://github.com/scummvm/scummvm/commit/ebe99bc27194efb413e8a2f145c460ba63f5d04b
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Fix adding items to inventory

Fixed items not being marked as owned even when they're in the inventory.

Changed paths:
    engines/nancy/state/scene.cpp


diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index 4ec0c99790..b817f70165 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -86,6 +86,7 @@ void Scene::popScene() {
 }
 
 void Scene::addItemToInventory(uint16 id) {
+    _flags.items[id] = kTrue;
     if (_flags.heldItem == id) {
         setHeldItem(-1);
     }


Commit: 1de1f568d7cec06abed9ec52b988acb44573c2ad
    https://github.com/scummvm/scummvm/commit/1de1f568d7cec06abed9ec52b988acb44573c2ad
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Fix secondary movie

Fix playback of secondary movies, which was overlooked during the engine rewrite.

Changed paths:
    engines/nancy/action/secondaryvideo.cpp
    engines/nancy/action/secondaryvideo.h


diff --git a/engines/nancy/action/secondaryvideo.cpp b/engines/nancy/action/secondaryvideo.cpp
index 4f19f31d62..9c6fa7ccfc 100644
--- a/engines/nancy/action/secondaryvideo.cpp
+++ b/engines/nancy/action/secondaryvideo.cpp
@@ -234,7 +234,9 @@ void PlaySecondaryMovie::init() {
     if(_decoder.isVideoLoaded()) {
         _decoder.close();
     }
-    _drawSurface.create(_decoder.getWidth(), _decoder.getHeight());
+    _decoder.loadFile(videoName + ".avf");
+    _drawSurface.create(_decoder.getWidth(), _decoder.getHeight(), GraphicsManager::pixelFormat);
+    _screenPosition = _drawSurface.getBounds();
 
     RenderObject::init();
 }
@@ -244,19 +246,18 @@ void PlaySecondaryMovie::updateGraphics() {
         return;
     }
 
-    if (!_decoder.isPlaying()) {
+    if (!_decoder.isPlaying() && _isVisible) {
         _decoder.start();
     }
 
-    if (_decoder.needsUpdate() && !_screenPosition.isEmpty()) {
-        const Graphics::Surface *surf = _decoder.decodeNextFrame();
-        int descID = -1;
+    if (_decoder.needsUpdate()) {
+        uint descID = 0;
         for (uint i = 0; i < videoDescs.size(); ++i) {
-            if (videoDescs[i].frameID == _decoder.getCurFrame()) {
+            if (videoDescs[i].frameID == _curViewportFrame) {
                 descID = i;
             }
         }
-        _drawSurface.blitFrom(*surf, videoDescs[descID].srcRect, Common::Point());
+        _drawSurface.blitFrom(*_decoder.decodeNextFrame(), videoDescs[descID].srcRect, Common::Point());
         _needsRedraw = true;
     } else {
         // Set flag if not drawing new frame
@@ -274,6 +275,7 @@ void PlaySecondaryMovie::execute(NancyEngine *engine) {
     switch (state) {
         case kBegin:
             init();
+            registerGraphics();
             if (soundName != "NO SOUND") {
                 engine->sound->loadSound(soundName, soundChannel, 1, soundVolume);
             }
@@ -282,24 +284,30 @@ void PlaySecondaryMovie::execute(NancyEngine *engine) {
         case kRun: {
             engine->cursorManager->showCursor(false);
             
-            int activeFrame = -1;
+            int newFrame = _engine->scene->getSceneInfo().frameID;
 
-            for (uint i = 0; i < videoDescs.size(); ++i) {
-                if (_engine->scene->getSceneInfo().frameID == videoDescs[i].frameID) {
-                    activeFrame = i;
+            if (newFrame != _curViewportFrame) {
+                _curViewportFrame = newFrame;
+                int activeFrame = -1;
+                for (uint i = 0; i < videoDescs.size(); ++i) {
+                    if (newFrame == videoDescs[i].frameID) {
+                        activeFrame = i;
+                        break;
+                    }
                 }
-            }
 
-            if (activeFrame != -1) {
-                _screenPosition = videoDescs[activeFrame].destRect;
+                if (activeFrame != -1) {
+                    _screenPosition = videoDescs[activeFrame].destRect;
+
+                    // Start sound if any
+                    if (soundName != "NO SOUND") {
+                        engine->sound->pauseSound(soundChannel, false);
+                    }
 
-                // Start sound if any
-                if (soundName != "NO SOUND") {
-                    engine->sound->pauseSound(soundChannel, false);
+                    setVisible(true);
+                } else {
+                    setVisible(false);
                 }
-            } else {
-                _screenPosition = Common::Rect();
-                _needsRedraw = true;
             }
 
             break;
diff --git a/engines/nancy/action/secondaryvideo.h b/engines/nancy/action/secondaryvideo.h
index bd98177108..a3ccb8edd6 100644
--- a/engines/nancy/action/secondaryvideo.h
+++ b/engines/nancy/action/secondaryvideo.h
@@ -93,7 +93,7 @@ public:
     PlaySecondaryMovie(RenderObject &redrawFrom) : RenderObject(redrawFrom) {}
     virtual ~PlaySecondaryMovie() { _decoder.close(); }
 
-    virtual void init()override;
+    virtual void init() override;
     virtual void updateGraphics() override;
 
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
@@ -117,6 +117,7 @@ protected:
     virtual bool isViewportRelative() const override { return true; }
 
     AVFDecoder _decoder;
+    int _curViewportFrame = -1;
 };
 
 } // End of namespace Action


Commit: c73ea1b0cbf73e9bf013b48c13baf979b0a84e01
    https://github.com/scummvm/scummvm/commit/c73ea1b0cbf73e9bf013b48c13baf979b0a84e01
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Viewport movement fixes

Added code so viewport edges do not get highlighted in directions the player can't go in.

Changed paths:
    engines/nancy/state/map.cpp
    engines/nancy/state/scene.cpp
    engines/nancy/ui/viewport.cpp
    engines/nancy/ui/viewport.h


diff --git a/engines/nancy/state/map.cpp b/engines/nancy/state/map.cpp
index 685d38e7aa..f8dbd140dd 100644
--- a/engines/nancy/state/map.cpp
+++ b/engines/nancy/state/map.cpp
@@ -69,7 +69,7 @@ void Map::init() {
     Common::String n(name);
 
     _viewport.loadVideo(n, 0, 0);
-    _viewport.setEdges(0, 0, 0, 0);
+    _viewport.setEdgesSize(0, 0, 0, 0);
 
     // Load the audio
     chunk->seek(0x18 + _mapID * 0x20, SEEK_SET);
diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index b817f70165..d301b249b5 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -179,11 +179,11 @@ void Scene::load() {
 
     _viewport.loadVideo(_sceneState.summary.videoFile, _sceneState.nextScene.frameID, _sceneState.nextScene.verticalOffset);
 
+    // TODO TEMPORARY
+    _viewport.setEdgesSize(25, 25, 25, 25);
+
     if (_viewport.getFrameCount() <= 1) {
-        _viewport.setEdges(-1, -1, 0, 0);
-    } else {
-        // TODO TEMPORARY
-        _viewport.setEdges(-1, -1, 25, 25);
+        _viewport.disableEdges(kLeft | kRight);
     }
 
     if (!hasLoadedFromSavefile) {
@@ -205,10 +205,7 @@ void Scene::load() {
             // TODO not sure this ever gets hit
         } else if (_sceneState.summary.videoFormat == 2) {
             if (_viewport.getMaxScroll() == 0) {
-                _viewport.setEdges(0, 0, -1, -1);
-            } else {
-                // TODO TEMPORARY
-                _viewport.setEdges(25, 25, -1, -1);
+                _viewport.disableEdges(kUp | kDown);
             }
         }
     }
diff --git a/engines/nancy/ui/viewport.cpp b/engines/nancy/ui/viewport.cpp
index 396f8c62a6..7f8146483a 100644
--- a/engines/nancy/ui/viewport.cpp
+++ b/engines/nancy/ui/viewport.cpp
@@ -64,16 +64,57 @@ void Viewport::handleInput(NancyInput &input) {
         _engine->cursorManager->setCursorType(CursorManager::kNormal);
     }
 
-    if (_upHotspot.contains(input.mousePos)) {
-        direction |= kUp;
-    } else if (_downHotspot.contains(input.mousePos)) {
-        direction |= kDown;
+    // Do not handle hotspots marked as incative and ignore diagonals if intersecting hotspots are not active
+    if (_upHotspot.contains(input.mousePos) && (_edgesMask & kUp) == 0) {
+        if (_upHotspot.findIntersectingRect(_leftHotspot).contains(input.mousePos)) {
+            if ((_edgesMask & kLeft) == 0) {
+                direction |= kUp;
+            }
+        } else if (_upHotspot.findIntersectingRect(_rightHotspot).contains(input.mousePos)) {
+            if ((_edgesMask & kRight) == 0) {
+                direction |= kUp;
+            }
+        } else {
+            direction |= kUp;
+        }
+    } else if (_downHotspot.contains(input.mousePos) && (_edgesMask & kDown) == 0) {
+        if (_downHotspot.findIntersectingRect(_leftHotspot).contains(input.mousePos)) {
+            if ((_edgesMask & kLeft) == 0) {
+                direction |= kDown;
+            }
+        } else if (_downHotspot.findIntersectingRect(_rightHotspot).contains(input.mousePos)) {
+            if ((_edgesMask & kRight) == 0) {
+                direction |= kDown;
+            }
+        } else {
+            direction |= kDown;
+        }
     }
 
-    if (_leftHotspot.contains(input.mousePos)) {
-        direction |= kLeft;
-    } else if (_rightHotspot.contains(input.mousePos)) {
-        direction |= kRight;
+    if (_leftHotspot.contains(input.mousePos) && (_edgesMask & kLeft) == 0) {
+        if (_leftHotspot.findIntersectingRect(_upHotspot).contains(input.mousePos)) {
+            if ((_edgesMask & kUp) == 0) {
+                direction |= kLeft;
+            }
+        } else if (_leftHotspot.findIntersectingRect(_downHotspot).contains(input.mousePos)) {
+            if ((_edgesMask & kDown) == 0) {
+                direction |= kLeft;
+            }
+        } else {
+            direction |= kLeft;
+        }
+    } else if (_rightHotspot.contains(input.mousePos) && (_edgesMask & kRight) == 0) {
+        if (_rightHotspot.findIntersectingRect(_upHotspot).contains(input.mousePos)) {
+            if ((_edgesMask & kUp) == 0) {
+                direction |= kRight;
+            }
+        } else if (_rightHotspot.findIntersectingRect(_downHotspot).contains(input.mousePos)) {
+            if ((_edgesMask & kDown) == 0) {
+                direction |= kRight;
+            }
+        } else {
+            direction |= kRight;
+        }
     }
 
     if (direction) {
@@ -145,6 +186,8 @@ void Viewport::loadVideo(const Common::String &filename, uint frameNr, uint vert
     }
     _decoder.loadFile(filename + ".avf");
     
+    enableEdges(kUp | kDown | kLeft | kRight);
+    
     setFrame(frameNr);
     setVerticalScroll(verticalScroll); 
 }
@@ -180,6 +223,16 @@ void Viewport::setVerticalScroll(uint scroll) {
     sourceBounds.moveTo(0, scroll + 1);
     _drawSurface.create(_fullFrame, sourceBounds);
     _needsRedraw = true;
+
+    if (scroll == getMaxScroll()) {
+        disableEdges(kDown);
+        enableEdges(kUp);
+    } else if (scroll == 0) {
+        disableEdges(kUp);
+        enableEdges(kDown);
+    } else {
+        enableEdges(kUp | kDown);
+    }
     
     _engine->scene->getSceneInfo().verticalOffset = scroll;
 }
@@ -216,26 +269,25 @@ Common::Rect Viewport::convertScreenToViewport(const Common::Rect &viewportRect)
     return ret;
 }
 
-void Viewport::setEdges(int16 upSize, int16 downSize, int16 leftSize, int16 rightSize) {
-    if (upSize > -1) {
-        _upHotspot.setHeight(upSize);
-    }
+void Viewport::setEdgesSize(uint16 upSize, uint16 downSize, uint16 leftSize, uint16 rightSize) {
+    _upHotspot.setHeight(upSize);
+    _leftHotspot.setWidth(leftSize);
 
-    if (leftSize > -1) {
-        _leftHotspot.setWidth(leftSize);
-    }
+    _downHotspot.top = _screenPosition.bottom;
+    _downHotspot.setHeight(downSize);
+    _downHotspot.translate(0, -downSize);
+    
+    _rightHotspot.left = _screenPosition.right;
+    _rightHotspot.setWidth(rightSize);
+    _rightHotspot.translate(-rightSize, 0);
+}
 
-    if (downSize > -1) {
-        _downHotspot.top = _screenPosition.bottom;
-        _downHotspot.setHeight(downSize);
-        _downHotspot.translate(0, -downSize);
-    }
+void Viewport::disableEdges(byte edges) {
+    _edgesMask |= edges;
+}
 
-    if (rightSize > -1) {
-        _rightHotspot.left = _screenPosition.right;
-        _rightHotspot.setWidth(rightSize);
-        _rightHotspot.translate(-rightSize, 0);
-    }
+void Viewport::enableEdges(byte edges) {
+    _edgesMask &= ~edges;
 }
 
 } // End of namespace UI
diff --git a/engines/nancy/ui/viewport.h b/engines/nancy/ui/viewport.h
index 51cddd7e7a..078713e3f2 100644
--- a/engines/nancy/ui/viewport.h
+++ b/engines/nancy/ui/viewport.h
@@ -42,7 +42,8 @@ class Viewport : public Nancy::RenderObject {
 public:
     Viewport(NancyEngine *engine) :
         RenderObject(engine),
-        _movementLastFrame(0)  {}
+        _movementLastFrame(0),
+        _edgesMask(0)  {}
     virtual ~Viewport() { _decoder.close(); _fullFrame.free(); }
 
     virtual void init() override;
@@ -71,7 +72,9 @@ public:
     Common::Rect convertScreenToViewport(const Common::Rect &viewportRect) const;
 
     // 0 is inactive, -1 is keep unchanged
-    void setEdges(int16 upSize, int16 downSize, int16 leftSize, int16 rightSize);
+    void setEdgesSize(uint16 upSize, uint16 downSize, uint16 leftSize, uint16 rightSize);
+    void disableEdges(byte edges);
+    void enableEdges(byte edges);
 
 protected:
     virtual uint16 getZOrder() const override { return 6; }
@@ -81,6 +84,8 @@ protected:
     Common::Rect _downHotspot;
     Common::Rect _leftHotspot;
     Common::Rect _rightHotspot;
+
+    byte _edgesMask;
     
     byte _movementLastFrame;
     Time _nextMovementTime;


Commit: e4890a18fc6cf18c004025282305f1cfda26961d
    https://github.com/scummvm/scummvm/commit/e4890a18fc6cf18c004025282305f1cfda26961d
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Sound manager improvements

The original engine stores sound information in at least four different types of structs, all of which can now be loaded and played back. Also implemented sound types, which will be configurable from the ScummVM interface, added the correct number of sound channels, and implemented sound fx in a couple of action record types.

Changed paths:
  A engines/nancy/sound.cpp
  A engines/nancy/sound.h
  R engines/nancy/audio.cpp
  R engines/nancy/audio.h
    engines/nancy/action/actionmanager.cpp
    engines/nancy/action/primaryvideo.cpp
    engines/nancy/action/primaryvideo.h
    engines/nancy/action/recordtypes.cpp
    engines/nancy/action/recordtypes.h
    engines/nancy/action/secondaryvideo.cpp
    engines/nancy/action/secondaryvideo.h
    engines/nancy/console.cpp
    engines/nancy/module.mk
    engines/nancy/nancy.cpp
    engines/nancy/nancy.h
    engines/nancy/state/logo.cpp
    engines/nancy/state/logo.h
    engines/nancy/state/map.cpp
    engines/nancy/state/map.h
    engines/nancy/state/scene.cpp
    engines/nancy/state/scene.h


diff --git a/engines/nancy/action/actionmanager.cpp b/engines/nancy/action/actionmanager.cpp
index 39e4ef4815..41e7864caf 100644
--- a/engines/nancy/action/actionmanager.cpp
+++ b/engines/nancy/action/actionmanager.cpp
@@ -66,6 +66,10 @@ void ActionManager::handleInput(NancyInput &input) {
                             shouldTrigger = true;
                         }
                     }
+
+                    if (!shouldTrigger) {
+                        _engine->sound->playSound(17); // Hardcoded by original engine
+                    }
                 } else {
                     shouldTrigger = true;
                 }
diff --git a/engines/nancy/action/primaryvideo.cpp b/engines/nancy/action/primaryvideo.cpp
index 1a96b5752b..61e9d6f4ae 100644
--- a/engines/nancy/action/primaryvideo.cpp
+++ b/engines/nancy/action/primaryvideo.cpp
@@ -28,7 +28,7 @@
 #include "engines/nancy/nancy.h"
 #include "engines/nancy/state/scene.h"
 #include "engines/nancy/nancy.h"
-#include "engines/nancy/audio.h"
+#include "engines/nancy/sound.h"
 #include "engines/nancy/util.h"
 
 #include "common/file.h"
@@ -85,19 +85,9 @@ uint16 PlayPrimaryVideoChan0::readData(Common::SeekableReadStream &stream) {
     UI::Textbox::assembleTextLine(rawText, text, 1500);
     delete[] rawText;
 
-    stream.read(name, 10);
-    soundName = Common::String(name);
-    soundChannelID = stream.readUint16LE();
-
-    stream.skip(8);
-
-    numRepeats = stream.readUint16LE();
-
-    stream.skip(4);
-
-    volume = stream.readUint16LE();
+    sound.read(stream, SoundManager::SoundDescription::kNormal);
 
-    stream.skip(0x29);
+    stream.skip(0x23);
     conditionalResponseCharacterID = stream.readByte();
     goodbyeResponseCharacterID = stream.readByte();
     numSceneChanges = stream.readByte();
@@ -180,8 +170,8 @@ void PlayPrimaryVideoChan0::execute(NancyEngine *engine) {
         case kBegin:
             init();
             registerGraphics();
-            engine->sound->loadSound(soundName, soundChannelID, numRepeats, volume);
-            engine->sound->pauseSound(soundChannelID, false);
+            engine->sound->loadSound(sound);
+            engine->sound->playSound(sound.channelID);
             state = kRun;
             // fall through
         case kRun:
@@ -205,7 +195,7 @@ void PlayPrimaryVideoChan0::execute(NancyEngine *engine) {
                 }
             }
 
-            if (!engine->sound->isSoundPlaying(soundChannelID)) {
+            if (!engine->sound->isSoundPlaying(sound.channelID)) {
                 if (responses.size() == 0) {
                     // NPC has finished talking with no responses available, auto-advance to next scene
                     state = kActionTrigger;
@@ -221,8 +211,11 @@ void PlayPrimaryVideoChan0::execute(NancyEngine *engine) {
                     if (pickedResponse != -1) {
                         // Player has picked response, play sound file and change state
                         sceneChange = responses[pickedResponse].sceneChange;
-                        engine->sound->loadSound(responses[pickedResponse].soundName, soundChannelID, numRepeats, volume);
-                        engine->sound->pauseSound(soundChannelID, false);
+                        SoundManager::SoundDescription responseSound = sound;
+                        responseSound.name = responses[pickedResponse].soundName;
+                        // TODO this is probably not correct
+                        engine->sound->loadSound(responseSound);
+                        engine->sound->playSound(responseSound.channelID);
                         state = kActionTrigger;
                     }
                 }
@@ -259,7 +252,7 @@ void PlayPrimaryVideoChan0::execute(NancyEngine *engine) {
                 engine->scene->setEventFlag(responses[pickedResponse].flagDesc.label, responses[pickedResponse].flagDesc.flag);
             }
 
-            if (!engine->sound->isSoundPlaying(soundChannelID)) {
+            if (!engine->sound->isSoundPlaying(sound.channelID)) {
                 if (shouldPopScene) {
                     // Exit dialogue
                     engine->scene->popScene();
@@ -326,6 +319,7 @@ void PlayPrimaryVideoChan0::addGoodbye(NancyEngine *engine) {
             newResponse.text = file.readString();
             // response is picked randomly
             newResponse.sceneChange.sceneID = res.sceneIDs[engine->_rnd->getRandomNumber(3)];
+            newResponse.sceneChange.doNotStartSound = true;
 
             file.close();
         }
diff --git a/engines/nancy/action/primaryvideo.h b/engines/nancy/action/primaryvideo.h
index 8ea257cbb6..69f3baac71 100644
--- a/engines/nancy/action/primaryvideo.h
+++ b/engines/nancy/action/primaryvideo.h
@@ -65,7 +65,7 @@ public:
     PlayPrimaryVideoChan0(RenderObject &redrawFrom) : RenderObject(redrawFrom) {}
     virtual ~PlayPrimaryVideoChan0();
 
-    virtual void init()override;
+    virtual void init() override;
     virtual void updateGraphics() override;
 
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
@@ -80,10 +80,7 @@ public:
     // _screenPosition 0x2D
     Common::String text; // 0x3D
 
-    Common::String soundName; // 0x619, TODO make a proper soundDesc struct
-    uint16 soundChannelID; // 0x623
-    uint16 numRepeats; // 0x62D
-    uint16 volume; // 0x633
+    SoundManager::SoundDescription sound; // 0x619
 
     byte conditionalResponseCharacterID; // 0x65E
     byte goodbyeResponseCharacterID; // 0x65F
diff --git a/engines/nancy/action/recordtypes.cpp b/engines/nancy/action/recordtypes.cpp
index 341aec4331..3a5ce5ce20 100644
--- a/engines/nancy/action/recordtypes.cpp
+++ b/engines/nancy/action/recordtypes.cpp
@@ -28,7 +28,7 @@
 #include "engines/nancy/nancy.h"
 #include "engines/nancy/nancy.h"
 #include "engines/nancy/graphics.h"
-#include "engines/nancy/audio.h"
+#include "engines/nancy/sound.h"
 #include "engines/nancy/input.h"
 #include "engines/nancy/resource.h"
 #include "engines/nancy/util.h"
@@ -493,7 +493,7 @@ void ShowInventoryItem::execute(NancyEngine *engine) {
             break;
         }
         case kActionTrigger:
-            // TODO play sound
+            engine->sound->playSound(24); // Hardcoded by original engine
             engine->scene->addItemToInventory(objectID);
             setVisible(false);
             hasHotspot = false;
@@ -503,15 +503,7 @@ void ShowInventoryItem::execute(NancyEngine *engine) {
 }
 
 uint16 PlayDigiSoundAndDie::readData(Common::SeekableReadStream &stream) {
-    char str[10];
-    stream.read(str, 10);
-    filename = Common::String(str);
-    id = stream.readSint16LE();
-    stream.skip(4);
-    numLoops = stream.readUint16LE();
-    stream.skip(4);
-    volume = stream.readUint16LE();
-    stream.skip(6);
+    sound.read(stream, SoundManager::SoundDescription::kDIGI);
     SceneChange::readData(stream);
     return 0x2B;
 }
@@ -519,12 +511,12 @@ uint16 PlayDigiSoundAndDie::readData(Common::SeekableReadStream &stream) {
 void PlayDigiSoundAndDie::execute(NancyEngine *engine) {
     switch (state) {
         case kBegin:
-            engine->sound->loadSound(filename, id, numLoops, volume);
-            engine->sound->pauseSound(id, false);
+            engine->sound->loadSound(sound);
+            engine->sound->playSound(sound.channelID);
             state = kRun;
             break;
         case kRun:
-            if (!engine->sound->isSoundPlaying(id)) {
+            if (!engine->sound->isSoundPlaying(sound.channelID)) {
                 state = kActionTrigger;
             }
             break;
diff --git a/engines/nancy/action/recordtypes.h b/engines/nancy/action/recordtypes.h
index df94bf503a..6c2042ddee 100644
--- a/engines/nancy/action/recordtypes.h
+++ b/engines/nancy/action/recordtypes.h
@@ -27,6 +27,8 @@
 #include "engines/nancy/commontypes.h"
 #include "engines/nancy/renderobject.h"
 
+#include "engines/nancy/sound.h"
+
 #include "common/stream.h"
 #include "common/array.h"
 #include "common/str.h"
@@ -349,10 +351,7 @@ public:
     virtual void execute(Nancy::NancyEngine *engine) override;
     // TODO subclass into Play and Stop (?)
 
-    Common::String filename;
-    int16 id = -1; // 0xA
-    uint16 numLoops = 0; // 0x10
-    uint16 volume = 0; // 0x16, maximum is 65?
+    SoundManager::SoundDescription sound;
     // ...
     // SceneChange elements at 0x1E
 };
diff --git a/engines/nancy/action/secondaryvideo.cpp b/engines/nancy/action/secondaryvideo.cpp
index 9c6fa7ccfc..82a85e773a 100644
--- a/engines/nancy/action/secondaryvideo.cpp
+++ b/engines/nancy/action/secondaryvideo.cpp
@@ -28,7 +28,7 @@
 
 #include "engines/nancy/nancy.h"
 #include "engines/nancy/util.h"
-#include "engines/nancy/audio.h"
+#include "engines/nancy/sound.h"
 #include "engines/nancy/cursor.h"
 #include "engines/nancy/input.h"
 #include "engines/nancy/graphics.h"
@@ -212,13 +212,7 @@ uint16 PlaySecondaryMovie::readData(Common::SeekableReadStream &stream) {
     }
 
     triggerFlags.readData(stream);
-    stream.read(name, 10);
-    soundName = Common::String(name);
-    soundChannel = stream.readUint16LE();
-    stream.skip(0xE);
-    soundVolume = stream.readUint16LE();
-    
-    stream.skip(6);
+    sound.read(stream, SoundManager::SoundDescription::kNormal);
     SceneChange::readData(stream);
 
     uint16 numVideoDescs = stream.readUint16LE();
@@ -276,8 +270,8 @@ void PlaySecondaryMovie::execute(NancyEngine *engine) {
         case kBegin:
             init();
             registerGraphics();
-            if (soundName != "NO SOUND") {
-                engine->sound->loadSound(soundName, soundChannel, 1, soundVolume);
+            if (sound.name != "NO SOUND") {
+                engine->sound->loadSound(sound);
             }
             state = kRun;
             // fall through
@@ -300,8 +294,8 @@ void PlaySecondaryMovie::execute(NancyEngine *engine) {
                     _screenPosition = videoDescs[activeFrame].destRect;
 
                     // Start sound if any
-                    if (soundName != "NO SOUND") {
-                        engine->sound->pauseSound(soundChannel, false);
+                    if (sound.name != "NO SOUND") {
+                        engine->sound->playSound(sound.channelID);
                     }
 
                     setVisible(true);
diff --git a/engines/nancy/action/secondaryvideo.h b/engines/nancy/action/secondaryvideo.h
index a3ccb8edd6..ab7f9ba12a 100644
--- a/engines/nancy/action/secondaryvideo.h
+++ b/engines/nancy/action/secondaryvideo.h
@@ -104,9 +104,7 @@ public:
     FlagAtFrame frameFlags[15]; // 0x26
     EventFlagsDesc triggerFlags; // 0x80
 
-    Common::String soundName; // 0xA8
-    uint16 soundChannel = 0; // 0xB2
-    uint16 soundVolume = 0; // 0xC2
+    SoundManager::SoundDescription sound; // 0xA8
 
     // SceneChange data at 0xCA
     Common::Array<SecondaryVideoDesc> videoDescs; // 0xD4
diff --git a/engines/nancy/console.cpp b/engines/nancy/console.cpp
index 716148873e..7ae3f8cd9c 100644
--- a/engines/nancy/console.cpp
+++ b/engines/nancy/console.cpp
@@ -24,7 +24,7 @@
 #include "engines/nancy/nancy.h"
 #include "engines/nancy/resource.h"
 #include "engines/nancy/video.h"
-#include "engines/nancy/audio.h"
+#include "engines/nancy/sound.h"
 #include "engines/nancy/iff.h"
 #include "engines/nancy/state/scene.h"
 
@@ -270,7 +270,7 @@ bool NancyConsole::Cmd_playAudio(int argc, const char **argv) {
 		return true;
 	}
 
-	Audio::AudioStream *stream = makeHISStream(f, DisposeAfterUse::YES);
+	Audio::AudioStream *stream = SoundManager::makeHISStream(f, DisposeAfterUse::YES);
 
 	if (!stream) {
 		debugPrintf("Failed to load '%s.his'\n", argv[1]);
diff --git a/engines/nancy/module.mk b/engines/nancy/module.mk
index 0447cf32b7..beec002714 100644
--- a/engines/nancy/module.mk
+++ b/engines/nancy/module.mk
@@ -14,8 +14,7 @@ MODULE_OBJS = \
   ui/viewport.o \
   state/logo.o \
   state/map.o \
-  state/scene.o\
-  audio.o \
+  state/scene.o \
   console.o \
   cursor.o \
   decompress.o \
@@ -27,6 +26,7 @@ MODULE_OBJS = \
   nancy.o \
   renderobject.o \
   resource.o \
+  sound.o \
   video.o
 
 # This module can be built as a plugin
diff --git a/engines/nancy/nancy.cpp b/engines/nancy/nancy.cpp
index e64f147ea0..30867750e9 100644
--- a/engines/nancy/nancy.cpp
+++ b/engines/nancy/nancy.cpp
@@ -26,9 +26,9 @@
 #include "engines/nancy/nancy.h"
 #include "engines/nancy/resource.h"
 #include "engines/nancy/iff.h"
-#include "engines/nancy/audio.h"
+#include "engines/nancy/sound.h"
 #include "engines/nancy/input.h"
-#include "engines/nancy/audio.h"
+#include "engines/nancy/sound.h"
 #include "engines/nancy/state/map.h"
 #include "engines/nancy/graphics.h"
 #include "engines/nancy/cursor.h"
@@ -248,6 +248,16 @@ Common::SeekableReadStream *NancyEngine::getBootChunkStream(const Common::String
 	else return nullptr;
 }
 
+void NancyEngine::stopAndUnloadSpecificSounds() {
+	// TODO missing if
+	
+	sound->stopSound(logo->MSNDchannelID);
+
+	for (uint i = 0; i < 10; ++i) {
+		sound->stopSound(i);
+	}
+}
+
 void NancyEngine::clearBootChunks() {
 	for (auto const& i : _bootChunks) {
 		delete i._value;
diff --git a/engines/nancy/nancy.h b/engines/nancy/nancy.h
index 1e5f1c80fc..fcfb202a5e 100644
--- a/engines/nancy/nancy.h
+++ b/engines/nancy/nancy.h
@@ -124,6 +124,9 @@ public:
 
 	// Chunks found in BOOT get extracted and cached at startup, this function lets other classes access them
 	Common::SeekableReadStream *getBootChunkStream(const Common::String &name);
+
+	// Used for state switching
+	void stopAndUnloadSpecificSounds();
 	
 	void setGameState(GameState state);
 	GameState getGameState() const { return _gameFlow.minGameState; }
diff --git a/engines/nancy/audio.cpp b/engines/nancy/sound.cpp
similarity index 52%
rename from engines/nancy/audio.cpp
rename to engines/nancy/sound.cpp
index 64df060a7a..497bd7c12a 100644
--- a/engines/nancy/audio.cpp
+++ b/engines/nancy/sound.cpp
@@ -20,7 +20,7 @@
  *
  */
 
-#include "engines/nancy/audio.h"
+#include "engines/nancy/sound.h"
 #include "engines/nancy/nancy.h"
 
 #include "common/system.h"
@@ -122,7 +122,7 @@ bool readHISHeader(Common::SeekableReadStream *stream, SoundType &type, uint16 &
 	return true;
 }
 
-Audio::SeekableAudioStream *makeHISStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) {
+Audio::SeekableAudioStream *SoundManager::makeHISStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) {
 	char buf[22];
 
 	stream->read(buf, 22);
@@ -176,52 +176,159 @@ Audio::SeekableAudioStream *makeHISStream(Common::SeekableReadStream *stream, Di
 		return Audio::makeVorbisStream(subStream, DisposeAfterUse::YES);
 }
 
+void SoundManager::SoundDescription::read(Common::SeekableReadStream &stream, Type type) {
+	char buf[10];
+
+	stream.read(buf, 10);
+	name = buf;
+	if (type == SoundDescription::kScene) {
+		stream.skip(4);
+	}
+	channelID = stream.readUint16LE();
+
+	// The difference between these is a couple members found at the same position
+	// whose purpose I don't understand, so for now just skip them
+	switch (type) {
+		case kNormal:
+			stream.skip(8);
+			break;
+		case kMenu:
+			stream.skip(6);	
+			break;
+		case kScene:
+			// fall through
+		case kDIGI:
+			stream.skip(4);
+			break;
+	}
+
+	numLoops = stream.readUint16LE();
+	if (stream.readUint16LE() != 0) { // loop indefinitely
+		numLoops = 0;
+	}
+	stream.skip(2);
+	volume = stream.readUint16LE();
+	stream.skip(6);
+}
+
 SoundManager::SoundManager(NancyEngine *engine) :
 		_engine(engine) {
 	_mixer = _engine->_system->getMixer();
+
+	initSoundChannels();
 }
 
+SoundManager::~SoundManager() {
+	stopAllSounds();
+}
 
-void SoundManager::loadSound(Common::String &name, int16 id, uint16 numLoops, uint16 volume) {
-	if (_mixer->isSoundHandleActive(handles[id])) {
-		_mixer->stopHandle(handles[id]);
+uint16 SoundManager::loadSound(const SoundDescription &description) {
+	if (_mixer->isSoundHandleActive(_channels[description.channelID].handle)) {
+		_mixer->stopHandle(_channels[description.channelID].handle);
 	}
-	Common::SeekableReadStream *mSnd = SearchMan.createReadStreamForMember(name + ".his");
-	if (mSnd) {
-		Audio::RewindableAudioStream *aStr = makeHISStream(mSnd, DisposeAfterUse::YES);
-		if (aStr) {
-			Audio::AudioStream *aStrLoop = Audio::makeLoopingAudioStream(aStr, numLoops);
-			_engine->_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &handles[id], aStrLoop, -1, volume * 255 / 100);
-			_engine->_system->getMixer()->pauseHandle(handles[id], true);
-			names[id] = name;
-		}
+
+	delete _channels[description.channelID].stream;
+	_channels[description.channelID].stream = nullptr;
+
+	_channels[description.channelID].name = description.name;
+	_channels[description.channelID].numLoops = description.numLoops;
+	_channels[description.channelID].volume = description.volume;
+
+	Common::SeekableReadStream *file = SearchMan.createReadStreamForMember(description.name + ".his");
+	if (file) {
+		_channels[description.channelID].stream = makeHISStream(file, DisposeAfterUse::YES);
 	}
+
+	return description.channelID;
 }
 
-void SoundManager::pauseSound(int16 id, bool pause) {
-	if (id < 0 || id > 20)
+void SoundManager::playSound(uint16 channelID) {
+	if (channelID > 32 || _channels[channelID].stream == 0)
 		return;
 
-	_engine->_system->getMixer()->pauseHandle(handles[id], pause);
+	_channels[channelID].stream->seek(0);
+
+	_mixer->playStream(	_channels[channelID].type,
+						&_channels[channelID].handle,
+						Audio::makeLoopingAudioStream(_channels[channelID].stream, _channels[channelID].numLoops),
+						channelID,
+						_channels[channelID].volume * 255 / 100,
+						0, DisposeAfterUse::NO);
 }
 
-void SoundManager::stopSound(int16 id) {
-	if (isSoundPlaying(id)) {
-		_mixer->stopHandle(handles[id]);
+void SoundManager::pauseSound(uint16 channelID, bool pause) {
+	if (channelID > 32)
+		return;
+
+	if (isSoundPlaying(channelID)) {
+		_engine->_system->getMixer()->pauseHandle(_channels[channelID].handle, pause);
 	}
-	names[id] = Common::String();
 }
 
-bool SoundManager::isSoundPlaying(int16 id) {
-	if (id >= 0 && id < 20) {
-		return _mixer->isSoundHandleActive(handles[id]);
+void SoundManager::stopSound(uint16 channelID) {
+	if (channelID > 32)
+		return;
+
+	if (isSoundPlaying(channelID)) {
+		_mixer->stopHandle(_channels[channelID].handle);
 	}
-	return false;
+	_channels[channelID].name = Common::String();
+	delete _channels[channelID].stream;
+	_channels[channelID].stream = nullptr;
+}
+
+bool SoundManager::isSoundPlaying(uint16 channelID) {
+	if (channelID > 32)
+		return false;
+	
+	return _mixer->isSoundHandleActive(_channels[channelID].handle);
 }
 
 // Returns whether the exception was skipped
 void SoundManager::stopAllSounds() {
-	_mixer->stopAll();
+	for (uint i = 0; i < 32; ++i) {
+		stopSound(i);
+	}
+}
+
+void SoundManager::initSoundChannels() {
+	// Original engine hardcoded these and so do we
+	_channels[7].type = Audio::Mixer::kSpeechSoundType;
+	_channels[8].type = Audio::Mixer::kSpeechSoundType;
+	_channels[30].type = Audio::Mixer::kSpeechSoundType;
+	
+	_channels[0].type = Audio::Mixer::kMusicSoundType;
+	_channels[1].type = Audio::Mixer::kMusicSoundType;
+	_channels[2].type = Audio::Mixer::kMusicSoundType;
+	_channels[27].type = Audio::Mixer::kMusicSoundType;
+	_channels[28].type = Audio::Mixer::kMusicSoundType;
+	_channels[29].type = Audio::Mixer::kMusicSoundType;
+	_channels[19].type = Audio::Mixer::kMusicSoundType;
+	
+	_channels[3].type = Audio::Mixer::kSFXSoundType;
+	_channels[4].type = Audio::Mixer::kSFXSoundType;
+	_channels[5].type = Audio::Mixer::kSFXSoundType;
+	_channels[6].type = Audio::Mixer::kSFXSoundType;
+	_channels[20].type = Audio::Mixer::kSFXSoundType;
+	_channels[21].type = Audio::Mixer::kSFXSoundType;
+	_channels[25].type = Audio::Mixer::kSFXSoundType;
+	_channels[26].type = Audio::Mixer::kSFXSoundType;
+	_channels[24].type = Audio::Mixer::kSFXSoundType;
+	_channels[23].type = Audio::Mixer::kSFXSoundType;
+	_channels[22].type = Audio::Mixer::kSFXSoundType;
+	_channels[31].type = Audio::Mixer::kSFXSoundType;
+	_channels[18].type = Audio::Mixer::kSFXSoundType;
+	_channels[17].type = Audio::Mixer::kSFXSoundType;
+	
+	_channels[9].type = Audio::Mixer::kPlainSoundType;
+	_channels[10].type = Audio::Mixer::kPlainSoundType;
+	_channels[11].type = Audio::Mixer::kPlainSoundType;
+	_channels[12].type = Audio::Mixer::kPlainSoundType;
+	_channels[13].type = Audio::Mixer::kPlainSoundType;
+	_channels[14].type = Audio::Mixer::kPlainSoundType;
+	_channels[15].type = Audio::Mixer::kPlainSoundType;
+	_channels[16].type = Audio::Mixer::kPlainSoundType;
+	
 }
 
 } // End of namespace Nancy
diff --git a/engines/nancy/audio.h b/engines/nancy/sound.h
similarity index 53%
rename from engines/nancy/audio.h
rename to engines/nancy/sound.h
index eb07003292..ad179f7144 100644
--- a/engines/nancy/audio.h
+++ b/engines/nancy/sound.h
@@ -20,8 +20,8 @@
  *
  */
 
-#ifndef NANCY_AUDIO_H
-#define NANCY_AUDIO_H
+#ifndef NANCY_SOUND_H
+#define NANCY_SOUND_H
 
 #include "common/types.h"
 #include "common/str.h"
@@ -40,28 +40,53 @@ namespace Nancy {
 
 class NancyEngine;
 
-Audio::SeekableAudioStream *makeHISStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse);
-
 class SoundManager {
 public:
-    enum SoundLoopType { kLoop = 0, kOneShot = 1 };
+    // Combines four different structs in one
+    struct SoundDescription {
+        enum Type { kNormal, kMenu, kDIGI, kScene };
+
+        Common::String name;
+        uint16 channelID;
+        uint16 numLoops;
+        uint16 volume;
+
+        void read(Common::SeekableReadStream &stream, Type type);
+    };
+
     SoundManager(NancyEngine *engine);
-    ~SoundManager() =default;
+    ~SoundManager();
 
-    void loadSound(Common::String &name, int16 id, uint16 numLoops = 0, uint16 volume = 60);
-    void pauseSound(int16 id, bool pause);
-    void stopSound(int16 id);
-    bool isSoundPlaying(int16 id);
+    // Load a sound into a channel without starting it
+    uint16 loadSound(const SoundDescription &description);
+
+    void playSound(uint16 channelID);
+    void pauseSound(uint16 channelID, bool pause);
+    bool isSoundPlaying(uint16 channelID);
+
+    // Stop playing a sound and unload it from the channel
+    void stopSound(uint16 channelID);
     void stopAllSounds();
 
-private:
+    static Audio::SeekableAudioStream *makeHISStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse);
+
+protected:
+    struct Channel {
+        Common::String name;
+        Audio::Mixer::SoundType type;
+        uint16 numLoops = 0;
+        uint volume = 0;
+        Audio::SeekableAudioStream *stream = nullptr;
+        Audio::SoundHandle handle;
+    };
+
+    void initSoundChannels();
     NancyEngine *_engine;
     Audio::Mixer *_mixer;
 
-    Audio::SoundHandle handles[20];
-    Common::String names[20];
+    Channel _channels[32];
 };
 
 } // End of namespace Nancy
 
-#endif // NANCY_AUDIO_H
+#endif // NANCY_SOUND_H
diff --git a/engines/nancy/state/logo.cpp b/engines/nancy/state/logo.cpp
index 35e22d9e35..8d1392ac2f 100644
--- a/engines/nancy/state/logo.cpp
+++ b/engines/nancy/state/logo.cpp
@@ -24,7 +24,7 @@
 
 #include "engines/nancy/nancy.h"
 #include "engines/nancy/resource.h"
-#include "engines/nancy/audio.h"
+#include "engines/nancy/sound.h"
 #include "engines/nancy/input.h"
 
 #include "common/error.h"
@@ -66,13 +66,24 @@ void Logo::init() {
 }
 
 void Logo::startSound() {
-	Common::SeekableReadStream *msnd = _engine->getBootChunkStream("MSND");
-	char name[10];
-	msnd->seek(0);
-	msnd->read(name, 10);
-	Common::String sname(name);
-	_engine->sound->loadSound(sname, 0);
-	_engine->sound->pauseSound(0, false);
+	SoundManager::SoundDescription desc;
+	desc.read(*_engine->getBootChunkStream("MSND"), SoundManager::SoundDescription::kMenu);
+	_engine->sound->loadSound(desc);
+	MSNDchannelID = desc.channelID;
+	desc.read(*_engine->getBootChunkStream("BUOK"), SoundManager::SoundDescription::kNormal);
+	_engine->sound->loadSound(desc);
+	desc.read(*_engine->getBootChunkStream("BUDE"), SoundManager::SoundDescription::kNormal);
+	_engine->sound->loadSound(desc);
+	desc.read(*_engine->getBootChunkStream("BULS"), SoundManager::SoundDescription::kNormal);
+	_engine->sound->loadSound(desc);
+	desc.read(*_engine->getBootChunkStream("GLOB"), SoundManager::SoundDescription::kNormal);
+	_engine->sound->loadSound(desc);
+	desc.read(*_engine->getBootChunkStream("CURT"), SoundManager::SoundDescription::kNormal);
+	_engine->sound->loadSound(desc);
+	desc.read(*_engine->getBootChunkStream("CANT"), SoundManager::SoundDescription::kNormal);
+	_engine->sound->loadSound(desc);
+
+	_engine->sound->playSound(MSNDchannelID);
 
 	_startTicks = _engine->_system->getMillis();
 	_state = kRun;
diff --git a/engines/nancy/state/logo.h b/engines/nancy/state/logo.h
index 592295d4c0..fbc612839d 100644
--- a/engines/nancy/state/logo.h
+++ b/engines/nancy/state/logo.h
@@ -46,6 +46,8 @@ public:
 		
 	void process();
 
+	uint MSNDchannelID; // This definitely shouldn't be here
+
 private:
 	void init();
 	void startSound();
diff --git a/engines/nancy/state/map.cpp b/engines/nancy/state/map.cpp
index f8dbd140dd..5ddfcc0b58 100644
--- a/engines/nancy/state/map.cpp
+++ b/engines/nancy/state/map.cpp
@@ -25,7 +25,7 @@
 #include "engines/nancy/state/scene.h"
 
 #include "engines/nancy/resource.h"
-#include "engines/nancy/audio.h"
+#include "engines/nancy/sound.h"
 #include "engines/nancy/input.h"
 #include "engines/nancy/nancy.h"
 #include "engines/nancy/util.h"
@@ -73,13 +73,10 @@ void Map::init() {
 
     // Load the audio
     chunk->seek(0x18 + _mapID * 0x20, SEEK_SET);
-    chunk->read(name, 10);
-    n = Common::String(name);
-    uint16 channel = chunk->readUint16LE();
-    chunk->skip(0xA);
-    uint16 volume = chunk->readUint16LE();
-    _engine->sound->loadSound(n, channel, 0, volume);
-    _engine->sound->pauseSound(channel, false);
+    SoundManager::SoundDescription sound;
+    sound.read(*chunk, SoundManager::SoundDescription::kMenu);
+    _engine->sound->loadSound(sound);
+    _engine->sound->playSound(sound.channelID);
 
     for (uint i = 0; i < 4; ++i) {
         chunk->seek(0x162 + i * 16, SEEK_SET);
@@ -133,6 +130,7 @@ void Map::run() {
             // TODO handle map button as well
 
             if (input.input & NancyInput::kLeftMouseButtonUp) {
+                stopSound();
                 _engine->setGameState(NancyEngine::kScene);
                 _engine->scene->changeScene(loc.scenes[_mapID].sceneID, loc.scenes[_mapID].frameID, loc.scenes[_mapID].verticalOffset, false);
                 _state = kInit;
@@ -142,6 +140,14 @@ void Map::run() {
     }
 }
 
+void Map::stopSound() {
+    Common::SeekableReadStream *chunk = _engine->getBootChunkStream("MAP");
+    SoundManager::SoundDescription sound;
+    chunk->seek(0x18 + _mapID * 0x20, SEEK_SET);
+    sound.read(*chunk, SoundManager::SoundDescription::kMenu);
+    _engine->sound->stopSound(sound.channelID);
+}
+
 void Map::registerGraphics() {
     _viewport.registerGraphics();
     _label.registerGraphics();
diff --git a/engines/nancy/state/map.h b/engines/nancy/state/map.h
index 19aed87877..25129be636 100644
--- a/engines/nancy/state/map.h
+++ b/engines/nancy/state/map.h
@@ -89,6 +89,7 @@ private:
 
     void init();
     void run();
+    void stopSound();
 
     void registerGraphics();
 
diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index d301b249b5..e0aca2aa56 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -26,7 +26,7 @@
 #include "engines/nancy/iff.h"
 #include "engines/nancy/action/actionmanager.h"
 #include "engines/nancy/input.h"
-#include "engines/nancy/audio.h"
+#include "engines/nancy/sound.h"
 #include "engines/nancy/graphics.h"
 #include "engines/nancy/cursor.h"
 #include "engines/nancy/time.h"
@@ -55,9 +55,9 @@ void Scene::process() {
     case kStartSound:
         _state = kRun;
         if (!_sceneState._doNotStartSound) {
-            _engine->sound->stopAllSounds();
-            _engine->sound->loadSound(_sceneState.summary.audioFile, _sceneState.summary.audioID, 0, _sceneState.summary.audioVolume);
-            _engine->sound->pauseSound(_sceneState.summary.audioID, false);
+            _engine->stopAndUnloadSpecificSounds();
+            _engine->sound->loadSound(_sceneState.summary.sound);
+            _engine->sound->playSound(_sceneState.summary.sound.channelID);
         }
         // fall through
     case kRun:
@@ -85,6 +85,20 @@ void Scene::popScene() {
     _sceneState.isScenePushed = false;
 }
 
+void Scene::pauseSceneSpecificSounds() {
+    // TODO missing if, same condition as the one in NancyEngine::stopAndUnloadSpecificSounds
+
+    for (uint i = 0; i < 10; ++i) {
+		_engine->sound->pauseSound(i, true);
+	}
+}
+
+void Scene::unpauseSceneSpecificSounds() {
+    for (uint i = 0; i < 10; ++i) {
+		_engine->sound->pauseSound(i, false);
+	}
+}
+
 void Scene::addItemToInventory(uint16 id) {
     _flags.items[id] = kTrue;
     if (_flags.heldItem == id) {
@@ -245,6 +259,7 @@ void Scene::run() {
         }
 
         registerGraphics();
+        unpauseSceneSpecificSounds();
 
         return;
     }
@@ -305,12 +320,7 @@ void Scene::readSceneSummary(Common::SeekableReadStream &stream) {
     stream.seek(3, SEEK_CUR);
     _sceneState.summary.videoFormat = stream.readUint16LE();
 
-    stream.read(buf, 10);
-    buf[9] = 0;
-    _sceneState.summary.audioFile = Common::String(buf);
-    _sceneState.summary.audioID = stream.readSint16LE();
-    stream.skip(0xE);
-    _sceneState.summary.audioVolume = stream.readUint16LE();
+    _sceneState.summary.sound.read(stream, SoundManager::SoundDescription::kScene);
 
     stream.seek(0x72);
     _sceneState.summary.verticalScrollDelta = stream.readUint16LE();
@@ -332,6 +342,7 @@ bool Scene::changeGameState() {
         _timers.pushedPlayTime = _engine->getTotalPlayTime();
         _engine->setGameState(_gameStateRequested);
         _gameStateRequested = NancyEngine::kScene;
+        pauseSceneSpecificSounds();
 
         return true;
     }
diff --git a/engines/nancy/state/scene.h b/engines/nancy/state/scene.h
index 72ef3a4665..e99d4913e6 100644
--- a/engines/nancy/state/scene.h
+++ b/engines/nancy/state/scene.h
@@ -33,6 +33,7 @@
 #include "engines/nancy/time.h"
 #include "engines/nancy/commontypes.h"
 #include "engines/nancy/nancy.h"
+#include "engines/nancy/sound.h"
 
 #include "common/scummsys.h"
 #include "common/array.h"
@@ -64,19 +65,18 @@ class Scene {
     friend class Nancy::NancyConsole;
 public:
     struct SceneSummary { // SSUM
-        Common::String description; // 0x00
-        Common::String videoFile;   // 0x32
+        Common::String description;             // 0x00
+        Common::String videoFile;               // 0x32
         //
-        uint16 videoFormat;         // 0x3E, value is 1 or 2
-        Common::String audioFile;   // 0x40
-        int16 audioID;              // 0x4A
-        uint16 audioVolume;         // 0x5A
+        uint16 videoFormat;                     // 0x3E, value is 1 or 2
+        Common::String audioFile;               
+        SoundManager::SoundDescription sound;   // 0x40
         //
-        uint16 verticalScrollDelta; // 0x72
-        uint16 horizontalEdgeSize;  // 0x74
-        uint16 verticalEdgeSize;    // 0x76
-        Time slowMoveTimeDelta;   // 0x78
-        Time fastMoveTimeDelta;   // 0x7A
+        uint16 verticalScrollDelta;             // 0x72
+        uint16 horizontalEdgeSize;              // 0x74
+        uint16 verticalEdgeSize;                // 0x76
+        Time slowMoveTimeDelta;                 // 0x78
+        Time fastMoveTimeDelta;                 // 0x7A
         // byte unknown7C enum with 4 values
         //
     };
@@ -97,6 +97,9 @@ public:
     void pushScene();
     void popScene();
 
+    void pauseSceneSpecificSounds();
+    void unpauseSceneSpecificSounds();
+
     void addItemToInventory(uint16 id);
     void removeItemFromInventory(uint16 id, bool pickUp = true);
     int16 getHeldItem() { return _flags.heldItem; }


Commit: 05ac9934c7e124ff997a0044417fdff42c9645d7
    https://github.com/scummvm/scummvm/commit/05ac9934c7e124ff997a0044417fdff42c9645d7
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Global main menu support

Added proper support for the GMM, so videos will not fast-forward on resume and timers will not include the time spent outside the game.

Changed paths:
    engines/nancy/action/primaryvideo.cpp
    engines/nancy/action/primaryvideo.h
    engines/nancy/action/secondaryvideo.cpp
    engines/nancy/action/secondaryvideo.h
    engines/nancy/graphics.cpp
    engines/nancy/graphics.h
    engines/nancy/nancy.cpp
    engines/nancy/nancy.h
    engines/nancy/renderobject.h
    engines/nancy/state/scene.cpp
    engines/nancy/state/scene.h
    engines/nancy/time.h


diff --git a/engines/nancy/action/primaryvideo.cpp b/engines/nancy/action/primaryvideo.cpp
index 61e9d6f4ae..6c06368418 100644
--- a/engines/nancy/action/primaryvideo.cpp
+++ b/engines/nancy/action/primaryvideo.cpp
@@ -68,6 +68,10 @@ void PlayPrimaryVideoChan0::updateGraphics() {
     RenderObject::updateGraphics();
 }
 
+void PlayPrimaryVideoChan0::onPause(bool pause) {
+    _decoder.pauseVideo(pause);
+}
+
 uint16 PlayPrimaryVideoChan0::readData(Common::SeekableReadStream &stream) {
     uint16 bytesRead = stream.pos();
 
diff --git a/engines/nancy/action/primaryvideo.h b/engines/nancy/action/primaryvideo.h
index 69f3baac71..c65c63ddce 100644
--- a/engines/nancy/action/primaryvideo.h
+++ b/engines/nancy/action/primaryvideo.h
@@ -67,6 +67,7 @@ public:
 
     virtual void init() override;
     virtual void updateGraphics() override;
+    virtual void onPause(bool pause) override;
 
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
     virtual void execute(NancyEngine *engine) override;
diff --git a/engines/nancy/action/secondaryvideo.cpp b/engines/nancy/action/secondaryvideo.cpp
index 82a85e773a..7b70a7905a 100644
--- a/engines/nancy/action/secondaryvideo.cpp
+++ b/engines/nancy/action/secondaryvideo.cpp
@@ -123,6 +123,10 @@ void PlaySecondaryVideo::updateGraphics() {
     RenderObject::updateGraphics();
 }
 
+void PlaySecondaryVideo::onPause(bool pause) {
+    _decoder.pauseVideo(pause);
+}
+
 void PlaySecondaryVideo::handleInput(NancyInput &input) {
     if (hasHotspot && _engine->scene->getViewport().convertViewportToScreen(hotspot).contains(input.mousePos)) {
         _isHovered = true;
@@ -265,6 +269,10 @@ void PlaySecondaryMovie::updateGraphics() {
     RenderObject::updateGraphics();
 }
 
+void PlaySecondaryMovie::onPause(bool pause) {
+    _decoder.pauseVideo(pause);
+}
+
 void PlaySecondaryMovie::execute(NancyEngine *engine) {
     switch (state) {
         case kBegin:
diff --git a/engines/nancy/action/secondaryvideo.h b/engines/nancy/action/secondaryvideo.h
index ab7f9ba12a..ed57016a85 100644
--- a/engines/nancy/action/secondaryvideo.h
+++ b/engines/nancy/action/secondaryvideo.h
@@ -54,6 +54,7 @@ public:
 
     virtual void init() override;
     virtual void updateGraphics() override;
+    virtual void onPause(bool pause) override;
     virtual void handleInput(NancyInput &input) override;
 
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
@@ -95,6 +96,7 @@ public:
 
     virtual void init() override;
     virtual void updateGraphics() override;
+    virtual void onPause(bool pause) override;
 
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
     virtual void execute(NancyEngine *engine) override;
diff --git a/engines/nancy/graphics.cpp b/engines/nancy/graphics.cpp
index 47187b789a..a4f9518596 100644
--- a/engines/nancy/graphics.cpp
+++ b/engines/nancy/graphics.cpp
@@ -94,6 +94,12 @@ void GraphicsManager::draw() {
     _screen.update();
 }
 
+void GraphicsManager::onPause(bool pause) {
+    for (auto &object : _objects) {
+        object->onPause(pause);
+    }
+}
+
 void GraphicsManager::addObject(RenderObject *object) {
     _objects.insert(object);
 }
diff --git a/engines/nancy/graphics.h b/engines/nancy/graphics.h
index 4ae2d02531..ad8364017d 100644
--- a/engines/nancy/graphics.h
+++ b/engines/nancy/graphics.h
@@ -42,6 +42,8 @@ public:
     void init();
     void draw();
 
+    void onPause(bool pause);
+
     void addObject(RenderObject *object);
     void removeObject(RenderObject *object);
     void clearObjects();
diff --git a/engines/nancy/nancy.cpp b/engines/nancy/nancy.cpp
index 30867750e9..cefce166f2 100644
--- a/engines/nancy/nancy.cpp
+++ b/engines/nancy/nancy.cpp
@@ -258,6 +258,23 @@ void NancyEngine::stopAndUnloadSpecificSounds() {
 	}
 }
 
+void NancyEngine::pauseEngineIntern(bool pause) {
+	if (pause) {
+		if (getGameState() == kScene) {
+			scene->requestStateChange(kPause);
+			scene->changeGameState(true);
+		} else {
+			setGameState(kPause, true);
+		}
+	} else {
+		setGameState(getPreviousGameState(), true);
+	}
+
+	graphicsManager->onPause(pause);
+
+	Engine::pauseEngineIntern(pause);
+}
+
 void NancyEngine::clearBootChunks() {
 	for (auto const& i : _bootChunks) {
 		delete i._value;
@@ -339,14 +356,14 @@ void NancyEngine::readImageList(const IFF &boot, const Common::String &prefix, I
 	}
 }
 
-void NancyEngine::setGameState(GameState state) {
+void NancyEngine::setGameState(GameState state, bool keepGraphics) {
 	_gameFlow.previousGameState = _gameFlow.minGameState;
 	_gameFlow.minGameState = state;
 	_gameFlow.justChanged = true;
 
-	// Do not erase the frame if we're switching to the map
-	// This makes the labels not crash the game
-	graphicsManager->clearObjects();
+	if (!keepGraphics) {
+		graphicsManager->clearObjects();
+	}
 }
 
 class NancyEngine_v0 : public NancyEngine {
diff --git a/engines/nancy/nancy.h b/engines/nancy/nancy.h
index fcfb202a5e..b10ea8201b 100644
--- a/engines/nancy/nancy.h
+++ b/engines/nancy/nancy.h
@@ -98,6 +98,7 @@ public:
 		kQuit,
 		// regain focus
 		kIdle,
+		kPause, // only used when the GMM is on screen
 		kReloadSave
 	};
 
@@ -128,10 +129,12 @@ public:
 	// Used for state switching
 	void stopAndUnloadSpecificSounds();
 	
-	void setGameState(GameState state);
+	void setGameState(GameState state, bool keepGraphics = false);
 	GameState getGameState() const { return _gameFlow.minGameState; }
 	GameState getPreviousGameState() const { return _gameFlow.previousGameState; }
 
+	virtual void pauseEngineIntern(bool pause) override;
+
 	// Managers
 	ResourceManager *_res;
 	GraphicsManager *graphicsManager;
diff --git a/engines/nancy/renderobject.h b/engines/nancy/renderobject.h
index c9e7641bb9..d1ed1fe5e9 100644
--- a/engines/nancy/renderobject.h
+++ b/engines/nancy/renderobject.h
@@ -59,7 +59,8 @@ public:
 
     virtual void init(); // Does _not_ get called automatically
     virtual void registerGraphics();
-    virtual void updateGraphics() {};
+    virtual void updateGraphics() {}
+    virtual void onPause(bool pause) {}
 
     void moveTo(Common::Point position);
     void setVisible(bool visible);
diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index e0aca2aa56..197a6cdae0 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -250,15 +250,15 @@ void Scene::run() {
     if (_engine->getGameState() != _engine->getPreviousGameState()) {
         if (hasLoadedFromSavefile) {
             if (playTime > _timers.pushedPlayTime) {
-                playTime -= _timers.pushedPlayTime;
-                _timers.totalTime -= playTime;
-                _timers.sceneTime -= playTime;
-                if (_timers.timerIsActive)
-                    _timers.timerTime -= playTime;
+                _engine->setTotalPlayTime((uint32)_timers.pushedPlayTime);
+                playTime = _timers.pushedPlayTime;
             }
         }
 
-        registerGraphics();
+        // If the GMM was on we shouldn't reregister graphics
+        if (_engine->getPreviousGameState() != Nancy::NancyEngine::kPause) {
+            registerGraphics();
+        }
         unpauseSceneSpecificSounds();
 
         return;
@@ -337,10 +337,10 @@ void Scene::readSceneSummary(Common::SeekableReadStream &stream) {
     delete[] buf;
 }
 
-bool Scene::changeGameState() {
+bool Scene::changeGameState(bool keepGraphics) {
     if (_gameStateRequested != NancyEngine::kScene) {
         _timers.pushedPlayTime = _engine->getTotalPlayTime();
-        _engine->setGameState(_gameStateRequested);
+        _engine->setGameState(_gameStateRequested, keepGraphics);
         _gameStateRequested = NancyEngine::kScene;
         pauseSceneSpecificSounds();
 
diff --git a/engines/nancy/state/scene.h b/engines/nancy/state/scene.h
index e99d4913e6..30958c04fe 100644
--- a/engines/nancy/state/scene.h
+++ b/engines/nancy/state/scene.h
@@ -63,6 +63,7 @@ class Scene {
     friend class Nancy::Action::ActionRecord;
     friend class Nancy::Action::ActionManager;
     friend class Nancy::NancyConsole;
+    friend class Nancy::NancyEngine;
 public:
     struct SceneSummary { // SSUM
         Common::String description;             // 0x00
@@ -140,7 +141,7 @@ private:
 
     void readSceneSummary(Common::SeekableReadStream &stream);
 
-    bool changeGameState();
+    bool changeGameState(bool keepGraphics = false);
 
     void clearSceneData();
 
diff --git a/engines/nancy/time.h b/engines/nancy/time.h
index cb762b7dda..d9f7524e3a 100644
--- a/engines/nancy/time.h
+++ b/engines/nancy/time.h
@@ -34,6 +34,7 @@ public:
     Time(const uint32 &t) { _milliseconds = t; }
     Time(const Time &t) =default;
     ~Time() =default;
+    explicit operator uint32() const { return _milliseconds; }
     Time &operator=(const Time &t)                          { if (this != &t) _milliseconds = t._milliseconds; return *this; }
     Time &operator=(const uint32 &t)                        { _milliseconds = t; return *this; }
     Time &operator+=(const Time &t)                         { _milliseconds += t._milliseconds; return *this; }


Commit: 20d226fc104d855a1ada97866562827489054d98
    https://github.com/scummvm/scummvm/commit/20d226fc104d855a1ada97866562827489054d98
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Fix load_scene and scene_id console commands

Fixed an issue where load_scene and scene_id would incorrectly complain about being in the wrong game state.

Changed paths:
    engines/nancy/console.cpp


diff --git a/engines/nancy/console.cpp b/engines/nancy/console.cpp
index 7ae3f8cd9c..3dabb9712b 100644
--- a/engines/nancy/console.cpp
+++ b/engines/nancy/console.cpp
@@ -289,7 +289,7 @@ bool NancyConsole::Cmd_loadScene(int argc, const char **argv) {
 		return true;
 	}
 	
-	if (_vm->_gameFlow.minGameState != NancyEngine::GameState::kScene) {
+	if (_vm->getPreviousGameState() != NancyEngine::GameState::kScene) {
 		debugPrintf("Not in the kScene state\n");
 		return true;
 	}
@@ -307,7 +307,7 @@ bool NancyConsole::Cmd_loadScene(int argc, const char **argv) {
 }
 
 bool NancyConsole::Cmd_sceneID(int argc, const char **argv) {
-	if (_vm->_gameFlow.minGameState != NancyEngine::GameState::kScene) {
+	if (_vm->getPreviousGameState() != NancyEngine::GameState::kScene) {
 		debugPrintf("Not in the kScene state\n");
 		return true;
 	}


Commit: a96f6fdebd7b5e938aaaf939a533faede6332135
    https://github.com/scummvm/scummvm/commit/a96f6fdebd7b5e938aaaf939a533faede6332135
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Move NO SOUND keyword check

Some action records have fields for sound that can be left unused by putting "NO SOUND" in the filename string. This commit moves the check for that keyword inside sound.cpp so it isn't repeated everywhere.

Changed paths:
    engines/nancy/action/secondaryvideo.cpp
    engines/nancy/sound.cpp
    engines/nancy/sound.h


diff --git a/engines/nancy/action/secondaryvideo.cpp b/engines/nancy/action/secondaryvideo.cpp
index 7b70a7905a..c088fec9b4 100644
--- a/engines/nancy/action/secondaryvideo.cpp
+++ b/engines/nancy/action/secondaryvideo.cpp
@@ -278,9 +278,7 @@ void PlaySecondaryMovie::execute(NancyEngine *engine) {
         case kBegin:
             init();
             registerGraphics();
-            if (sound.name != "NO SOUND") {
-                engine->sound->loadSound(sound);
-            }
+            engine->sound->loadSound(sound);
             state = kRun;
             // fall through
         case kRun: {
@@ -300,12 +298,7 @@ void PlaySecondaryMovie::execute(NancyEngine *engine) {
 
                 if (activeFrame != -1) {
                     _screenPosition = videoDescs[activeFrame].destRect;
-
-                    // Start sound if any
-                    if (sound.name != "NO SOUND") {
-                        engine->sound->playSound(sound.channelID);
-                    }
-
+                    engine->sound->playSound(sound.channelID);
                     setVisible(true);
                 } else {
                     setVisible(false);
diff --git a/engines/nancy/sound.cpp b/engines/nancy/sound.cpp
index 497bd7c12a..6552eadaa3 100644
--- a/engines/nancy/sound.cpp
+++ b/engines/nancy/sound.cpp
@@ -181,6 +181,7 @@ void SoundManager::SoundDescription::read(Common::SeekableReadStream &stream, Ty
 
 	stream.read(buf, 10);
 	name = buf;
+
 	if (type == SoundDescription::kScene) {
 		stream.skip(4);
 	}
@@ -222,7 +223,7 @@ SoundManager::~SoundManager() {
 	stopAllSounds();
 }
 
-uint16 SoundManager::loadSound(const SoundDescription &description) {
+void SoundManager::loadSound(const SoundDescription &description) {
 	if (_mixer->isSoundHandleActive(_channels[description.channelID].handle)) {
 		_mixer->stopHandle(_channels[description.channelID].handle);
 	}
@@ -234,16 +235,18 @@ uint16 SoundManager::loadSound(const SoundDescription &description) {
 	_channels[description.channelID].numLoops = description.numLoops;
 	_channels[description.channelID].volume = description.volume;
 
+	if (description.name == "NO SOUND") {
+		return;
+	}
+
 	Common::SeekableReadStream *file = SearchMan.createReadStreamForMember(description.name + ".his");
 	if (file) {
 		_channels[description.channelID].stream = makeHISStream(file, DisposeAfterUse::YES);
 	}
-
-	return description.channelID;
 }
 
 void SoundManager::playSound(uint16 channelID) {
-	if (channelID > 32 || _channels[channelID].stream == 0)
+	if (channelID > 32 || _channels[channelID].stream == 0 || _channels->name == "NO SOUND")
 		return;
 
 	_channels[channelID].stream->seek(0);
diff --git a/engines/nancy/sound.h b/engines/nancy/sound.h
index ad179f7144..6ea0b895b3 100644
--- a/engines/nancy/sound.h
+++ b/engines/nancy/sound.h
@@ -58,7 +58,7 @@ public:
     ~SoundManager();
 
     // Load a sound into a channel without starting it
-    uint16 loadSound(const SoundDescription &description);
+    void loadSound(const SoundDescription &description);
 
     void playSound(uint16 channelID);
     void pauseSound(uint16 channelID, bool pause);


Commit: 36111842611ac4a0af6f30c3bfaeec9334ba9ce3
    https://github.com/scummvm/scummvm/commit/36111842611ac4a0af6f30c3bfaeec9334ba9ce3
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Fix leaked surfaces

Fixed a few instances of surfaces not getting freed after use.

Changed paths:
    engines/nancy/cursor.cpp
    engines/nancy/graphics.cpp
    engines/nancy/ui/frame.cpp


diff --git a/engines/nancy/cursor.cpp b/engines/nancy/cursor.cpp
index 2f338aa161..e79aea3ef3 100644
--- a/engines/nancy/cursor.cpp
+++ b/engines/nancy/cursor.cpp
@@ -56,6 +56,8 @@ void CursorManager::init() {
     _invCursorsSurface.create(surf.w, surf.h, surf.format);
     _invCursorsSurface.blitFrom(surf);
 
+    surf.free();
+
     setCursor(kNormalArrow, -1);
     showCursor(false);
 
diff --git a/engines/nancy/graphics.cpp b/engines/nancy/graphics.cpp
index a4f9518596..6242508593 100644
--- a/engines/nancy/graphics.cpp
+++ b/engines/nancy/graphics.cpp
@@ -43,6 +43,7 @@ void GraphicsManager::init() {
     _engine->_res->loadImage("ciftree", "OBJECT0", surf);
     object0.create(surf.w, surf.h, surf.format);
     object0.blitFrom(surf, Common::Point(0, 0));
+    surf.free();
 
     Common::SeekableReadStream *fontChunk = _engine->getBootChunkStream("FONT");
     while(fontChunk->pos() != fontChunk->size()) {
diff --git a/engines/nancy/ui/frame.cpp b/engines/nancy/ui/frame.cpp
index 7959b584e7..24f1d30e77 100644
--- a/engines/nancy/ui/frame.cpp
+++ b/engines/nancy/ui/frame.cpp
@@ -39,6 +39,7 @@ void Frame::init() {
     
     _drawSurface.create(surf.w, surf.h, surf.format);
     _drawSurface.blitFrom(surf, Common::Point(0, 0));
+    surf.free();
 
     RenderObject::init();
 }


Commit: 0ff64ca11a0c95419aa5d5cea01107d8c537a02d
    https://github.com/scummvm/scummvm/commit/0ff64ca11a0c95419aa5d5cea01107d8c537a02d
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Implement ordering puzzle

Implemented the OrderingPuzzle action record, which is used for the safe in nancy1.

Changed paths:
  A engines/nancy/action/orderingpuzzle.cpp
  A engines/nancy/action/orderingpuzzle.h
    engines/nancy/action/arfactory_v1.cpp
    engines/nancy/action/recordtypes.cpp
    engines/nancy/action/recordtypes.h
    engines/nancy/module.mk


diff --git a/engines/nancy/action/arfactory_v1.cpp b/engines/nancy/action/arfactory_v1.cpp
index cef5602325..99b32d12d8 100644
--- a/engines/nancy/action/arfactory_v1.cpp
+++ b/engines/nancy/action/arfactory_v1.cpp
@@ -26,6 +26,7 @@
 #include "engines/nancy/action/primaryvideo.h"
 #include "engines/nancy/action/secondaryvideo.h"
 #include "engines/nancy/action/staticbitmapanim.h"
+#include "engines/nancy/action/orderingpuzzle.h"
 
 #include "engines/nancy/state/scene.h"
 
@@ -101,7 +102,7 @@ ActionRecord *ActionManager::createActionRecord(uint16 type) {
         case 0x61:
             return new EventFlags();
         case 0x62:
-            return new OrderingPuzzle();
+            return new OrderingPuzzle(_engine->scene->getViewport());
         case 0x63:
             return new LoseGame();
         case 0x64:
diff --git a/engines/nancy/action/orderingpuzzle.cpp b/engines/nancy/action/orderingpuzzle.cpp
new file mode 100644
index 0000000000..e311b4a54a
--- /dev/null
+++ b/engines/nancy/action/orderingpuzzle.cpp
@@ -0,0 +1,236 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/nancy/action/orderingpuzzle.h"
+
+#include "engines/nancy/util.h"
+#include "engines/nancy/nancy.h"
+#include "engines/nancy/state/scene.h"
+#include "engines/nancy/ui/viewport.h"
+#include "engines/nancy/graphics.h"
+#include "engines/nancy/resource.h"
+#include "engines/nancy/input.h"
+#include "engines/nancy/cursor.h"
+
+#include "graphics/surface.h"
+
+namespace Nancy {
+namespace Action {
+
+void OrderingPuzzle::init() {
+    // Screen position is initialized in readData and fits exactly the bounds of all elements on screen.
+    // This is a hacky way to make this particular action record work with this implementation's graphics manager
+    _drawSurface.create(_screenPosition.width(), _screenPosition.height(), GraphicsManager::pixelFormat);
+    clearAllElements();
+
+    Graphics::Surface surf;
+    _engine->_res->loadImage("ciftree", imageName, surf);
+    image.create(surf.w, surf.h, surf.format);
+    image.blitFrom(surf);
+    surf.free();
+
+    setVisible(false);
+
+    RenderObject::init();
+}
+
+uint16 OrderingPuzzle::readData(Common::SeekableReadStream &stream) {
+    char buf[10];
+
+    stream.read(buf, 10);
+    imageName = buf;
+    uint16 numElements = stream.readUint16LE();
+
+    for (uint i = 0; i < numElements; ++i) {
+        srcRects.push_back(Common::Rect());
+        readRect(stream, srcRects.back());
+    }
+    
+    stream.skip(16 * (15 - numElements));
+
+    for (uint i = 0; i < numElements; ++i) {
+        destRects.push_back(Common::Rect());
+        readRect(stream, destRects.back());
+
+        if (i == 0) {
+            _screenPosition = destRects[i];
+        } else {
+            _screenPosition.extend(destRects[i]);
+        }
+
+        drawnElements.push_back(false);
+    }
+
+    stream.skip(16 * (15 - numElements));
+
+    sequenceLength = stream.readUint16LE();
+
+    for (uint i = 0; i < 15; ++i) {
+        correctSequence.push_back(stream.readByte());
+    }
+
+    clickSound.read(stream, SoundManager::SoundDescription::kNormal);
+    solveExitScene.readData(stream);
+    stream.skip(2); // shouldStopRendering, useless
+    flagOnSolve.label = stream.readUint16LE();
+    flagOnSolve.flag = (NancyFlag)stream.readByte();
+    solveSoundDelay = stream.readUint16LE();
+    solveSound.read(stream, SoundManager::SoundDescription::kNormal);
+    exitScene.readData(stream);
+    stream.skip(2); // shouldStopRendering, useless
+    flagOnExit.label = stream.readUint16LE();
+    flagOnExit.flag = (NancyFlag)stream.readByte();
+    readRect(stream, exitHotspot);
+
+    return 0x26D;
+}
+
+void OrderingPuzzle::execute(Nancy::NancyEngine *engine) {
+    switch (state) {
+        case kBegin:
+            init();
+            registerGraphics();
+            _engine->sound->loadSound(clickSound);
+            _engine->sound->loadSound(solveSound);
+            state = kRun;
+            // fall through
+        case kRun:
+            switch (solveState) {
+                case kNotSolved:
+                    if (clickedSequence.size() != sequenceLength) {
+                        return;
+                    }
+
+                    for (uint i = 0; i < sequenceLength; ++i) {
+                        if (clickedSequence[i] != (int16)correctSequence[i]) {
+                            return;
+                        }
+                    }
+
+                    _engine->scene->setEventFlag(flagOnSolve.label, flagOnSolve.flag);
+                    solveSoundPlayTime = _engine->getTotalPlayTime() + solveSoundDelay * 1000;
+                    solveState = kPlaySound;
+                    // fall through
+                case kPlaySound:
+                    if (_engine->getTotalPlayTime() <= solveSoundPlayTime) {
+                        break;
+                    }
+
+                    _engine->sound->playSound(solveSound.channelID);
+                    solveState = kWaitForSound;
+                    break;
+                case kWaitForSound:
+                    if (!_engine->sound->isSoundPlaying(solveSound.channelID)) {
+                        state = kActionTrigger;
+                    }
+
+                    break;
+            }
+            break;
+        case kActionTrigger:
+            _engine->sound->stopSound(clickSound.channelID);
+            _engine->sound->stopSound(solveSound.channelID);
+
+            if (solveState == kNotSolved) {
+                if (exitScene.sceneID != 9999) {
+                    _engine->scene->changeScene(exitScene.sceneID, exitScene.frameID, exitScene.verticalOffset, exitScene.doNotStartSound);
+                    _engine->scene->setEventFlag(flagOnExit.label, flagOnExit.flag);
+                }
+            } else {
+                if (solveExitScene.sceneID != 9999) {
+                    _engine->scene->changeScene(solveExitScene.sceneID, solveExitScene.frameID, solveExitScene.verticalOffset, solveExitScene.doNotStartSound);
+                }
+            }
+
+            isDone = true;
+    }
+}
+
+void OrderingPuzzle::handleInput(NancyInput &input) {
+    if (solveState != kNotSolved) {
+        return;
+    }
+
+    if (_engine->scene->getViewport().convertViewportToScreen(exitHotspot).contains(input.mousePos)) {
+        _engine->cursorManager->setCursorType(CursorManager::kExitArrow);
+
+        if (input.input & NancyInput::kLeftMouseButtonUp) {
+            state = kActionTrigger;
+        }
+        return;
+    }
+
+    for (int i = 0; i < (int)destRects.size(); ++i) {
+        if (_engine->scene->getViewport().convertViewportToScreen(destRects[i]).contains(input.mousePos)) {
+            _engine->cursorManager->setCursorType(CursorManager::kHotspot);
+
+            if (input.input & NancyInput::kLeftMouseButtonUp) {
+                _engine->sound->playSound(clickSound.channelID);
+                
+                for (uint j = 0; j < clickedSequence.size(); ++j) {
+                    if (clickedSequence[j] == i && drawnElements[i] == true) {
+                        undrawElement(i);
+                        if (clickedSequence.back() == i) {
+                            clickedSequence.pop_back();
+                        }
+                        return;
+                    }
+                }
+
+                clickedSequence.push_back(i);
+
+                if (clickedSequence.size() > (uint)sequenceLength + 1) {
+                    clearAllElements();
+                } else {
+                    drawElement(i);
+                }
+            }
+            return;
+        }
+    }
+}
+
+void OrderingPuzzle::drawElement(uint id) {
+    drawnElements[id] = true;
+    Common::Point destPoint(destRects[id].left - _screenPosition.left, destRects[id].top - _screenPosition.top);
+    _drawSurface.blitFrom(image, srcRects[id], destPoint);
+    setVisible(true);
+}
+
+void OrderingPuzzle::undrawElement(uint id) {
+    drawnElements[id] = false;
+    Common::Rect bounds = destRects[id];
+    bounds.translate(-_screenPosition.left, -_screenPosition.top);
+
+    _drawSurface.fillRect(bounds, GraphicsManager::transColor);
+    _needsRedraw = true;
+}
+
+void OrderingPuzzle::clearAllElements() {
+    _drawSurface.clear(_engine->graphicsManager->transColor);
+    setVisible(false);
+    clickedSequence.clear();
+    return;
+}
+
+} // End of namespace Action
+} // End of namespace Nancy
\ No newline at end of file
diff --git a/engines/nancy/action/orderingpuzzle.h b/engines/nancy/action/orderingpuzzle.h
new file mode 100644
index 0000000000..c6a22a5357
--- /dev/null
+++ b/engines/nancy/action/orderingpuzzle.h
@@ -0,0 +1,86 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef NANCY_ACTION_ORDERINGPUZZLE_H
+#define NANCY_ACTION_ORDERINGPUZZLE_H
+
+#include "engines/nancy/action/recordtypes.h"
+#include "engines/nancy/renderobject.h"
+
+#include "engines/nancy/sound.h"
+
+#include "common/str.h"
+#include "common/array.h"
+#include "common/rect.h"
+
+#include "graphics/managed_surface.h"
+
+namespace Nancy {
+namespace Action {
+
+class OrderingPuzzle : public ActionRecord, public RenderObject {
+public:
+    enum SolveState { kNotSolved, kPlaySound, kWaitForSound };
+    OrderingPuzzle(RenderObject &redrawFrom) : RenderObject(redrawFrom) {}
+    ~OrderingPuzzle() {}
+
+    virtual void init() override;
+    
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void execute(Nancy::NancyEngine *engine) override;
+    virtual void handleInput(NancyInput &input) override;
+
+    Common::String imageName; // 0x00
+    Common::Array<Common::Rect> srcRects; // 0xC, 15
+    Common::Array<Common::Rect> destRects; // 0xFC, 15
+    uint16 sequenceLength; // 0x1EC;
+    Common::Array<byte> correctSequence; // 0x1EE, 15 bytes
+    Nancy::SoundManager::SoundDescription clickSound; // 0x1FD, kNormal
+    SceneChangeDesc solveExitScene; // 0x21F
+    FlagDesc flagOnSolve; // 0x229
+    uint16 solveSoundDelay; // 0x22C 
+    Nancy::SoundManager::SoundDescription solveSound; // 0x22E
+    SceneChangeDesc exitScene; // 0x250
+    //
+    FlagDesc flagOnExit; // 0x25A
+    Common::Rect exitHotspot; // 0x25D
+
+    SolveState solveState = kNotSolved;
+    Graphics::ManagedSurface image;
+    Common::Array<int16> clickedSequence;
+    Common::Array<bool> drawnElements;
+    Time solveSoundPlayTime;
+
+protected:
+    virtual uint16 getZOrder() const override { return 7; }
+    virtual BlitType getBlitType() const override { return kTrans; }
+    virtual bool isViewportRelative() const override { return true; }
+
+    void drawElement(uint id);
+    void undrawElement(uint id);
+    void clearAllElements();
+};
+
+} // End of namespace Action 
+} // End of namespace Nancy
+
+#endif // NANCY_ACTION_ORDERINGPUZZLE_H
\ No newline at end of file
diff --git a/engines/nancy/action/recordtypes.cpp b/engines/nancy/action/recordtypes.cpp
index 3a5ce5ce20..6f8e081fff 100644
--- a/engines/nancy/action/recordtypes.cpp
+++ b/engines/nancy/action/recordtypes.cpp
@@ -361,10 +361,6 @@ void EventFlagsMultiHS::execute(NancyEngine *engine) {
     }
 }
 
-uint16 OrderingPuzzle::readData(Common::SeekableReadStream &stream) {
-    return readRaw(stream, 0x26D); // TODO
-}
-
 uint16 LoseGame::readData(Common::SeekableReadStream &stream) {
     loseData = stream.readByte();
     return 1;
@@ -437,6 +433,7 @@ void ShowInventoryItem::init() {
     _engine->_res->loadImage("ciftree", imageName, srcSurf);
     _fullSurface.create(srcSurf.w, srcSurf.h, srcSurf.format);
     _fullSurface.blitFrom(srcSurf);
+    srcSurf.free();
 
     _drawSurface.create(_fullSurface, bitmaps[0].src);
 
diff --git a/engines/nancy/action/recordtypes.h b/engines/nancy/action/recordtypes.h
index 6c2042ddee..0db919c19e 100644
--- a/engines/nancy/action/recordtypes.h
+++ b/engines/nancy/action/recordtypes.h
@@ -245,11 +245,6 @@ public:
     Common::Array<HotspotDesc> hotspots;
 };
 
-class OrderingPuzzle : public ActionRecord {
-public:
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
-};
-
 class LoseGame : public ActionRecord {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
diff --git a/engines/nancy/module.mk b/engines/nancy/module.mk
index beec002714..beb7932133 100644
--- a/engines/nancy/module.mk
+++ b/engines/nancy/module.mk
@@ -3,6 +3,7 @@ MODULE := engines/nancy
 MODULE_OBJS = \
   action/actionmanager.o \
   action/arfactory_v1.o \
+  action/orderingpuzzle.o \
   action/primaryvideo.o \
   action/recordtypes.o \
   action/secondaryvideo.o \


Commit: a7619575ac3223b712e8c464ccec570823b4225f
    https://github.com/scummvm/scummvm/commit/a7619575ac3223b712e8c464ccec570823b4225f
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Bitmap animation fixes

Added support for sound in PlayIntStaticBitmapAnimation and fixed a surface not getting freed after use.

Changed paths:
    engines/nancy/action/staticbitmapanim.cpp
    engines/nancy/action/staticbitmapanim.h


diff --git a/engines/nancy/action/staticbitmapanim.cpp b/engines/nancy/action/staticbitmapanim.cpp
index 0e1bc7dbd3..b910b051e0 100644
--- a/engines/nancy/action/staticbitmapanim.cpp
+++ b/engines/nancy/action/staticbitmapanim.cpp
@@ -39,13 +39,13 @@ void PlayIntStaticBitmapAnimation::init() {
 
     _fullSurface.create(surf.w, surf.h, surf.format);
     _fullSurface.blitFrom(surf);
+    surf.free();
     setFrame(0);
 
     RenderObject::init();
 }
 
 uint16 PlayIntStaticBitmapAnimation::readData(Common::SeekableReadStream &stream) {
-    uint beginOffset = stream.pos();
     char name[10];
     stream.read(name, 10);
     imageName = Common::String(name);
@@ -58,16 +58,9 @@ uint16 PlayIntStaticBitmapAnimation::readData(Common::SeekableReadStream &stream
     stream.skip(2);
     soundFlagDesc.label = stream.readSint16LE();
     soundFlagDesc.flag = (NancyFlag)stream.readUint16LE();
-
     SceneChange::readData(stream);
-
     triggerFlags.readData(stream);
-
-    stream.read(name, 10);
-    soundName = name;
-    channelID = stream.readUint16LE();
-
-    stream.seek(beginOffset + 0x74, SEEK_SET);
+    sound.read(stream, SoundManager::SoundDescription::kNormal);
     uint numFrames = stream.readUint16LE();
 
     for (uint i = firstFrame; i <= lastFrame; ++i) {
@@ -93,11 +86,8 @@ void PlayIntStaticBitmapAnimation::execute(NancyEngine *engine) {
         case kBegin:
             init();
             registerGraphics();
-
-            if (soundName != "NO SOUND") {
-                warning("PlayIntStaticBitmapAnimation has a sound, please implement it!");
-            }
-
+            _engine->sound->loadSound(sound);
+            _engine->sound->playSound(sound.channelID);
             state = kRun;
             // fall through
         case kRun: {
@@ -125,8 +115,8 @@ void PlayIntStaticBitmapAnimation::execute(NancyEngine *engine) {
         }
         case kActionTrigger:
             triggerFlags.execute(engine);
-
             SceneChange::execute(engine);
+            _engine->sound->stopSound(sound.channelID);
             break;
     }
 }
diff --git a/engines/nancy/action/staticbitmapanim.h b/engines/nancy/action/staticbitmapanim.h
index f079a60193..bad0c93126 100644
--- a/engines/nancy/action/staticbitmapanim.h
+++ b/engines/nancy/action/staticbitmapanim.h
@@ -26,6 +26,8 @@
 #include "engines/nancy/action/recordtypes.h"
 #include "engines/nancy/renderobject.h"
 
+#include "engines/nancy/sound.h"
+
 #include "common/str.h"
 #include "common/array.h"
 
@@ -51,9 +53,7 @@ public:
     EventFlagsDesc triggerFlags;
     Time frameTime;
 
-    // Todo
-    Common::String soundName;
-    uint16 channelID;
+    Nancy::SoundManager::SoundDescription sound;
 
     // Describes a single frame in this animation
     Common::Array<Common::Rect> srcRects;


Commit: 46ed7dce8706147b7d80d7132b595f86c8be72b3
    https://github.com/scummvm/scummvm/commit/46ed7dce8706147b7d80d7132b595f86c8be72b3
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Fix show_image and play_video debug commands

Added code to make the show_image and play_video debug commands play nice with the new graphics manager.

Changed paths:
    engines/nancy/console.cpp
    engines/nancy/console.h
    engines/nancy/graphics.cpp
    engines/nancy/graphics.h
    engines/nancy/input.h


diff --git a/engines/nancy/console.cpp b/engines/nancy/console.cpp
index 3dabb9712b..4d954a267c 100644
--- a/engines/nancy/console.cpp
+++ b/engines/nancy/console.cpp
@@ -27,6 +27,8 @@
 #include "engines/nancy/sound.h"
 #include "engines/nancy/iff.h"
 #include "engines/nancy/state/scene.h"
+#include "engines/nancy/input.h"
+#include "engines/nancy/graphics.h"
 
 #include "common/system.h"
 #include "common/events.h"
@@ -67,8 +69,9 @@ void NancyConsole::postEnter() {
 			while (!_vm->shouldQuit() && !dec->endOfVideo()) {
 				Common::Event event;
 				if (ev->pollEvent(event)) {
-					if (event.type == Common::EVENT_KEYDOWN || event.type == Common::EVENT_LBUTTONDOWN)
+					if (event.type == Common::EVENT_CUSTOM_ENGINE_ACTION_END && event.customType == Nancy::InputManager::kNancyActionLeftClick) {
 						break;
+					}
 				}
 
 				if (dec->needsUpdate()) {
@@ -78,14 +81,49 @@ void NancyConsole::postEnter() {
 						_vm->_system->updateScreen();
 					}
 				}
+
 				_vm->_system->delayMillis(10);
 			}
-		} else
+
+			_vm->graphicsManager->redrawAll();
+		} else {
 			debugPrintf("Failed to load '%s'\n", _videoFile.c_str());
+		}
 
 		_videoFile.clear();
 		delete dec;
 	}
+
+	if (!_imageFile.empty()) {
+		Graphics::Surface surf;
+		if (_vm->_res->loadImage(_imageCifTree, _imageFile, surf)) {
+			_vm->_system->fillScreen(0);
+			_vm->_system->copyRectToScreen(surf.getPixels(), surf.pitch, 0, 0, surf.w > 640 ? 640 : surf.w, surf.h > 480 ? 480 : surf.h);
+			_vm->_system->updateScreen();
+			surf.free();
+
+			Common::EventManager *ev = g_system->getEventManager();
+			while (!_vm->shouldQuit()) {
+				Common::Event event;
+				if (ev->pollEvent(event)) {
+					if (event.type == Common::EVENT_CUSTOM_ENGINE_ACTION_END && event.customType == Nancy::InputManager::kNancyActionLeftClick) {
+						break;
+					}
+
+					_vm->_system->updateScreen();
+				}
+
+				_vm->_system->delayMillis(10);
+			}
+			
+			_vm->graphicsManager->redrawAll();
+		} else {
+			debugPrintf("Failed to load image '%s'\n", _imageFile.c_str());
+		}
+		
+		_imageFile.clear();
+		_imageCifTree.clear();
+	}
 }
 
 bool NancyConsole::Cmd_cifHexDump(int argc, const char **argv) {
@@ -220,17 +258,9 @@ bool NancyConsole::Cmd_showImage(int argc, const char **argv) {
 		return true;
 	}
 
-	Graphics::Surface surf;
-	if (_vm->_res->loadImage((argc == 2 ? "ciftree" : argv[2]), argv[1], surf)) {
-		_vm->_system->fillScreen(0);
-		_vm->_system->copyRectToScreen(surf.getPixels(), surf.pitch, 0, 0, surf.w > 640 ? 640 : surf.w, surf.h > 480 ? 480 : surf.h);
-		surf.free();
-		_vm->_gameFlow.minGameState = NancyEngine::kIdle;
-		return cmdExit(0, 0);
-	} else {
-		debugPrintf("Failed to load image\n");
-		return true;
-	}
+	_imageFile = argv[1];
+	_imageCifTree = argc == 2 ? "ciftree" : argv[2];
+	return cmdExit(0, 0);
 }
 
 bool NancyConsole::Cmd_loadCal(int argc, const char **argv) {
diff --git a/engines/nancy/console.h b/engines/nancy/console.h
index cb6fa19942..9141523e88 100644
--- a/engines/nancy/console.h
+++ b/engines/nancy/console.h
@@ -54,6 +54,8 @@ private:
 	bool Cmd_sceneID(int argc, const char **argv);
 
 	Common::String _videoFile;
+	Common::String _imageFile;
+	Common::String _imageCifTree;
 };
 
 } // End of namespace Nancy
diff --git a/engines/nancy/graphics.cpp b/engines/nancy/graphics.cpp
index 6242508593..d4ef0e3e2c 100644
--- a/engines/nancy/graphics.cpp
+++ b/engines/nancy/graphics.cpp
@@ -117,6 +117,12 @@ void GraphicsManager::clearObjects() {
     _objects.clear();
 }
 
+void GraphicsManager::redrawAll() {
+    for (auto &obj : _objects) {
+        obj->_needsRedraw = true;
+    }
+}
+
 void GraphicsManager::loadFonts() {
     Common::SeekableReadStream *chunk = _engine->getBootChunkStream("FONT");
     
diff --git a/engines/nancy/graphics.h b/engines/nancy/graphics.h
index ad8364017d..34df53fe67 100644
--- a/engines/nancy/graphics.h
+++ b/engines/nancy/graphics.h
@@ -48,6 +48,8 @@ public:
     void removeObject(RenderObject *object);
     void clearObjects();
 
+    void redrawAll();
+
     Font *getFont(uint id) { return id < _fonts.size() ? &_fonts[id] : nullptr; }
 
     Graphics::ManagedSurface object0;
diff --git a/engines/nancy/input.h b/engines/nancy/input.h
index c65a39a9c9..94f5df16da 100644
--- a/engines/nancy/input.h
+++ b/engines/nancy/input.h
@@ -65,6 +65,7 @@ struct NancyInput {
 // This class handles collecting events and translating them to a NancyInput object,
 // which can then be pulled by interested classes through getInput()
 class InputManager {
+    friend class NancyConsole;
 enum NancyAction {
     kNancyActionMoveUp,
     kNancyActionMoveDown,


Commit: 725ff2f95dc998c8e19e5124d49d97ee96d3ba7c
    https://github.com/scummvm/scummvm/commit/725ff2f95dc998c8e19e5124d49d97ee96d3ba7c
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Implement inventory scrollbar

Implemented scrolling of the inventory through the scrollbar.

Changed paths:
    engines/nancy/state/scene.cpp
    engines/nancy/ui/inventorybox.cpp
    engines/nancy/ui/inventorybox.h


diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index 197a6cdae0..71783fac59 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -115,7 +115,7 @@ void Scene::removeItemFromInventory(uint16 id, bool pickUp) {
         setHeldItem(id);
     }
 
-    _inventoryBox.removeItem(id, pickUp);
+    _inventoryBox.removeItem(id);
 }
 
 void Scene::registerGraphics() {
diff --git a/engines/nancy/ui/inventorybox.cpp b/engines/nancy/ui/inventorybox.cpp
index 8ac17da5b4..83e8ca11ca 100644
--- a/engines/nancy/ui/inventorybox.cpp
+++ b/engines/nancy/ui/inventorybox.cpp
@@ -89,6 +89,14 @@ void InventoryBox::init() {
     _shades.init();
 }
 
+void InventoryBox::updateGraphics() {
+    if (_scrollbarPos != _scrollbar.getPos()) {
+        _scrollbarPos = _scrollbar.getPos();
+
+        onScrollbarMove();
+    }
+}
+
 void InventoryBox::registerGraphics() {
     RenderObject::registerGraphics();
     _scrollbar.registerGraphics();
@@ -96,7 +104,9 @@ void InventoryBox::registerGraphics() {
 }
 
 void InventoryBox::handleInput(NancyInput &input) {
-    _scrollbar.handleInput(input);
+    if (_order.size()) {
+        _scrollbar.handleInput(input);
+    }
     
     for (uint i = 0; i < 4; ++i) {
         if (_itemHotspots[i].hotspot.contains(input.mousePos)) {
@@ -129,7 +139,7 @@ void InventoryBox::addItem(uint itemID) {
     onReorder();
 }
 
-void InventoryBox::removeItem(uint itemID, bool hold) {
+void InventoryBox::removeItem(uint itemID) {
     for (auto &i : _order) {
         if (i == itemID) {
             if (_order.size() == 1) {
@@ -144,6 +154,8 @@ void InventoryBox::removeItem(uint itemID, bool hold) {
 }
 
 void InventoryBox::onReorder() {
+    onScrollbarMove();
+
     _fullInventorySurface.clear();
     for (uint i = 0; i < _order.size(); ++i) {
         Common::Rect dest;
@@ -154,15 +166,7 @@ void InventoryBox::onReorder() {
 
         _fullInventorySurface.blitFrom(_iconsSurface, _itemDescriptions[_order[i]].sourceRect, destPoint);
     }
-    
-    // Scroll one page up if current on becomes empty
-    uint curPage = _drawSurface.getOffsetFromOwner().y / _screenPosition.height();
-    if (_order.size() < curPage * 4) {
-        --curPage;
-        onScrollbarMove();
-    }
 
-    setHotspots(curPage);
 
     _needsRedraw = true;
 }
@@ -180,9 +184,9 @@ void InventoryBox::setHotspots(uint pageNr) {
 void InventoryBox::onScrollbarMove() {
     float scrollPos = _scrollbar.getPos();
 
-    uint numPages = _order.size() / 4 + 1;
+    float numPages = (_order.size() - 1) / 4 + 1;
     float pageFrac = 1 / numPages;
-    uint curPage = scrollPos / pageFrac;
+    uint curPage = MIN<uint>(scrollPos / pageFrac, numPages - 1);
 
     Common::Rect sourceRect = _screenPosition;
     sourceRect.moveTo(0, curPage * sourceRect.height());
diff --git a/engines/nancy/ui/inventorybox.h b/engines/nancy/ui/inventorybox.h
index 8673713b5d..0de648d2ea 100644
--- a/engines/nancy/ui/inventorybox.h
+++ b/engines/nancy/ui/inventorybox.h
@@ -53,17 +53,19 @@ public:
     InventoryBox(RenderObject &redrawFrom) :
         RenderObject(redrawFrom),
         _scrollbar(redrawFrom, this),
-        _shades(*this, this) {}
+        _shades(*this, this),
+        _scrollbarPos(0) {}
 
     virtual ~InventoryBox() { _fullInventorySurface.free(); _iconsSurface.free(); }
 
     virtual void init() override;
+    virtual void updateGraphics() override;
     virtual void registerGraphics() override;
     void handleInput(NancyInput &input);
 
     // To be called from Scene
     void addItem(uint itemID);
-    void removeItem(uint itemID, bool hold = true);
+    void removeItem(uint itemID);
 
     ItemDescription getItemDescription(uint id) { return _itemDescriptions[id]; }
 
@@ -126,6 +128,8 @@ private:
     InventoryScrollbar _scrollbar;
     Shades _shades;
 
+    float _scrollbarPos;
+
     Common::Array<uint> _order;
     ItemHotspot _itemHotspots[4]; 
 


Commit: c7e1c95803d51a7c08fe25ea83aa44ab38c930d0
    https://github.com/scummvm/scummvm/commit/c7e1c95803d51a7c08fe25ea83aa44ab38c930d0
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Implement rotating lock puzzle

Implemented the RotatingLockPuzzle action record.

Changed paths:
  A engines/nancy/action/rotatinglockpuzzle.cpp
  A engines/nancy/action/rotatinglockpuzzle.h
    engines/nancy/action/arfactory_v1.cpp
    engines/nancy/action/orderingpuzzle.cpp
    engines/nancy/action/orderingpuzzle.h
    engines/nancy/action/recordtypes.cpp
    engines/nancy/action/recordtypes.h
    engines/nancy/module.mk


diff --git a/engines/nancy/action/arfactory_v1.cpp b/engines/nancy/action/arfactory_v1.cpp
index 99b32d12d8..8e316f2f0f 100644
--- a/engines/nancy/action/arfactory_v1.cpp
+++ b/engines/nancy/action/arfactory_v1.cpp
@@ -27,6 +27,7 @@
 #include "engines/nancy/action/secondaryvideo.h"
 #include "engines/nancy/action/staticbitmapanim.h"
 #include "engines/nancy/action/orderingpuzzle.h"
+#include "engines/nancy/action/rotatinglockpuzzle.h"
 
 #include "engines/nancy/state/scene.h"
 
@@ -114,7 +115,7 @@ ActionRecord *ActionManager::createActionRecord(uint16 type) {
         case 0x67:
             return new DifficultyLevel();
         case 0x68:
-            return new RotatingLockPuzzle();
+            return new RotatingLockPuzzle(_engine->scene->getViewport());
         case 0x69:
             return new LeverPuzzle();
         case 0x6A:
diff --git a/engines/nancy/action/orderingpuzzle.cpp b/engines/nancy/action/orderingpuzzle.cpp
index e311b4a54a..1c0796c851 100644
--- a/engines/nancy/action/orderingpuzzle.cpp
+++ b/engines/nancy/action/orderingpuzzle.cpp
@@ -233,4 +233,4 @@ void OrderingPuzzle::clearAllElements() {
 }
 
 } // End of namespace Action
-} // End of namespace Nancy
\ No newline at end of file
+} // End of namespace Nancy
diff --git a/engines/nancy/action/orderingpuzzle.h b/engines/nancy/action/orderingpuzzle.h
index c6a22a5357..2626d6169d 100644
--- a/engines/nancy/action/orderingpuzzle.h
+++ b/engines/nancy/action/orderingpuzzle.h
@@ -41,7 +41,7 @@ class OrderingPuzzle : public ActionRecord, public RenderObject {
 public:
     enum SolveState { kNotSolved, kPlaySound, kWaitForSound };
     OrderingPuzzle(RenderObject &redrawFrom) : RenderObject(redrawFrom) {}
-    ~OrderingPuzzle() {}
+    virtual ~OrderingPuzzle() {}
 
     virtual void init() override;
     
@@ -60,7 +60,6 @@ public:
     uint16 solveSoundDelay; // 0x22C 
     Nancy::SoundManager::SoundDescription solveSound; // 0x22E
     SceneChangeDesc exitScene; // 0x250
-    //
     FlagDesc flagOnExit; // 0x25A
     Common::Rect exitHotspot; // 0x25D
 
@@ -83,4 +82,4 @@ protected:
 } // End of namespace Action 
 } // End of namespace Nancy
 
-#endif // NANCY_ACTION_ORDERINGPUZZLE_H
\ No newline at end of file
+#endif // NANCY_ACTION_ORDERINGPUZZLE_H
diff --git a/engines/nancy/action/recordtypes.cpp b/engines/nancy/action/recordtypes.cpp
index 6f8e081fff..8d78a7df7c 100644
--- a/engines/nancy/action/recordtypes.cpp
+++ b/engines/nancy/action/recordtypes.cpp
@@ -424,10 +424,6 @@ void DifficultyLevel::execute(NancyEngine *engine) {
     isDone = true;
 }
 
-uint16 RotatingLockPuzzle::readData(Common::SeekableReadStream &stream) {
-    return readRaw(stream, 0x2A4); // TODO
-}
-
 void ShowInventoryItem::init() {
     Graphics::Surface srcSurf;
     _engine->_res->loadImage("ciftree", imageName, srcSurf);
diff --git a/engines/nancy/action/recordtypes.h b/engines/nancy/action/recordtypes.h
index 0db919c19e..f12fc24e88 100644
--- a/engines/nancy/action/recordtypes.h
+++ b/engines/nancy/action/recordtypes.h
@@ -312,11 +312,6 @@ public:
     FlagDesc flag;
 };
 
-class RotatingLockPuzzle : public ActionRecord {
-public:
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
-};
-
 class ShowInventoryItem : public ActionRecord, public RenderObject {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
diff --git a/engines/nancy/action/rotatinglockpuzzle.cpp b/engines/nancy/action/rotatinglockpuzzle.cpp
new file mode 100644
index 0000000000..72e28e09a9
--- /dev/null
+++ b/engines/nancy/action/rotatinglockpuzzle.cpp
@@ -0,0 +1,224 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/nancy/action/rotatinglockpuzzle.h"
+
+#include "engines/nancy/util.h"
+#include "engines/nancy/nancy.h"
+#include "engines/nancy/resource.h"
+#include "engines/nancy/graphics.h"
+#include "engines/nancy/state/scene.h"
+
+#include "common/random.h"
+
+namespace Nancy {
+namespace Action {
+
+void RotatingLockPuzzle::init() {
+    _drawSurface.create(_screenPosition.width(), _screenPosition.height(), GraphicsManager::pixelFormat);
+    _drawSurface.clear(GraphicsManager::transColor);
+
+    Graphics::Surface surf;
+    _engine->_res->loadImage("ciftree", imageName, surf);
+    image.create(surf.w, surf.h, surf.format);
+    image.blitFrom(surf);
+    surf.free();
+}
+
+uint16 RotatingLockPuzzle::readData(Common::SeekableReadStream &stream) {
+    char buf[10];
+    stream.read(buf, 10);
+    imageName = buf;
+
+    uint numDials = stream.readUint16LE();
+
+    for (uint i = 0; i < 10; ++i) {
+        srcRects.push_back(Common::Rect());
+        readRect(stream, srcRects.back());
+    }
+
+    for (uint i = 0; i < numDials; ++i) {
+        destRects.push_back(Common::Rect());
+        readRect(stream, destRects.back());
+
+        if (i == 0) {
+            _screenPosition = destRects.back();
+        } else {
+            _screenPosition.extend(destRects.back());
+        }
+    }
+
+    stream.skip((8 - numDials) * 16);
+
+    for (uint i = 0; i < numDials; ++i) {
+        upHotspots.push_back(Common::Rect());
+        readRect(stream, upHotspots.back());
+    }
+
+    stream.skip((8 - numDials) * 16);
+
+    for (uint i = 0; i < numDials; ++i) {
+        downHotspots.push_back(Common::Rect());
+        readRect(stream, downHotspots.back());
+    }
+
+    stream.skip((8 - numDials) * 16);
+
+    for (uint i = 0; i < numDials; ++i) {
+        correctSequence.push_back(stream.readByte());
+    }
+
+    stream.skip(8 - numDials);
+
+    clickSound.read(stream, SoundManager::SoundDescription::kNormal);
+    solveExitScene.readData(stream);
+    stream.skip(2); // shouldStopRendering, useless
+    flagOnSolve.label = stream.readUint16LE();
+    flagOnSolve.flag = (NancyFlag)stream.readByte();
+    solveSoundDelay = stream.readUint16LE();
+    solveSound.read(stream, SoundManager::SoundDescription::kNormal);
+    exitScene.readData(stream);
+    stream.skip(2); // shouldStopRendering, useless
+    flagOnExit.label = stream.readUint16LE();
+    flagOnExit.flag = (NancyFlag)stream.readByte();
+    readRect(stream, exitHotspot);
+
+    return 0x2A4;
+}
+
+void RotatingLockPuzzle::execute(Nancy::NancyEngine *engine) {
+    switch (state) {
+        case kBegin:
+            init();
+            registerGraphics();
+
+            for (uint i = 0; i < correctSequence.size(); ++i) {
+                currentSequence.push_back(_engine->_rnd->getRandomNumber(9));
+                drawDial(i);
+            }
+
+            _engine->sound->loadSound(clickSound);
+            _engine->sound->loadSound(solveSound);
+            state = kRun;
+            // fall through
+        case kRun:
+            switch (solveState) {
+                case kNotSolved:
+                    for (uint i = 0; i < correctSequence.size(); ++i) {
+                        if (currentSequence[i] != (int16)correctSequence[i]) {
+                            return;
+                        }
+                    }
+
+                    _engine->scene->setEventFlag(flagOnSolve.label, flagOnSolve.flag);
+                    solveSoundPlayTime = _engine->getTotalPlayTime() + solveSoundDelay * 1000;
+                    solveState = kPlaySound;
+                    // fall through
+                case kPlaySound:
+                    if (_engine->getTotalPlayTime() <= solveSoundPlayTime) {
+                        break;
+                    }
+
+                    _engine->sound->playSound(solveSound.channelID);
+                    solveState = kWaitForSound;
+                    break;
+                case kWaitForSound:
+                    if (!_engine->sound->isSoundPlaying(solveSound.channelID)) {
+                        state = kActionTrigger;
+                    }
+
+                    break;
+            }
+            break;
+        case kActionTrigger:
+            _engine->sound->stopSound(clickSound.channelID);
+            _engine->sound->stopSound(solveSound.channelID);
+
+            if (solveState == kNotSolved) {
+                if (exitScene.sceneID != 9999) {
+                    _engine->scene->changeScene(exitScene.sceneID, exitScene.frameID, exitScene.verticalOffset, exitScene.doNotStartSound);
+                    _engine->scene->setEventFlag(flagOnExit.label, flagOnExit.flag);
+                }
+            } else {
+                if (solveExitScene.sceneID != 9999) {
+                    _engine->scene->changeScene(solveExitScene.sceneID, solveExitScene.frameID, solveExitScene.verticalOffset, solveExitScene.doNotStartSound);
+                }
+            }
+
+            isDone = true;
+    }
+}
+
+void RotatingLockPuzzle::handleInput(NancyInput &input) {
+    if (solveState != kNotSolved) {
+        return;
+    }
+
+    if (_engine->scene->getViewport().convertViewportToScreen(exitHotspot).contains(input.mousePos)) {
+        _engine->cursorManager->setCursorType(CursorManager::kExitArrow);
+
+        if (input.input & NancyInput::kLeftMouseButtonUp) {
+            state = kActionTrigger;
+        }
+        return;
+    }
+
+    for (uint i = 0; i < upHotspots.size(); ++i) {
+        if (_engine->scene->getViewport().convertViewportToScreen(upHotspots[i]).contains(input.mousePos)) {
+            _engine->cursorManager->setCursorType(CursorManager::kHotspot);
+
+            if (input.input & NancyInput::kLeftMouseButtonUp) {
+                _engine->sound->playSound(clickSound.channelID);
+                
+                currentSequence[i] = ++currentSequence[i] > 9 ? 0 : currentSequence[i];
+                drawDial(i);
+            }
+            return;
+        }
+    }
+
+    for (uint i = 0; i < downHotspots.size(); ++i) {
+        if (_engine->scene->getViewport().convertViewportToScreen(downHotspots[i]).contains(input.mousePos)) {
+            _engine->cursorManager->setCursorType(CursorManager::kHotspot);
+
+            if (input.input & NancyInput::kLeftMouseButtonUp) {
+                _engine->sound->playSound(clickSound.channelID);
+
+                int8 n = currentSequence[i];
+                n = --n < 0 ? 9 : n;
+                currentSequence[i] = n;
+                drawDial(i);
+            }
+            return;
+        }
+    }
+}
+
+void RotatingLockPuzzle::drawDial(uint id) {
+    Common::Point destPoint(destRects[id].left - _screenPosition.left, destRects[id].top - _screenPosition.top);
+    _drawSurface.blitFrom(image, srcRects[currentSequence[id]], destPoint);
+
+    _needsRedraw = true;
+}
+
+} // End of namespace Action
+} // End of namespace Nancy
diff --git a/engines/nancy/action/rotatinglockpuzzle.h b/engines/nancy/action/rotatinglockpuzzle.h
new file mode 100644
index 0000000000..f6c05adb26
--- /dev/null
+++ b/engines/nancy/action/rotatinglockpuzzle.h
@@ -0,0 +1,84 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef NANCY_ACTION_ROTATINGLOCKPUZZLE_H
+#define NANCY_ACTION_ROTATINGLOCKPUZZLE_H
+
+#include "engines/nancy/action/recordtypes.h"
+#include "engines/nancy/renderobject.h"
+
+#include "engines/nancy/sound.h"
+#include "engines/nancy/time.h"
+
+#include "common/str.h"
+#include "common/array.h"
+#include "common/rect.h"
+
+namespace Nancy {
+namespace Action {
+
+class RotatingLockPuzzle : public ActionRecord, public RenderObject {
+public:
+    enum SolveState { kNotSolved, kPlaySound, kWaitForSound };
+    RotatingLockPuzzle(RenderObject &redrawFrom) : RenderObject(redrawFrom) {}
+    virtual ~RotatingLockPuzzle() {}
+    
+    virtual void init() override;
+    
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void execute(Nancy::NancyEngine *engine) override;
+    virtual void handleInput(NancyInput &input) override;
+
+    Common::String imageName; // 0x00
+    // 0xA numDials
+    Common::Array<Common::Rect> srcRects; // 0xC, 10
+    Common::Array<Common::Rect> destRects; // 0xAC, 8
+    Common::Array<Common::Rect> upHotspots; // 0x12C, 8
+    Common::Array<Common::Rect> downHotspots; // 0x1AC, 8
+    Common::Array<byte> correctSequence; // 0x22C
+    Nancy::SoundManager::SoundDescription clickSound; // 0x234, kNormal
+    SceneChangeDesc solveExitScene; // 0x256
+    FlagDesc flagOnSolve; // 0x260
+    uint16 solveSoundDelay; // 0x263
+    Nancy::SoundManager::SoundDescription solveSound; // 0x265
+    SceneChangeDesc exitScene; // 0x287
+    FlagDesc flagOnExit; // 0x291
+    Common::Rect exitHotspot; // 0x294
+
+    SolveState solveState = kNotSolved;
+    Graphics::ManagedSurface image;
+    Common::Array<byte> currentSequence;
+    Time solveSoundPlayTime;
+
+
+protected:
+    virtual uint16 getZOrder() const override { return 7; }
+    virtual BlitType getBlitType() const override { return kTrans; }
+    virtual bool isViewportRelative() const override { return true; }
+
+    void drawDial(uint id);
+};
+
+} // End of namespace Action
+} // End of namespace Nancy
+
+#endif // NANCY_ACTION_ROTATINGLOCKPUZZLE_H
diff --git a/engines/nancy/module.mk b/engines/nancy/module.mk
index beb7932133..546936e80b 100644
--- a/engines/nancy/module.mk
+++ b/engines/nancy/module.mk
@@ -6,6 +6,7 @@ MODULE_OBJS = \
   action/orderingpuzzle.o \
   action/primaryvideo.o \
   action/recordtypes.o \
+  action/rotatinglockpuzzle.o \
   action/secondaryvideo.o \
   action/staticbitmapanim.o \
   ui/frame.o \


Commit: b7c2b5a6e827d3ecd5c9a6dae62ee0c6d6733633
    https://github.com/scummvm/scummvm/commit/b7c2b5a6e827d3ecd5c9a6dae62ee0c6d6733633
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Textbox improvements

The textbox's token parser no longer makes a distinction between captions and responses, and also supports telephone-specific tokens. The code still makes a couple of assumptions about the input text, but those can be fixed in the future if needed.

Changed paths:
    engines/nancy/ui/textbox.cpp
    engines/nancy/ui/textbox.h


diff --git a/engines/nancy/ui/textbox.cpp b/engines/nancy/ui/textbox.cpp
index 3df6b940d4..3288d89409 100644
--- a/engines/nancy/ui/textbox.cpp
+++ b/engines/nancy/ui/textbox.cpp
@@ -37,12 +37,14 @@
 namespace Nancy {
 namespace UI {
 
-const Common::String Textbox::beginToken = Common::String("<i>");
-const Common::String Textbox::endToken = Common::String("<o>");
+const Common::String Textbox::CCBeginToken = Common::String("<i>");
+const Common::String Textbox::CCEndToken = Common::String("<o>");
 const Common::String Textbox::colorBeginToken = Common::String("<c1>");
 const Common::String Textbox::colorEndToken = Common::String("<c0>");
 const Common::String Textbox::hotspotToken = Common::String("<h>");
 const Common::String Textbox::newLineToken = Common::String("<n>");
+const Common::String Textbox::tabToken = Common::String("<t>");
+const Common::String Textbox::telephoneEndToken = Common::String("<e>");
 
 void Textbox::init() {    
     Common::SeekableReadStream *chunk = _engine->getBootChunkStream("TBOX");
@@ -63,6 +65,9 @@ void Textbox::init() {
     // Not sure why but to get exact results we subtract 1
     _borderWidth = chunk->readUint16LE() - 1;
 
+    chunk->seek(0x1FE, SEEK_SET);
+    _fontID = chunk->readUint16LE();
+
     chunk = _engine->getBootChunkStream("BSUM");
     chunk->seek(0x164);
     readRect(*chunk, _screenPosition);
@@ -94,11 +99,12 @@ void Textbox::updateGraphics() {
 
     RenderObject::updateGraphics();
 }
+
 void Textbox::handleInput(NancyInput &input) {
     _scrollbar.handleInput(input);
 
-    for (uint i = 0; i < _responses.size(); ++i) {
-        Common::Rect hotspot = _responses[i].hotspot;
+    for (uint i = 0; i < _hotspots.size(); ++i) {
+        Common::Rect hotspot = _hotspots[i];
         hotspot.translate(0, -_drawSurface.getOffsetFromOwner().y);
         if (convertToScreen(hotspot).findIntersectingRect(_screenPosition).contains(input.mousePos)) {
             _engine->cursorManager->setCursorType(CursorManager::kHotspotArrow);
@@ -115,94 +121,111 @@ void Textbox::handleInput(NancyInput &input) {
 }
 
 void Textbox::drawTextbox() {
-    Common::Array<Common::String> wrappedLines;
-    Common::String tokenlessString;
+    using namespace Common;
+
     _numLines = 0;
-    // Hardcode to 1 until I figure out the proper logic
-    Font *font = _engine->graphicsManager->getFont(1);
 
-    uint maxWidth = _fullSurface.w - _borderWidth;
-    uint lineDist = _lineHeight + _lineHeight / 4;
+    Font *font = _engine->graphicsManager->getFont(_fontID);
 
-    Common::String line;
+    uint maxWidth = _fullSurface.w - _borderWidth * 2;
+    uint lineDist = _lineHeight + _lineHeight / 4;
+    
+    for (uint lineID = 0; lineID < _textLines.size(); ++lineID) {
+        Common::String currentLine = _textLines[lineID];
+        currentLine.trim();
 
-    _mainString.trim();
+        uint horizontalOffset = 0;
+        bool hasHotspot = false;
+        Rect hotspot;
 
-    // Scan for and remove tokens from main string
-    if (_mainString.hasPrefix(beginToken) && _mainString.hasSuffix(endToken)) {
-        tokenlessString = _mainString.substr(beginToken.size(), _mainString.size() - beginToken.size() - endToken.size());
-        if (tokenlessString.hasSuffix(newLineToken)) {
-            tokenlessString = tokenlessString.substr(0, tokenlessString.size() - newLineToken.size());
+        // Trim the begin and end tokens from the line
+        if (currentLine.hasPrefix(CCBeginToken) && currentLine.hasSuffix(CCEndToken)) {
+            currentLine = currentLine.substr(CCBeginToken.size(), currentLine.size() - CCBeginToken.size() - CCEndToken.size());
+        }
+        
+        // Replace every newline token with \n
+        uint32 newLinePos;
+        while (newLinePos = currentLine.find(newLineToken), newLinePos != String::npos) {
+            currentLine.replace(newLinePos, newLineToken.size(), "\n");
         }
-    }
-
-    // Wrap text for main string
-    font->wordWrapText(tokenlessString, maxWidth, wrappedLines);
 
-    // Draw main string
-    for (auto &str : wrappedLines) {
-        font->drawString(&_fullSurface, str, _borderWidth, _firstLineOffset - font->getFontHeight() + _numLines * lineDist, maxWidth, 0);
-        ++_numLines;
-    }
+        // Simply remove telephone end token
+        if (currentLine.hasSuffix(telephoneEndToken)) {
+            currentLine = currentLine.substr(0, currentLine.size() - telephoneEndToken.size());
+        }
 
-    for (auto &res : _responses) {
-        // Scan for tokens
-        ++_numLines;
-        bool newLineAtEnd = false;
-        uint colorCharOffset = 0;
-        uint colorPixelOffset = 0;
+        // Remove hotspot token and mark that we need to calculate the bounds
+        // Assumes a single text line has a single hotspot
+        uint32 hotspotPos = currentLine.find(hotspotToken);
+        if (hotspotPos != String::npos) {
+            currentLine.erase(hotspotPos, hotspotToken.size());
+            hasHotspot = true;
+        }
 
-        res.text.trim();
+        // Subdivide current line into sublines for proper handling of the tab and color tokens
+        // Assumes the tab token is on a new line
+        while (!currentLine.empty())
+        {
+            if (currentLine.hasPrefix(tabToken)) {
+                horizontalOffset += font->getStringWidth("    "); // Replace tab with 4 spaces
+                currentLine = currentLine.substr(tabToken.size());
+            }
 
-        tokenlessString.clear();
-        wrappedLines.clear();
+            String currentSubLine;
 
-        if (res.text.hasPrefix(colorBeginToken)) {
-            // Create a substring with all (usually just one) of the colored characters
-            for (uint i = colorBeginToken.size(); res.text.substr(i, colorEndToken.size()) != colorEndToken; ++i) {
-                tokenlessString += res.text[i];
+            uint32 nextTabPos = currentLine.find(tabToken);
+            if (nextTabPos != String::npos) {
+                currentSubLine = currentLine.substr(0, nextTabPos);
+                currentLine = currentLine.substr(nextTabPos);
+            } else {
+                currentSubLine = currentLine;
+                currentLine.clear();
             }
 
-            // Draw the color string
-            font->drawString(&_fullSurface, tokenlessString, _borderWidth, _firstLineOffset - font->getFontHeight() + _numLines * lineDist, maxWidth, 1);
-            colorCharOffset = tokenlessString.size() + colorBeginToken.size() + colorEndToken.size();
-            colorPixelOffset = font->getStringWidth(tokenlessString);
-        }
+            // Assumes color token will be at the beginning of the line, and color string will not need wrapping
+            if (currentSubLine.hasPrefix(colorBeginToken)) {
+                // Found color string, look for end token
+                uint32 colorEndPos = currentSubLine.find(colorEndToken);
 
-        tokenlessString = res.text.substr(colorCharOffset, res.text.size() - colorCharOffset);
+                Common::String colorSubLine = currentSubLine.substr(colorBeginToken.size(), colorEndPos - colorBeginToken.size());
+                currentSubLine = currentSubLine.substr(colorBeginToken.size() + colorEndToken.size() + colorSubLine.size());
 
+                // Draw the color line
+                font->drawString(&_fullSurface, colorSubLine, _borderWidth + horizontalOffset, _firstLineOffset - font->getFontHeight() + _numLines * lineDist, maxWidth, 1);
+                horizontalOffset += font->getStringWidth(colorSubLine);
+            }
 
-        // Remove the new line token and add a line break
-        if (tokenlessString.hasSuffix(newLineToken)) {
-            newLineAtEnd = true;
-            tokenlessString = tokenlessString.substr(0, tokenlessString.size() - newLineToken.size());
-        }
+            Array<Common::String> wrappedLines;
 
-        // Remove the hotspot token
-        if (tokenlessString.hasSuffix(hotspotToken)) {
-            tokenlessString = tokenlessString.substr(0, tokenlessString.size() - hotspotToken.size());
-        }
+            // Do word wrapping on the rest of the text
+            font->wordWrapText(currentSubLine, maxWidth, wrappedLines, horizontalOffset);
 
-        // Word wrap the response
-        font->wordWrapText(tokenlessString, maxWidth, wrappedLines, colorPixelOffset);
+            if (hasHotspot) {
+                hotspot.left = _borderWidth;
+                hotspot.top = _firstLineOffset - font->getFontHeight() + (_numLines + 1) * lineDist;
+                hotspot.setHeight((wrappedLines.size() - 1) * lineDist + _lineHeight);
+                hotspot.setWidth(0);
+            }
 
-        res.hotspot.left = _borderWidth;
-        res.hotspot.top = _firstLineOffset - font->getFontHeight() + (_numLines + 1) * lineDist;
-        res.hotspot.setHeight((wrappedLines.size() - 1) * lineDist + _lineHeight);
-        res.hotspot.setWidth(0);
+            // Draw the wrapped lines
+            for (uint i = 0; i < wrappedLines.size(); ++i) {
+                font->drawString(&_fullSurface, wrappedLines[i], _borderWidth + (i == 0 ? horizontalOffset : 0), _firstLineOffset - font->getFontHeight() + _numLines * lineDist, maxWidth, 0);
+                if (hasHotspot) {
+                    hotspot.setWidth(MAX<int16>(hotspot.width(), font->getStringWidth(wrappedLines[i]) + (i == 0 ? horizontalOffset : 0)));
+                }
+                ++_numLines;
+            }
 
-        // Draw the response
-        for (uint i = 0; i < wrappedLines.size(); ++i) {
-            font->drawString(&_fullSurface, wrappedLines[i], _borderWidth + (i == 0 ? colorPixelOffset : 0), _firstLineOffset - font->getFontHeight() + _numLines * lineDist, maxWidth, 0);
-            // Set the response's hotspot width
-            res.hotspot.setWidth(MAX<int16>(res.hotspot.width(), font->getStringWidth(wrappedLines[i]) + (i == 0 ? colorPixelOffset : 0)));
-            ++_numLines;
+            horizontalOffset = 0;
         }
-
-        // Add a line break if there was a new line token
-        if (!newLineAtEnd) {
-            --_numLines;
+        
+        // Add the hotspot to the list
+        if (hasHotspot) {
+            _hotspots.push_back(hotspot);
         }
+
+        // Add a new line after every text line
+        ++_numLines;
     }
 
     setVisible(true);
@@ -211,8 +234,8 @@ void Textbox::drawTextbox() {
 
 void Textbox::clear() {
     _fullSurface.clear();
-    _mainString.clear();
-    _responses.clear();
+    _textLines.clear();
+    _hotspots.clear();
     _scrollbar.resetPosition();
     _numLines = 0;
     onScrollbarMove();
@@ -221,13 +244,8 @@ void Textbox::clear() {
 
 void Textbox::addTextLine(const Common::String &text) {
     // Scan for the hotspot token and assume the text is the main text if not found
-    if (text.contains(hotspotToken)) {
-        _responses.push_back(Response());
-        _responses.back().text = text;
-    } else {
-        _mainString = text;
-    }
-    
+    _textLines.push_back(text);
+ 
     _needsTextRedraw = true;
 }
 
@@ -264,7 +282,7 @@ void Textbox::onScrollbarMove() {
 
 uint16 Textbox::getInnerHeight() {
     uint lineDist = _lineHeight + _lineHeight / 4;
-    return _numLines * lineDist + 2 * _firstLineOffset;
+    return _numLines * lineDist + _firstLineOffset + lineDist / 2;
 }
 
 void Textbox::TextboxScrollbar::init() {
diff --git a/engines/nancy/ui/textbox.h b/engines/nancy/ui/textbox.h
index b90bcca905..1842cfb9b7 100644
--- a/engines/nancy/ui/textbox.h
+++ b/engines/nancy/ui/textbox.h
@@ -94,8 +94,8 @@ private:
 
     TextboxScrollbar _scrollbar;
 
-    Common::String _mainString;
-    Common::Array<Response> _responses;
+    Common::Array<Common::String> _textLines;
+    Common::Array<Common::Rect> _hotspots;
 
     Common::Rect _scrollbarSourceBounds;
     Common::Point _scrollbarDefaultDest;
@@ -103,17 +103,21 @@ private:
     uint16 _lineHeight;
     uint16 _borderWidth;
     uint16 _numLines;
+    uint16 _fontID;
 
     bool _needsTextRedraw;
     float _scrollbarPos;
 
-    static const Common::String beginToken;
-    static const Common::String endToken;
+    static const Common::String CCBeginToken;
+    static const Common::String CCEndToken;
     static const Common::String colorBeginToken;
     static const Common::String colorEndToken;
     static const Common::String hotspotToken;
     static const Common::String newLineToken;
+    static const Common::String tabToken;
+    static const Common::String telephoneEndToken;
 
+protected:
 };
 
 } // End of namespace UI


Commit: 67f11d9c919f026deddb744d3f52a5d52c183d0e
    https://github.com/scummvm/scummvm/commit/67f11d9c919f026deddb744d3f52a5d52c183d0e
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Scene improvements and fixes

Added functionality for the hint system, and added const qualifiers to functions that don't change the scene's state

Changed paths:
    engines/nancy/state/scene.cpp
    engines/nancy/state/scene.h


diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index 71783fac59..b0e7ae1863 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -153,6 +153,13 @@ void Scene::init() {
 
     _sceneState.nextScene.sceneID = _engine->firstSceneID;
 
+    Common::SeekableReadStream *hint = _engine->getBootChunkStream("HINT");
+    hint->seek(0);
+    for (uint i = 0; i < 3; ++i) {
+        _hintsRemaining.push_back(hint->readByte());
+    }
+    _lastHint = -1;
+
     _frame.init();
     _viewport.init();
     _textbox.init();
@@ -372,7 +379,7 @@ void Scene::setEventFlag(int16 label, NancyFlag flag) {
     }
 }
 
-bool Scene::getEventFlag(int16 label, NancyFlag flag) {
+bool Scene::getEventFlag(int16 label, NancyFlag flag) const {
     if (label > -1) {
         return _flags.eventFlags[label] == flag;
     } else {
@@ -387,7 +394,7 @@ void Scene::setLogicCondition(int16 label, NancyFlag flag) {
     }
 }
 
-bool Scene::getLogicCondition(int16 label, NancyFlag flag) {
+bool Scene::getLogicCondition(int16 label, NancyFlag flag) const {
     if (label > -1) {
         return _flags.logicConditions[label].flag == flag;
     } else {
@@ -402,5 +409,11 @@ void Scene::clearLogicConditions() {
     }
 }
 
+void Scene::useHint(int hintID, int hintWeight) {
+    if (_lastHint == hintID) {
+        _hintsRemaining[_difficulty] += hintWeight;
+    }
+}
+
 } // End of namespace State
 } // End of namespace Nancy
diff --git a/engines/nancy/state/scene.h b/engines/nancy/state/scene.h
index 30958c04fe..e49d9d1f84 100644
--- a/engines/nancy/state/scene.h
+++ b/engines/nancy/state/scene.h
@@ -86,6 +86,7 @@ public:
         _engine (engine),
         _state (kInit),
         _frame(engine),
+        _lastHint(-1),
         _gameStateRequested(NancyEngine::kScene),
         _viewport(engine),
         _textbox(_frame),
@@ -103,24 +104,29 @@ public:
 
     void addItemToInventory(uint16 id);
     void removeItemFromInventory(uint16 id, bool pickUp = true);
-    int16 getHeldItem() { return _flags.heldItem; }
+    int16 getHeldItem() const { return _flags.heldItem; }
     void setHeldItem(int16 id) { _flags.heldItem = id; _engine->cursorManager->setCursorItemID(id); }
+    NancyFlag hasItem(int16 id) const { return _flags.items[id]; }
 
     void setEventFlag(int16 label, NancyFlag flag = kTrue);
-    bool getEventFlag(int16 label, NancyFlag flag = kTrue);
+    bool getEventFlag(int16 label, NancyFlag flag = kTrue) const;
 
     void setLogicCondition(int16 label, NancyFlag flag = kTrue);
-    bool getLogicCondition(int16 label, NancyFlag flag = kTrue);
+    bool getLogicCondition(int16 label, NancyFlag flag = kTrue) const;
     void clearLogicConditions();
 
     void setDifficulty(uint difficulty) { _difficulty = difficulty; }
+    uint16 getDifficulty() const { return _difficulty; }
+
+    byte getHintsRemaining() const { return _hintsRemaining[_difficulty]; }
+    void useHint(int hintID, int hintWeight);
 
     void requestStateChange(NancyEngine::GameState state) { _gameStateRequested = state; }
 
     void resetAndStartTimer() { _timers.timerIsActive = true; _timers.timerTime = 0; }
     void stopTimer() { _timers.timerIsActive = false; _timers.timerTime = 0; }
 
-    Time getMovementTimeDelta(bool fast) { return fast ? _sceneState.summary.fastMoveTimeDelta : _sceneState.summary.slowMoveTimeDelta; }
+    Time getMovementTimeDelta(bool fast) const { return fast ? _sceneState.summary.fastMoveTimeDelta : _sceneState.summary.slowMoveTimeDelta; }
 
     void registerGraphics();
 
@@ -220,6 +226,8 @@ protected:
     PlayFlags _flags;
     Timers _timers;
     uint16 _difficulty;
+    Common::Array<byte> _hintsRemaining;
+    int16 _lastHint;
     NancyEngine::GameState _gameStateRequested;
 
     Action::ActionManager _actionManager;


Commit: d83edfdca395e8b032015346fb9133cf4267b306
    https://github.com/scummvm/scummvm/commit/d83edfdca395e8b032015346fb9133cf4267b306
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Implement telephone/hint system

Implemented the Telephone and HintSystem action records, which together make up the functionality of nancy1's in-game hint system.

Changed paths:
  A engines/nancy/action/telephone.cpp
  A engines/nancy/action/telephone.h
    engines/nancy/action/arfactory_v1.cpp
    engines/nancy/action/primaryvideo.cpp
    engines/nancy/action/primaryvideo.h
    engines/nancy/action/recordtypes.cpp
    engines/nancy/action/recordtypes.h
    engines/nancy/action/responses.cpp
    engines/nancy/module.mk


diff --git a/engines/nancy/action/arfactory_v1.cpp b/engines/nancy/action/arfactory_v1.cpp
index 8e316f2f0f..f925890c34 100644
--- a/engines/nancy/action/arfactory_v1.cpp
+++ b/engines/nancy/action/arfactory_v1.cpp
@@ -28,6 +28,7 @@
 #include "engines/nancy/action/staticbitmapanim.h"
 #include "engines/nancy/action/orderingpuzzle.h"
 #include "engines/nancy/action/rotatinglockpuzzle.h"
+#include "engines/nancy/action/telephone.h"
 
 #include "engines/nancy/state/scene.h"
 
@@ -119,7 +120,7 @@ ActionRecord *ActionManager::createActionRecord(uint16 type) {
         case 0x69:
             return new LeverPuzzle();
         case 0x6A:
-            return new Telephone();
+            return new Telephone(_engine->scene->getViewport());
         case 0x6B:
             return new SliderPuzzle();
         case 0x6C:
diff --git a/engines/nancy/action/primaryvideo.cpp b/engines/nancy/action/primaryvideo.cpp
index 6c06368418..15d0b9db48 100644
--- a/engines/nancy/action/primaryvideo.cpp
+++ b/engines/nancy/action/primaryvideo.cpp
@@ -90,8 +90,8 @@ uint16 PlayPrimaryVideoChan0::readData(Common::SeekableReadStream &stream) {
     delete[] rawText;
 
     sound.read(stream, SoundManager::SoundDescription::kNormal);
-
-    stream.skip(0x23);
+    responseGenericSound.read(stream, SoundManager::SoundDescription::kNormal);
+    stream.skip(1);
     conditionalResponseCharacterID = stream.readByte();
     goodbyeResponseCharacterID = stream.readByte();
     numSceneChanges = stream.readByte();
@@ -200,6 +200,7 @@ void PlayPrimaryVideoChan0::execute(NancyEngine *engine) {
             }
 
             if (!engine->sound->isSoundPlaying(sound.channelID)) {
+                _engine->sound->stopSound(sound.channelID);
                 if (responses.size() == 0) {
                     // NPC has finished talking with no responses available, auto-advance to next scene
                     state = kActionTrigger;
@@ -215,11 +216,10 @@ void PlayPrimaryVideoChan0::execute(NancyEngine *engine) {
                     if (pickedResponse != -1) {
                         // Player has picked response, play sound file and change state
                         sceneChange = responses[pickedResponse].sceneChange;
-                        SoundManager::SoundDescription responseSound = sound;
-                        responseSound.name = responses[pickedResponse].soundName;
+                        responseGenericSound.name = responses[pickedResponse].soundName;
                         // TODO this is probably not correct
-                        engine->sound->loadSound(responseSound);
-                        engine->sound->playSound(responseSound.channelID);
+                        engine->sound->loadSound(responseGenericSound);
+                        engine->sound->playSound(responseGenericSound.channelID);
                         state = kActionTrigger;
                     }
                 }
@@ -256,7 +256,8 @@ void PlayPrimaryVideoChan0::execute(NancyEngine *engine) {
                 engine->scene->setEventFlag(responses[pickedResponse].flagDesc.label, responses[pickedResponse].flagDesc.flag);
             }
 
-            if (!engine->sound->isSoundPlaying(sound.channelID)) {
+            if (!engine->sound->isSoundPlaying(responseGenericSound.channelID)) {
+                _engine->sound->stopSound(responseGenericSound.channelID);
                 if (shouldPopScene) {
                     // Exit dialogue
                     engine->scene->popScene();
diff --git a/engines/nancy/action/primaryvideo.h b/engines/nancy/action/primaryvideo.h
index c65c63ddce..aedc860380 100644
--- a/engines/nancy/action/primaryvideo.h
+++ b/engines/nancy/action/primaryvideo.h
@@ -82,6 +82,7 @@ public:
     Common::String text; // 0x3D
 
     SoundManager::SoundDescription sound; // 0x619
+    SoundManager::SoundDescription responseGenericSound; // 0x63B
 
     byte conditionalResponseCharacterID; // 0x65E
     byte goodbyeResponseCharacterID; // 0x65F
diff --git a/engines/nancy/action/recordtypes.cpp b/engines/nancy/action/recordtypes.cpp
index 8d78a7df7c..58213325ae 100644
--- a/engines/nancy/action/recordtypes.cpp
+++ b/engines/nancy/action/recordtypes.cpp
@@ -33,6 +33,8 @@
 #include "engines/nancy/resource.h"
 #include "engines/nancy/util.h"
 
+#include "engines/nancy/action/responses.cpp"
+
 #include "common/str.h"
 
 namespace Nancy {
@@ -385,16 +387,6 @@ uint16 LeverPuzzle::readData(Common::SeekableReadStream &stream) {
     return readRaw(stream, 0x192); // TODO
 }
 
-uint16 Telephone::readData(Common::SeekableReadStream &stream) {
-    byte *rawData = new byte[0x2016];
-    stream.read(rawData, 0x48C);
-
-    int32 sizeNext = (int16)(rawData[0x48A]) * 235;
-    stream.read(rawData + 0x48C, sizeNext);
-    delete[] rawData;
-    return sizeNext + 0x48C;
-}
-
 uint16 SliderPuzzle::readData(Common::SeekableReadStream &stream) {
     return readRaw(stream, 0x544); // TODO
 }
@@ -534,7 +526,115 @@ uint16 PlaySoundMultiHS::readData(Common::SeekableReadStream &stream) {
 }
 
 uint16 HintSystem::readData(Common::SeekableReadStream &stream) {
-    return readRaw(stream, 0x23); // TODO
+    characterID = stream.readByte();
+    genericSound.read(stream, SoundManager::SoundDescription::kNormal);
+    return 0x23;
+}
+
+void HintSystem::execute(Nancy::NancyEngine *engine) {
+    switch (state) {
+        case kBegin:
+            if (engine->scene->getHintsRemaining() > 0) {
+                selectHint(engine);
+            } else {
+                getHint(0, engine->scene->getDifficulty());
+            }
+
+            engine->scene->getTextbox().clear();
+            engine->scene->getTextbox().addTextLine(text);
+
+            engine->sound->loadSound(genericSound);
+            engine->sound->playSound(genericSound.channelID);
+            state = kRun;
+            break;
+        case kRun:
+            if (!engine->sound->isSoundPlaying(genericSound.channelID)) {
+                engine->sound->stopSound(genericSound.channelID);
+                state = kActionTrigger;
+            } else {
+                break;
+            }
+            // fall through
+        case kActionTrigger:
+            engine->scene->useHint(hintID, hintWeight);
+            engine->scene->getTextbox().clear();
+
+            if (sceneChange.sceneID != 9999) {
+                engine->scene->changeScene(sceneChange.sceneID, sceneChange.frameID, sceneChange.verticalOffset, sceneChange.doNotStartSound);
+            }
+
+            isDone = true;
+            break;
+    }
+}
+
+void HintSystem::selectHint(Nancy::NancyEngine *engine) {
+    for (auto &hint : nancy1Hints) {
+        if (hint.characterID != characterID) {
+            continue;
+        }
+
+        bool satisfied = true;
+
+        for (auto &flag : hint.flagConditions) {
+            if (flag.label == -1) {
+                break;
+            }
+            if (!engine->scene->getEventFlag(flag.label, flag.flag)) {
+                satisfied = false;
+                break;
+            }
+        }
+
+        for (auto &inv : hint.inventoryCondition) {
+            if (inv.label == -1) {
+                break;
+            }
+            if (engine->scene->hasItem(inv.label) != inv.flag) {
+                satisfied = false;
+                break;
+            }
+        }
+
+        if (satisfied) {
+            getHint(hint.hintID, engine->scene->getDifficulty());
+            break;
+        }
+    }
+}
+
+void HintSystem::getHint(uint hint, uint difficulty) {
+    uint fileOffset;
+    if (characterID < 3) {
+        fileOffset = nancy1HintOffsets[characterID];
+    }
+
+    fileOffset += 0x288 * hint;
+
+    Common::File file;
+    file.open("game.exe");
+    file.seek(fileOffset);
+
+    hintID = file.readSint16LE();
+    hintWeight = file.readSint16LE();
+
+    file.seek(difficulty * 10, SEEK_CUR);
+
+    char soundName[10];
+    file.read(soundName, 10);
+    genericSound.name = soundName;
+
+    file.seek(-(difficulty * 10) - 10, SEEK_CUR);
+    file.seek(30 + difficulty * 200, SEEK_CUR);
+
+    char textBuf[200];
+    file.read(textBuf, 200);
+    text = textBuf;
+
+    file.seek(-(difficulty * 200) - 200, SEEK_CUR);
+    file.seek(600, SEEK_CUR);
+
+    sceneChange.readData(file);
 }
 
 }
diff --git a/engines/nancy/action/recordtypes.h b/engines/nancy/action/recordtypes.h
index f12fc24e88..e3517485ae 100644
--- a/engines/nancy/action/recordtypes.h
+++ b/engines/nancy/action/recordtypes.h
@@ -278,11 +278,6 @@ public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
 };
 
-class Telephone : public ActionRecord {
-public:
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
-};
-
 class SliderPuzzle : public ActionRecord {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
@@ -359,6 +354,18 @@ public:
 class HintSystem : public ActionRecord {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void execute(Nancy::NancyEngine *engine) override;
+
+    byte characterID; // 0x00
+    SoundManager::SoundDescription genericSound; // 0x01
+
+    Common::String text;
+    SceneChangeDesc sceneChange;
+    uint16 hintID;
+    int16 hintWeight;
+
+    void selectHint(Nancy::NancyEngine *engine);
+    void getHint(uint hint, uint difficulty);
 };
 
 } // End of namespace Action
diff --git a/engines/nancy/action/responses.cpp b/engines/nancy/action/responses.cpp
index 3ac113a918..62969f0281 100644
--- a/engines/nancy/action/responses.cpp
+++ b/engines/nancy/action/responses.cpp
@@ -46,9 +46,17 @@ struct GoodbyeDesc {
     uint16 sceneIDs[4];
 };
 
+struct HintDesc {
+    byte characterID; // 0: Ned, 1: Bess, 2: George
+    byte hintID;
+    FlagDesc flagConditions[4];
+    FlagDesc inventoryCondition[2];
+};
+
 static const uint nancy1ResponseBaseFileOffset = 0xB1FE0; // TODO there could be more than one version of the exe
+static const uint nancy1HintOffsets[] = { 0xABB88, 0xAD760, 0xAF338 }; // Ned, Bess, George
 
-#define EMPTY_DESC {-1, kFalse }
+#define EMPTY_DESC { -1, kFalse }
 
 static const GoodbyeDesc nancy1Goodbyes[] = {
     // Daryl
@@ -631,5 +639,316 @@ static const ConditionalResponseDesc nancy1ConditionalResponses[] = {
     }
 };
 
+static const HintDesc nancy1Hints[] {
+    // Ned
+    {
+        0,
+        1,
+        {
+            { 0, kFalse },
+            EMPTY_DESC
+        },
+        {
+            EMPTY_DESC
+        }
+    },
+
+    {
+        0,
+        2,
+        {
+            { 0, kTrue },
+            { 1, kFalse },
+            EMPTY_DESC
+        },
+        {
+            EMPTY_DESC
+        }
+    },
+
+    {
+        0,
+        3,
+        {
+            { 1, kFalse },
+            EMPTY_DESC
+        },
+        {
+            { 3, kFalse },
+            EMPTY_DESC
+        }
+    },
+
+    {
+        0,
+        4,
+        {
+            { 0x55, kFalse },
+            EMPTY_DESC
+        },
+        {
+            { 3, kTrue },
+            EMPTY_DESC
+        }
+    },
+
+    {
+        0,
+        5,
+        {
+            { 0x55, kTrue },
+            { 0x56, kFalse },
+            EMPTY_DESC
+        },
+        {
+            EMPTY_DESC
+        }
+    },
+
+    {
+        0,
+        6,
+        {
+            { 0x57, kFalse },
+            { 0x56, kTrue },
+            EMPTY_DESC
+        },
+        {
+            EMPTY_DESC
+        }
+    },
+
+    {
+        0,
+        8,
+        {
+            { 0xA, kTrue },
+            { 0x3B, kTrue },
+            EMPTY_DESC
+        },
+        {
+            { 7, kFalse },
+            EMPTY_DESC
+        }
+    },
+
+    // Bess
+    {
+        1,
+        1,
+        {
+            { 0x57, kFalse },
+            EMPTY_DESC
+        },
+        {
+            EMPTY_DESC
+        }
+    },
+
+    {
+        1,
+        2,
+        {
+            { 0x57, kTrue },
+            { 0x3C, kFalse },
+            EMPTY_DESC
+        },
+        {
+            EMPTY_DESC
+        }
+    },
+
+    {
+        1,
+        3,
+        {
+            { 0x5A, kFalse },
+            { 0x3C, kTrue },
+            { 0x56, kFalse },
+            EMPTY_DESC
+        },
+        {
+            EMPTY_DESC
+        }
+    },
+
+    {
+        1,
+        4,
+        {
+            { 0x5A, kTrue },
+            { 0x56, kFalse },
+            EMPTY_DESC
+        },
+        {
+            EMPTY_DESC
+        }
+    },
+
+    {
+        1,
+        6,
+        {
+            { 0x5A, kFalse },
+            { 0x3C, kTrue },
+            { 0x56, kTrue },
+            EMPTY_DESC
+        },
+        {
+            EMPTY_DESC
+        }
+    },
+
+    {
+        1,
+        7,
+        {
+            { 0x59, kTrue },
+            { 0xA, kFalse },
+            EMPTY_DESC
+        },
+        {
+            { 0, kFalse },
+            EMPTY_DESC
+        }
+    },
+
+    {
+        1,
+        8,
+        {
+            { 0xA, kTrue },
+            { 0x3B, kTrue },
+            EMPTY_DESC
+        },
+        {
+            { 0, kTrue },
+            { 7, kFalse }
+        }
+    },
+
+    {
+        1,
+        9,
+        {
+            { 0x59, kFalse },
+            { 0xA, kTrue },
+            { 0x3B, kTrue },
+            EMPTY_DESC
+        },
+        {
+            { 7, kFalse },
+            EMPTY_DESC
+        }
+    },
+
+    // George
+    {
+        2,
+        0xA,
+        {
+            { 0x4A, kTrue },
+            EMPTY_DESC
+        },
+        EMPTY_DESC
+    },
+
+    {
+        2,
+        1,
+        {
+            { 0x5B, kFalse },
+            EMPTY_DESC
+        },
+        EMPTY_DESC
+    },
+
+    {
+        2,
+        2,
+        {
+            { 0x5B, kTrue },
+            EMPTY_DESC
+        },
+        {
+            { 9, kFalse },
+            EMPTY_DESC
+        }
+    },
+
+    {
+        2,
+        3,
+        {
+            { 0x5B, kTrue },
+            { 0x5C, kFalse },
+            { 0x5D, kFalse },
+            EMPTY_DESC
+        },
+        {
+            { 9, kTrue },
+            EMPTY_DESC
+        }
+    },
+
+    {
+        2,
+        4,
+        {
+            { 0x5B, kTrue },
+            { 0x5C, kTrue },
+            { 0x5D, kFalse },
+            EMPTY_DESC
+        },
+        {
+            { 9, kFalse },
+            EMPTY_DESC
+        }
+    },
+
+    {
+        2,
+        5,
+        {
+            { 0x5B, kTrue },
+            { 0x5C, kTrue },
+            { 0x5D, kTrue },
+            { 0x3B, kFalse }
+        },
+        {
+            { 9, kTrue },
+            EMPTY_DESC
+        }
+    },
+
+    {
+        2,
+        6,
+        {
+            { 0xA, kFalse },
+            { 0x3B, kTrue },
+            EMPTY_DESC
+        },
+        {
+            { 9, kTrue },
+            EMPTY_DESC
+        }
+    },
+
+    {
+        2,
+        7,
+        {
+            { 0x3B, kTrue },
+            { 0xA, kTrue },
+            EMPTY_DESC
+        },
+        {
+            { 7, kFalse },
+            EMPTY_DESC
+        }
+    }
+};
+
 }// End of namespace Action
 } // End of namespace Nancy::Action
diff --git a/engines/nancy/action/telephone.cpp b/engines/nancy/action/telephone.cpp
new file mode 100644
index 0000000000..fd477a9e0e
--- /dev/null
+++ b/engines/nancy/action/telephone.cpp
@@ -0,0 +1,324 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/nancy/action/telephone.h"
+
+#include "engines/nancy/util.h"
+#include "engines/nancy/nancy.h"
+#include "engines/nancy/graphics.h"
+#include "engines/nancy/resource.h"
+#include "engines/nancy/sound.h"
+#include "engines/nancy/cursor.h"
+
+#include "engines/nancy/state/scene.h"
+
+#include "engines/nancy/ui/textbox.h"
+
+namespace Nancy {
+namespace Action {
+
+void Telephone::init() {
+    _drawSurface.create(_screenPosition.width(), _screenPosition.height(), GraphicsManager::pixelFormat);
+    _drawSurface.clear(GraphicsManager::transColor);
+
+    Graphics::Surface surf;
+    _engine->_res->loadImage("ciftree", imageName, surf);
+    image.create(surf.w, surf.h, surf.format);
+    image.blitFrom(surf);
+    surf.free();
+}
+
+uint16 Telephone::readData(Common::SeekableReadStream &stream) {
+    char buf[10];
+    stream.read(buf, 10);
+    imageName = buf;
+
+    for (uint i = 0; i < 12; ++i) {
+        srcRects.push_back(Common::Rect());
+        readRect(stream, srcRects.back());
+    }
+
+    for (uint i = 0; i < 12; ++i) {
+        destRects.push_back(Common::Rect());
+        readRect(stream, destRects.back());
+
+        if (i == 0) {
+            _screenPosition = destRects.back();
+        } else {
+            _screenPosition.extend(destRects.back());
+        }
+    }
+
+    genericDialogueSound.read(stream, SoundManager::SoundDescription::kNormal);
+    genericButtonSound.read(stream, SoundManager::SoundDescription::kNormal);
+    ringSound.read(stream, SoundManager::SoundDescription::kNormal);
+    dialToneSound.read(stream, SoundManager::SoundDescription::kNormal);
+    dialAgainSound.read(stream, SoundManager::SoundDescription::kNormal);
+    hangUpSound.read(stream, SoundManager::SoundDescription::kNormal);
+
+    for (uint i = 0; i < 12; ++i) {
+        stream.read(buf, 10);
+        buttonSoundNames.push_back(buf);
+    }
+
+    char buf2[200];
+    stream.read(buf2, 200);
+    addressBookString = buf2;
+    stream.read(buf2, 200);
+    dialAgainString = buf2;
+    reloadScene.readData(stream);
+    stream.skip(2);
+    flagOnReload.label = stream.readUint16LE();
+    flagOnReload.flag = (NancyFlag)stream.readUint16LE();
+    exitScene.readData(stream);
+    stream.skip(2);
+    flagOnExit.label = stream.readUint16LE();
+    flagOnExit.flag = (NancyFlag)stream.readUint16LE();
+    readRect(stream, exitHotspot);
+
+    uint numCalls = stream.readUint16LE();
+
+    for (uint i = 0; i < numCalls; ++i) {
+        calls.push_back(PhoneCall());
+        PhoneCall &call = calls.back();
+        for (uint j = 0; j < 11; ++j) {
+            call.phoneNumber.push_back(stream.readByte());
+        }
+        
+        stream.read(buf, 10);
+        call.soundName = buf;
+        stream.read(buf2, 200);
+        call.text = buf2;
+        call.sceneChange.readData(stream);
+        stream.skip(2);
+        call.flag.label = stream.readUint16LE();
+        call.flag.flag = (NancyFlag)stream.readUint16LE();
+    }
+
+    return numCalls * 0xEB + 0x48C;
+}
+
+void Telephone::execute(NancyEngine *engine) {
+    switch (state) {
+        case kBegin:
+            init();
+            registerGraphics();
+            _engine->sound->loadSound(dialToneSound);
+            _engine->sound->playSound(dialToneSound.channelID);
+            _engine->scene->getTextbox().clear();
+            _engine->scene->getTextbox().addTextLine(addressBookString);
+            state = kRun;
+            // fall through
+        case kRun:
+            switch (callState) {
+                case kWaiting:
+                    // Long phone numbers start with 1
+                    if (calledNumber.size() >= 11 || (calledNumber.size() >= 7 && (calledNumber[0] != 1))) {
+                        _engine->scene->getTextbox().clear();
+                        _engine->scene->getTextbox().addTextLine("ringing...<n><e>"); // Hardcoded in the original engine
+                        _engine->sound->loadSound(ringSound);
+                        _engine->sound->playSound(ringSound.channelID);
+                        callState = kRinging;
+                    }
+                    break;
+                case kButtonPress:
+                    if (!_engine->sound->isSoundPlaying(genericButtonSound.channelID)) {
+                        _engine->sound->stopSound(genericButtonSound.channelID);
+                        undrawButton(selected);
+                        callState = kWaiting;
+                    }
+
+                    break;
+                case kRinging:
+                    if (!_engine->sound->isSoundPlaying(ringSound.channelID)) {
+                        _engine->sound->stopSound(ringSound.channelID);
+
+                        uint numberLength = calledNumber[0] == 1 ? 11 : 7;
+                        for (uint i = 0; i < calls.size(); ++i) {
+                            bool invalid = false;
+                            for (uint j = 0; j < numberLength; ++j) {
+                                if (calledNumber[j] != calls[i].phoneNumber[j]) {
+                                    // Invalid number, move onto next
+                                    invalid = true;
+                                    break;
+                                }
+                            }
+
+                            if (invalid) {
+                                continue;
+                            }
+
+                            _engine->scene->getTextbox().clear();
+                            _engine->scene->getTextbox().addTextLine(calls[i].text);
+
+                            genericDialogueSound.name = calls[i].soundName;
+                            _engine->sound->loadSound(genericDialogueSound);
+                            _engine->sound->playSound(genericDialogueSound.channelID);
+                            selected = i;
+                            callState = kCall;
+
+                            return;
+                        }
+                        
+                        _engine->scene->getTextbox().clear();
+                        _engine->scene->getTextbox().addTextLine(dialAgainString);
+
+                        _engine->sound->loadSound(dialAgainSound);
+                        _engine->sound->playSound(dialAgainSound.channelID);
+                        callState = kBadNumber;
+                        return;
+                    }
+
+                    break;
+                case kBadNumber:
+                    if (!_engine->sound->isSoundPlaying(dialAgainSound.channelID)) {
+                        _engine->sound->stopSound(dialAgainSound.channelID);
+
+                        state = kActionTrigger;
+                    }
+
+                    break;
+                case kCall:
+                    if (!_engine->sound->isSoundPlaying(genericDialogueSound.channelID)) {
+                        _engine->sound->stopSound(genericDialogueSound.channelID);
+
+                        state = kActionTrigger;
+                    }
+
+                    break;
+                case kHangUp:
+                    if (!_engine->sound->isSoundPlaying(hangUpSound.channelID)) {
+                        _engine->sound->stopSound(hangUpSound.channelID);
+
+                        state = kActionTrigger;
+                    }
+
+                    break;
+            }
+
+            break;
+        case kActionTrigger:
+            switch (callState) {
+                case kBadNumber:
+                    if (reloadScene.sceneID != 9999) {
+                        engine->scene->changeScene(reloadScene.sceneID, reloadScene.frameID, reloadScene.verticalOffset, reloadScene.doNotStartSound);
+                    }
+
+                    calledNumber.clear();
+                    _engine->scene->setEventFlag(flagOnReload.label, flagOnReload.flag);
+                    state = kRun;
+                    callState = kWaiting;
+
+                    break;
+                case kCall: {
+                    PhoneCall &call = calls[selected];
+
+                    if (call.sceneChange.sceneID != 9999) {
+                        _engine->scene->changeScene(call.sceneChange.sceneID, call.sceneChange.frameID, call.sceneChange.verticalOffset, call.sceneChange.doNotStartSound);
+                    }
+
+                    _engine->scene->setEventFlag(call.flag.label, call.flag.flag);
+
+                    break;
+                }
+                case kHangUp:
+                    if (exitScene.sceneID != 9999) {
+                        _engine->scene->changeScene(exitScene.sceneID, exitScene.frameID, exitScene.verticalOffset, exitScene.doNotStartSound);
+                    }
+
+                    _engine->scene->setEventFlag(flagOnExit.label, flagOnExit.flag);
+                    
+                    break;
+                default:
+                    break;
+
+            }
+
+            isDone = true;
+            _engine->scene->getTextbox().clear();
+    }
+}
+
+void Telephone::handleInput(NancyInput &input) {
+    int buttonNr = -1;
+    // Cursor gets changed regardless of state
+    for (uint i = 0; i < 12; ++i) {
+        if (_engine->scene->getViewport().convertViewportToScreen(destRects[i]).contains(input.mousePos)) {
+            _engine->cursorManager->setCursorType(CursorManager::kHotspot);
+            buttonNr = i;
+            break;
+        }
+    }
+
+    if (callState != kWaiting) {
+        return;
+    }
+
+    if (_engine->scene->getViewport().convertViewportToScreen(exitHotspot).contains(input.mousePos)) {
+        _engine->cursorManager->setCursorType(CursorManager::kExitArrow);
+
+        if (input.input & NancyInput::kLeftMouseButtonUp) {
+            _engine->sound->loadSound(hangUpSound);
+            _engine->sound->playSound(hangUpSound.channelID);
+
+            callState = kHangUp;
+        }
+        return;
+    }
+
+    if (buttonNr != -1) {
+        if (input.input & NancyInput::kLeftMouseButtonUp) {
+            if (_engine->sound->isSoundPlaying(dialToneSound.channelID)) {
+                _engine->sound->stopSound(dialToneSound.channelID);
+            }
+
+            calledNumber.push_back(buttonNr);
+            genericButtonSound.name = buttonSoundNames[buttonNr];
+            _engine->sound->loadSound(genericButtonSound);
+            _engine->sound->playSound(genericButtonSound.channelID);
+
+            drawButton(buttonNr);
+
+            selected = buttonNr;
+
+            callState = kButtonPress;
+        }
+    }
+}
+
+void Telephone::drawButton(uint id) {
+    Common::Point destPoint(destRects[id].left - _screenPosition.left, destRects[id].top - _screenPosition.top);
+    _drawSurface.blitFrom(image, srcRects[id], destPoint);
+
+    _needsRedraw = true;
+}
+
+void Telephone::undrawButton(uint id) {
+    Common::Rect bounds = destRects[id];
+    bounds.translate(-_screenPosition.left, -_screenPosition.top);
+
+    _drawSurface.fillRect(bounds, GraphicsManager::transColor);
+    _needsRedraw = true;
+}
+
+} // End of namespace Action
+} // End of namespace Nancy
\ No newline at end of file
diff --git a/engines/nancy/action/telephone.h b/engines/nancy/action/telephone.h
new file mode 100644
index 0000000000..29dd03077e
--- /dev/null
+++ b/engines/nancy/action/telephone.h
@@ -0,0 +1,102 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef NANCY_ACTION_TELEPHONE_H
+#define NANCY_ACTION_TELEPHONE_H
+
+#include "engines/nancy/action/recordtypes.h"
+#include "engines/nancy/renderobject.h"
+
+#include "engines/nancy/sound.h"
+
+#include "common/str.h"
+#include "common/array.h"
+#include "common/rect.h"
+
+#include "graphics/managed_surface.h"
+
+namespace Nancy {
+namespace Action {
+
+class Telephone : public ActionRecord, public RenderObject {
+public:
+    struct PhoneCall {
+        Common::Array<byte> phoneNumber; // 0x0, 11 bytes
+        Common::String soundName; // 0xB
+        Common::String text; // 0x15, 0xC8 bytes
+        SceneChangeDesc sceneChange; // 0xDD
+        // shouldStopRendering
+        FlagDesc flag; // 0xE7
+    };
+
+    enum CallState { kWaiting, kButtonPress, kRinging, kBadNumber, kCall, kHangUp };
+
+    Telephone(RenderObject &redrawFrom) :
+        RenderObject(redrawFrom),
+        callState(kWaiting),
+        selected(0) {}
+    virtual ~Telephone() {}
+
+    virtual void init() override;
+
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void execute(NancyEngine *engine) override;
+    virtual void handleInput(NancyInput &input) override;
+
+    Common::String imageName; // 0x00
+    Common::Array<Common::Rect> srcRects; // 0xA, 12
+    Common::Array<Common::Rect> destRects; // 0xCA, 12
+    SoundManager::SoundDescription genericDialogueSound; // 0x18A
+    SoundManager::SoundDescription genericButtonSound; // 0x1AC
+    SoundManager::SoundDescription ringSound; // 0x1CE
+    SoundManager::SoundDescription dialToneSound; // 0x1F0
+    SoundManager::SoundDescription dialAgainSound; // 0x212
+    SoundManager::SoundDescription hangUpSound; // 0x234
+    Common::Array<Common::String> buttonSoundNames; // 0x256, 12 * 0xA
+    Common::String addressBookString; // 0x2CE, 0xC8 long
+    Common::String dialAgainString; // 0x396
+    SceneChangeDesc reloadScene; // 0x45E
+    FlagDesc flagOnReload; // 0x468 ??
+    SceneChangeDesc exitScene; // 0x46C
+    FlagDesc flagOnExit; // 0x476
+    Common::Rect exitHotspot; // 0x47A
+    // 0x48A numConvos
+    Common::Array<PhoneCall> calls; // 0x48C
+
+    Common::Array<byte> calledNumber;
+    Graphics::ManagedSurface image;
+    CallState callState;
+    uint selected;
+
+protected:
+    virtual uint16 getZOrder() const override { return 7; }
+    virtual BlitType getBlitType() const override { return kTrans; }
+    virtual bool isViewportRelative() const override { return true; }
+
+    void drawButton(uint id);
+    void undrawButton(uint id);
+};
+
+} // End of namespace Action
+} // End of namespace Nancy
+
+#endif // NANCY_ACTION_TELEPHONE_H
\ No newline at end of file
diff --git a/engines/nancy/module.mk b/engines/nancy/module.mk
index 546936e80b..8d3eeca345 100644
--- a/engines/nancy/module.mk
+++ b/engines/nancy/module.mk
@@ -9,6 +9,7 @@ MODULE_OBJS = \
   action/rotatinglockpuzzle.o \
   action/secondaryvideo.o \
   action/staticbitmapanim.o \
+  action/telephone.o \
   ui/frame.o \
   ui/inventorybox.o \
   ui/scrollbar.o \


Commit: 1cd406b5af9cb2e020a18880ba3ac527c83da956
    https://github.com/scummvm/scummvm/commit/1cd406b5af9cb2e020a18880ba3ac527c83da956
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Implement slider puzzle

Implemented the SliderPuzzle action record, which is used for the jewel box in the safe in nancy1.

Changed paths:
  A engines/nancy/action/sliderpuzzle.cpp
  A engines/nancy/action/sliderpuzzle.h
    engines/nancy/action/arfactory_v1.cpp
    engines/nancy/action/recordtypes.cpp
    engines/nancy/action/recordtypes.h
    engines/nancy/module.mk
    engines/nancy/util.h


diff --git a/engines/nancy/action/arfactory_v1.cpp b/engines/nancy/action/arfactory_v1.cpp
index f925890c34..63442e265a 100644
--- a/engines/nancy/action/arfactory_v1.cpp
+++ b/engines/nancy/action/arfactory_v1.cpp
@@ -29,6 +29,7 @@
 #include "engines/nancy/action/orderingpuzzle.h"
 #include "engines/nancy/action/rotatinglockpuzzle.h"
 #include "engines/nancy/action/telephone.h"
+#include "engines/nancy/action/sliderpuzzle.h"
 
 #include "engines/nancy/state/scene.h"
 
@@ -122,7 +123,7 @@ ActionRecord *ActionManager::createActionRecord(uint16 type) {
         case 0x6A:
             return new Telephone(_engine->scene->getViewport());
         case 0x6B:
-            return new SliderPuzzle();
+            return new SliderPuzzle(_engine->scene->getViewport());
         case 0x6C:
             return new PasswordPuzzle();
         case 0x6E:
diff --git a/engines/nancy/action/recordtypes.cpp b/engines/nancy/action/recordtypes.cpp
index 58213325ae..3eb85bf321 100644
--- a/engines/nancy/action/recordtypes.cpp
+++ b/engines/nancy/action/recordtypes.cpp
@@ -387,10 +387,6 @@ uint16 LeverPuzzle::readData(Common::SeekableReadStream &stream) {
     return readRaw(stream, 0x192); // TODO
 }
 
-uint16 SliderPuzzle::readData(Common::SeekableReadStream &stream) {
-    return readRaw(stream, 0x544); // TODO
-}
-
 uint16 PasswordPuzzle::readData(Common::SeekableReadStream &stream) {
     return readRaw(stream, 0xD7); // TODO
 }
diff --git a/engines/nancy/action/recordtypes.h b/engines/nancy/action/recordtypes.h
index e3517485ae..ac4d821a8c 100644
--- a/engines/nancy/action/recordtypes.h
+++ b/engines/nancy/action/recordtypes.h
@@ -278,11 +278,6 @@ public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
 };
 
-class SliderPuzzle : public ActionRecord {
-public:
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
-};
-
 class PasswordPuzzle : public ActionRecord {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
diff --git a/engines/nancy/action/sliderpuzzle.cpp b/engines/nancy/action/sliderpuzzle.cpp
new file mode 100644
index 0000000000..e054df242a
--- /dev/null
+++ b/engines/nancy/action/sliderpuzzle.cpp
@@ -0,0 +1,308 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/nancy/action/sliderpuzzle.h"
+
+#include "engines/nancy/util.h"
+#include "engines/nancy/nancy.h"
+#include "engines/nancy/graphics.h"
+#include "engines/nancy/resource.h"
+#include "engines/nancy/sound.h"
+#include "engines/nancy/state/scene.h"
+
+namespace Nancy {
+namespace Action {
+
+Common::Array<Common::Array<int16>> SliderPuzzle::playerTileOrder = Common::Array<Common::Array<int16>>();
+bool SliderPuzzle::playerHasTriedPuzzle = false;
+
+void SliderPuzzle::init() {
+    _drawSurface.create(_screenPosition.width(), _screenPosition.height(), GraphicsManager::pixelFormat);
+    _drawSurface.clear(GraphicsManager::transColor);
+
+    Graphics::Surface surf;
+    _engine->_res->loadImage("ciftree", imageName, surf);
+    image.create(surf.w, surf.h, surf.format);
+    image.blitFrom(surf);
+    surf.free();
+}
+
+uint16 SliderPuzzle::readData(Common::SeekableReadStream &stream) {
+    char buf[10];
+    stream.read(buf, 10);
+    imageName = buf;
+
+    width = stream.readUint16LE();
+    height = stream.readUint16LE();
+
+    for (uint y = 0; y < height; ++y) {
+        srcRects.push_back(Common::Array<Common::Rect>());
+        for (uint x = 0; x < width; ++x) {
+            srcRects.back().push_back(Common::Rect());
+            readRect(stream, srcRects.back().back());
+        }
+        stream.skip((6 - width) * 16);
+    }
+
+    stream.skip((6 - height) * 6 * 16);
+
+    for (uint y = 0; y < height; ++y) {
+        destRects.push_back(Common::Array<Common::Rect>());
+        for (uint x = 0; x < width; ++x) {
+            destRects.back().push_back(Common::Rect());
+            readRect(stream, destRects.back().back());
+
+            if (x == 0 && y == 0) {
+                _screenPosition = destRects.back().back();
+            } else {
+                _screenPosition.extend(destRects.back().back());
+            }
+        }
+        stream.skip((6 - width) * 16);
+    }
+
+    stream.skip((6 - height) * 6 * 16);
+
+    for (uint y = 0; y < height; ++y) {
+        correctTileOrder.push_back(Common::Array<int16>());
+        for (uint x = 0; x < width; ++x) {
+            correctTileOrder.back().push_back(stream.readSint16LE());
+        }
+        stream.skip((6 - width) * 2);
+    }
+
+    stream.skip((6 - height) * 6 * 2);
+
+    clickSound.read(stream, SoundManager::SoundDescription::kNormal);
+    solveExitScene.readData(stream);
+    stream.skip(2);
+    flagOnSolve.label = stream.readUint16LE();
+    flagOnSolve.flag = (NancyFlag)stream.readByte();
+    solveSound.read(stream, SoundManager::SoundDescription::kNormal);
+    exitScene.readData(stream);
+    stream.skip(2);
+    flagOnExit.label = stream.readUint16LE();
+    flagOnExit.flag = (NancyFlag)stream.readByte();
+    readRect(stream, exitHotspot);
+
+    return 0x544;
+}
+
+void SliderPuzzle::execute(Nancy::NancyEngine *engine) {
+    switch (state) {
+        case kBegin:
+            init();
+            registerGraphics();
+            if (!playerHasTriedPuzzle) {
+                Common::SeekableReadStream *spuz = engine->getBootChunkStream("SPUZ");
+                playerTileOrder.clear();
+                spuz->seek(engine->scene->getDifficulty() * 0x48);
+                for (uint y = 0; y < height; ++y) {
+                    playerTileOrder.push_back(Common::Array<int16>());
+                    for (uint x = 0; x < width; ++x) {
+                        playerTileOrder.back().push_back(spuz->readSint16LE());
+                    }
+                    spuz->skip((6 - width) * 2);
+                }
+                playerHasTriedPuzzle = true;
+            }
+
+            for (uint y = 0; y < height; ++y) {
+                for (uint x = 0; x < width; ++x) {
+                    if (!srcRects[y][x].isEmpty()) {
+                        drawTile(playerTileOrder[y][x], x, y);
+                    }
+                }
+            }
+
+            engine->sound->loadSound(clickSound);
+            state = kRun;
+            // fall through
+        case kRun:
+            switch (solveState) {
+                case kNotSolved:
+                    for (uint y = 0; y < height; ++y) {
+                        for (uint x = 0; x < width; ++x) {
+                            if (playerTileOrder[y][x] != correctTileOrder[y][x]) {
+                                return;
+                            }
+                        }
+                    }
+
+                    engine->sound->loadSound(solveSound);
+                    engine->sound->playSound(solveSound.channelID);
+                    solveState = kWaitForSound;
+                    break;
+                case kWaitForSound:
+                    if (!engine->sound->isSoundPlaying(solveSound.channelID)) {
+                        engine->sound->stopSound(solveSound.channelID);
+                        state = kActionTrigger;
+                    }
+
+                    break;
+            }
+
+            break;
+        case kActionTrigger:
+            switch (solveState) {
+                case kNotSolved:
+                    if (exitScene.sceneID != 9999) {
+                        engine->scene->changeScene(exitScene.sceneID, exitScene.frameID, exitScene.verticalOffset, exitScene.doNotStartSound);
+                    }
+
+                    engine->scene->setEventFlag(flagOnExit.label, flagOnExit.flag);
+                    break;
+                case kWaitForSound:
+                    if (exitScene.sceneID != 9999) {
+                        engine->scene->changeScene(solveExitScene.sceneID, solveExitScene.frameID, solveExitScene.verticalOffset, solveExitScene.doNotStartSound);
+                    }
+
+                    engine->scene->setEventFlag(flagOnSolve.label, flagOnSolve.flag);
+                    playerHasTriedPuzzle = false;
+                    break;
+            }
+
+            engine->sound->stopSound(clickSound.channelID);
+            isDone = true;
+    }
+}
+
+void SliderPuzzle::handleInput(NancyInput &input) {
+    if (solveState != kNotSolved) {
+        return;
+    }
+
+    if (_engine->scene->getViewport().convertViewportToScreen(exitHotspot).contains(input.mousePos)) {
+        _engine->cursorManager->setCursorType(CursorManager::kExitArrow);
+
+        if (input.input & NancyInput::kLeftMouseButtonUp) {
+            state = kActionTrigger;
+        }
+        return;
+    }
+
+    int currentTileX = -1;
+    int currentTileY = -1;
+    uint direction = 0;
+    for (uint y = 0; y < height; ++y) {
+        bool shouldBreak = false;
+        for (uint x = 0; x < width; ++x) {
+            if (x > 0 && playerTileOrder[y][x - 1] < 0) {
+                if (_engine->scene->getViewport().convertViewportToScreen(destRects[y][x]).contains(input.mousePos)) {
+                    currentTileX = x;
+                    currentTileY = y;
+                    direction = kLeft;
+                    shouldBreak = true;
+                    break;
+                }
+            } else if ((int)x < width - 1 && playerTileOrder[y][x + 1] < 0) {
+                if (_engine->scene->getViewport().convertViewportToScreen(destRects[y][x]).contains(input.mousePos)) {
+                    currentTileX = x;
+                    currentTileY = y;
+                    direction = kRight;
+                    shouldBreak = true;
+                    break;
+                }
+            } else if (y > 0 && playerTileOrder[y - 1][x] < 0) {
+                if (_engine->scene->getViewport().convertViewportToScreen(destRects[y][x]).contains(input.mousePos)) {
+                    currentTileX = x;
+                    currentTileY = y;
+                    direction = kUp;
+                    shouldBreak = true;
+                    break;
+                }
+            } else if ((int)y < height - 1 && playerTileOrder[y + 1][x] < 0) {
+                if (_engine->scene->getViewport().convertViewportToScreen(destRects[y][x]).contains(input.mousePos)) {
+                    currentTileX = x;
+                    currentTileY = y;
+                    direction = kDown;
+                    shouldBreak = true;
+                    break;
+                }
+            }
+        }
+
+        if (shouldBreak) {
+            break;
+        }
+    }
+
+    if (currentTileX != -1) {
+        _engine->cursorManager->setCursorType(CursorManager::kHotspot);
+
+        if (input.input & NancyInput::kLeftMouseButtonUp) {
+            _engine->sound->playSound(clickSound.channelID);
+            switch (direction) {
+                case kUp: {
+                    uint curTileID = playerTileOrder[currentTileY][currentTileX];
+                    drawTile(curTileID, currentTileX, currentTileY - 1);
+                    undrawTile(currentTileX, currentTileY);
+                    playerTileOrder[currentTileY - 1][currentTileX] = curTileID;
+                    playerTileOrder[currentTileY][currentTileX] = -10;
+                    break;
+                }
+                case kDown: {
+                    uint curTileID = playerTileOrder[currentTileY][currentTileX];
+                    drawTile(curTileID, currentTileX, currentTileY + 1);
+                    undrawTile(currentTileX, currentTileY);
+                    playerTileOrder[currentTileY + 1][currentTileX] = curTileID;
+                    playerTileOrder[currentTileY][currentTileX] = -10;
+                    break;
+                }
+                case kLeft: {
+                    uint curTileID = playerTileOrder[currentTileY][currentTileX];
+                    drawTile(curTileID, currentTileX - 1, currentTileY);
+                    undrawTile(currentTileX, currentTileY);
+                    playerTileOrder[currentTileY][currentTileX - 1] = curTileID;
+                    playerTileOrder[currentTileY][currentTileX] = -10;
+                    break;
+                }
+                case kRight: {
+                    uint curTileID = playerTileOrder[currentTileY][currentTileX];
+                    drawTile(curTileID, currentTileX + 1, currentTileY);
+                    undrawTile(currentTileX, currentTileY);
+                    playerTileOrder[currentTileY][currentTileX + 1] = curTileID;
+                    playerTileOrder[currentTileY][currentTileX] = -10;
+                    break;
+                }
+            }
+        }
+    }
+}
+
+void SliderPuzzle::drawTile(uint tileID, uint posX, uint posY) {
+    Common::Point destPoint(destRects[posY][posX].left - _screenPosition.left, destRects[posY][posX].top - _screenPosition.top);
+    _drawSurface.blitFrom(image, srcRects[tileID / height][tileID % width], destPoint);
+
+    _needsRedraw = true;
+}
+
+void SliderPuzzle::undrawTile(uint posX, uint posY) {
+    Common::Rect bounds = destRects[posY][posX];
+    bounds.translate(-_screenPosition.left, -_screenPosition.top);
+    _drawSurface.fillRect(bounds, GraphicsManager::transColor);
+
+    _needsRedraw = true;
+}
+
+} // End of namespace Action
+} // End of namespace Nancy
diff --git a/engines/nancy/action/sliderpuzzle.h b/engines/nancy/action/sliderpuzzle.h
new file mode 100644
index 0000000000..2c36a0494d
--- /dev/null
+++ b/engines/nancy/action/sliderpuzzle.h
@@ -0,0 +1,84 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef NANCY_ACTION_SLIDERPUZZLE_H
+#define NANCY_ACTION_SLIDERPUZZLE_H
+
+#include "engines/nancy/action/recordtypes.h"
+#include "engines/nancy/renderobject.h"
+
+#include "engines/nancy/sound.h"
+
+#include "common/str.h"
+#include "common/array.h"
+#include "common/rect.h"
+
+#include "graphics/managed_surface.h"
+
+namespace Nancy {
+namespace Action {
+
+class SliderPuzzle: public ActionRecord, public RenderObject {
+public:
+    enum SolveState { kNotSolved, kWaitForSound };
+    SliderPuzzle(RenderObject &redrawFrom) : RenderObject(redrawFrom) {}
+    virtual ~SliderPuzzle() {}
+    
+    virtual void init() override;
+    
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void execute(Nancy::NancyEngine *engine) override;
+    virtual void handleInput(NancyInput &input) override;
+
+    Common::String imageName; // 0x00
+    uint16 width; // 0xA
+    uint16 height; // 0xC
+    Common::Array<Common::Array<Common::Rect>> srcRects; // 0x0E, size 0x240
+    Common::Array<Common::Array<Common::Rect>> destRects; // 0x24E, size 0x240
+    Common::Array<Common::Array<int16>> correctTileOrder; // 0x48E, size 0x48
+    SoundManager::SoundDescription clickSound; // 0x4D6
+    SceneChangeDesc solveExitScene; // 0x4F8
+    FlagDesc flagOnSolve; // 0x502
+    SoundManager::SoundDescription solveSound; // 0x505
+    SceneChangeDesc exitScene; // 0x527
+    FlagDesc flagOnExit; // 0x531
+    Common::Rect exitHotspot; // 0x534
+
+    SolveState solveState = kNotSolved;
+    Graphics::ManagedSurface image;
+
+    static Common::Array<Common::Array<int16>> playerTileOrder;
+    static bool playerHasTriedPuzzle;
+
+protected:
+    virtual uint16 getZOrder() const override { return 7; }
+    virtual BlitType getBlitType() const override { return kTrans; }
+    virtual bool isViewportRelative() const override { return true; }
+
+    void drawTile(uint tileID, uint posX, uint posY);
+    void undrawTile(uint posX, uint posY);
+};
+
+} // End of namespace Action
+} // End of namespace Nancy
+
+#endif // NANCY_ACTION_SLIDERPUZZLE_H
diff --git a/engines/nancy/module.mk b/engines/nancy/module.mk
index 8d3eeca345..205d0561ba 100644
--- a/engines/nancy/module.mk
+++ b/engines/nancy/module.mk
@@ -8,6 +8,7 @@ MODULE_OBJS = \
   action/recordtypes.o \
   action/rotatinglockpuzzle.o \
   action/secondaryvideo.o \
+  action/sliderpuzzle.o \
   action/staticbitmapanim.o \
   action/telephone.o \
   ui/frame.o \
diff --git a/engines/nancy/util.h b/engines/nancy/util.h
index 7f561718a5..871130ff97 100644
--- a/engines/nancy/util.h
+++ b/engines/nancy/util.h
@@ -28,10 +28,10 @@
 namespace Nancy {
 
 static void readRect(Common::SeekableReadStream &stream, Common::Rect &inRect) {
-    inRect.left = stream.readUint32LE();
-    inRect.top = stream.readUint32LE();
-    inRect.right = stream.readUint32LE();
-    inRect.bottom = stream.readUint32LE();
+    inRect.left = stream.readSint32LE();
+    inRect.top = stream.readSint32LE();
+    inRect.right = stream.readSint32LE();
+    inRect.bottom = stream.readSint32LE();
 }
 
 } // End of namespace Nancy


Commit: e0e18b0ebf212d0575967833a6270caca2611d5f
    https://github.com/scummvm/scummvm/commit/e0e18b0ebf212d0575967833a6270caca2611d5f
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Static bitmap animation fixes

The PlayIntStaticBitmapAnimation action record is now modeled after the original engine's implementation, and supports reverse playback and getting triggered by an event flag.

Changed paths:
    engines/nancy/action/staticbitmapanim.cpp
    engines/nancy/action/staticbitmapanim.h


diff --git a/engines/nancy/action/staticbitmapanim.cpp b/engines/nancy/action/staticbitmapanim.cpp
index b910b051e0..9de6894ec3 100644
--- a/engines/nancy/action/staticbitmapanim.cpp
+++ b/engines/nancy/action/staticbitmapanim.cpp
@@ -50,20 +50,24 @@ uint16 PlayIntStaticBitmapAnimation::readData(Common::SeekableReadStream &stream
     stream.read(name, 10);
     imageName = Common::String(name);
 
-    stream.skip(0xA);
+    stream.skip(0x2);
+    isTransparent = (NancyFlag)(stream.readUint16LE());
+    doNotChangeScene = (NancyFlag)(stream.readUint16LE());
+    isReverse = (NancyFlag)(stream.readUint16LE());
+    isLooping = (NancyFlag)(stream.readUint16LE());
     firstFrame = stream.readUint16LE();
-    stream.skip(2);
-    lastFrame = stream.readUint16LE();
+    loopFirstFrame = stream.readUint16LE();
+    loopLastFrame = stream.readUint16LE();
     frameTime = Common::Rational(1000, stream.readUint16LE()).toInt();
-    stream.skip(2);
-    soundFlagDesc.label = stream.readSint16LE();
-    soundFlagDesc.flag = (NancyFlag)stream.readUint16LE();
+    zOrder = stream.readUint16LE();
+    updateCondition.label = stream.readSint16LE();
+    updateCondition.flag = (NancyFlag)stream.readUint16LE();
     SceneChange::readData(stream);
     triggerFlags.readData(stream);
     sound.read(stream, SoundManager::SoundDescription::kNormal);
     uint numFrames = stream.readUint16LE();
 
-    for (uint i = firstFrame; i <= lastFrame; ++i) {
+    for (uint i = firstFrame; i <= loopLastFrame; ++i) {
         srcRects.push_back(Common::Rect());
         readRect(stream, srcRects[i]);
     }
@@ -76,11 +80,10 @@ uint16 PlayIntStaticBitmapAnimation::readData(Common::SeekableReadStream &stream
         readRect(stream, rects.dest);
     }
 
-    return 0x76 + numFrames * 0x22 + (lastFrame - firstFrame + 1) * 16;
+    return 0x76 + numFrames * 0x22 + (loopLastFrame - firstFrame + 1) * 16;
 }
 
 void PlayIntStaticBitmapAnimation::execute(NancyEngine *engine) {
-    // TODO handle sound, event flags
     uint32 currentFrameTime = engine->getTotalPlayTime();
     switch (state) {
         case kBegin:
@@ -91,32 +94,69 @@ void PlayIntStaticBitmapAnimation::execute(NancyEngine *engine) {
             state = kRun;
             // fall through
         case kRun: {
-            // Check if we've moved the viewport
-            uint16 newFrame = engine->scene->getSceneInfo().frameID;
-            if (currentViewportFrame != newFrame) {
-                currentViewportFrame = newFrame;
-                for (uint i = 0; i < bitmaps.size(); ++i) {
-                    if (currentViewportFrame == bitmaps[i].frameID) {
-                        nextFrameTime = 0;
-                        _screenPosition = bitmaps[i].dest;
-                        break;
-                    }
-                }
-            }
-
             // Check the timer to see if we need to draw the next animation frame
             if (nextFrameTime <= currentFrameTime) {
-                nextFrameTime = currentFrameTime + frameTime;
-                currentFrame = ++currentFrame > lastFrame ? firstFrame : currentFrame;
-                setFrame(currentFrame);
-            }
+                // World's worst if statement
+                if (engine->scene->getEventFlag(updateCondition.label, updateCondition.flag) ||
+                    (   (((currentFrame == loopLastFrame) && (isReverse == kFalse) && (isLooping == kFalse)) ||
+                        ((currentFrame == loopFirstFrame) && (isReverse == kTrue) && (isLooping == kFalse))) &&
+                            !engine->sound->isSoundPlaying(sound.channelID))   ) {
+                    
+                    state = kActionTrigger;
+
+                    // Not sure if hiding when triggered is a hack or the intended behavior, but it's here to fix
+                    // nancy1's safe lock light not turning off.
+                    setVisible(false);
+        
+                    if (!engine->sound->isSoundPlaying(sound.channelID)) {
+                        engine->sound->stopSound(sound.channelID);
+                    }
+                } else {
+                    // Check if we've moved the viewport
+                    uint16 newFrame = engine->scene->getSceneInfo().frameID;
+                    if (currentViewportFrame != newFrame) {
+                        currentViewportFrame = newFrame;
+                        for (uint i = 0; i < bitmaps.size(); ++i) {
+                            if (currentViewportFrame == bitmaps[i].frameID) {
+                                _screenPosition = bitmaps[i].dest;
+                                break;
+                            }
+                        }
+                    }
+                    
+                    nextFrameTime = currentFrameTime + frameTime;
+                    setFrame(currentFrame);
+                    if (isReverse == kTrue) {
+                        --currentFrame;
+                        currentFrame = currentFrame < loopFirstFrame ? loopLastFrame : currentFrame;
+                        return;
+                    } else {
+                        ++currentFrame;
+                        currentFrame = currentFrame > loopLastFrame ? loopFirstFrame : currentFrame;
+                        return;
+                    }
+                }                
+            } else {
+                // Check if we've moved the viewport
+                uint16 newFrame = engine->scene->getSceneInfo().frameID;
+                if (currentViewportFrame != newFrame) {
+                    currentViewportFrame = newFrame;
+                    for (uint i = 0; i < bitmaps.size(); ++i) {
+                        if (currentViewportFrame == bitmaps[i].frameID) {
+                            _screenPosition = bitmaps[i].dest;
+                            break;
+                        }
+                    }
+                }
+            }      
             
             break;
         }
         case kActionTrigger:
             triggerFlags.execute(engine);
-            SceneChange::execute(engine);
-            _engine->sound->stopSound(sound.channelID);
+            if (doNotChangeScene == kFalse) {
+                SceneChange::execute(engine);
+            }
             break;
     }
 }
diff --git a/engines/nancy/action/staticbitmapanim.h b/engines/nancy/action/staticbitmapanim.h
index bad0c93126..49c1bff434 100644
--- a/engines/nancy/action/staticbitmapanim.h
+++ b/engines/nancy/action/staticbitmapanim.h
@@ -41,19 +41,27 @@ public:
     PlayIntStaticBitmapAnimation(RenderObject &redrawFrom) : RenderObject(redrawFrom) {}
     virtual ~PlayIntStaticBitmapAnimation() { _fullSurface.free(); }
 
-    virtual void init()override;
+    virtual void init() override;
 
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
     virtual void execute(NancyEngine *engine) override;
 
     Common::String imageName;
-    uint16 firstFrame;
-    uint16 lastFrame;
-    FlagDesc soundFlagDesc;
-    EventFlagsDesc triggerFlags;
+
+    NancyFlag isTransparent; // 0xC
+    NancyFlag doNotChangeScene; // 0xE
+    NancyFlag isReverse; // 0x10
+    NancyFlag isLooping; // 0x12
+    uint16 firstFrame; // 0x14
+    uint16 loopFirstFrame; // 0x16
+    uint16 loopLastFrame; // 0x18
     Time frameTime;
+    uint16 zOrder; // 0x1C
+    FlagDesc updateCondition; // 0x1E
+    // SceneChange
+    EventFlagsDesc triggerFlags; // 0x2A
 
-    Nancy::SoundManager::SoundDescription sound;
+    Nancy::SoundManager::SoundDescription sound; // 0x52
 
     // Describes a single frame in this animation
     Common::Array<Common::Rect> srcRects;
@@ -66,8 +74,8 @@ public:
     Time nextFrameTime;
     
 protected:
-    virtual uint16 getZOrder() const override { return 7; }
-    virtual BlitType getBlitType() const override { return kNoTrans; }
+    virtual uint16 getZOrder() const override { return zOrder; }
+    virtual BlitType getBlitType() const override { return isTransparent == kTrue ? kTrans : kNoTrans; }
     virtual bool isViewportRelative() const override { return true; }
 
     void setFrame(uint frame);


Commit: cc7d69dd1053b3e222bf2e803f6b75212cb3b077
    https://github.com/scummvm/scummvm/commit/cc7d69dd1053b3e222bf2e803f6b75212cb3b077
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Fix wrong type reads for event flags

Fixed event flags' labels being read as unsigned ints when they should be read as signed instead.

Changed paths:
    engines/nancy/action/orderingpuzzle.cpp
    engines/nancy/action/rotatinglockpuzzle.cpp
    engines/nancy/action/sliderpuzzle.cpp
    engines/nancy/action/telephone.cpp


diff --git a/engines/nancy/action/orderingpuzzle.cpp b/engines/nancy/action/orderingpuzzle.cpp
index 1c0796c851..5331ca81f2 100644
--- a/engines/nancy/action/orderingpuzzle.cpp
+++ b/engines/nancy/action/orderingpuzzle.cpp
@@ -91,13 +91,13 @@ uint16 OrderingPuzzle::readData(Common::SeekableReadStream &stream) {
     clickSound.read(stream, SoundManager::SoundDescription::kNormal);
     solveExitScene.readData(stream);
     stream.skip(2); // shouldStopRendering, useless
-    flagOnSolve.label = stream.readUint16LE();
+    flagOnSolve.label = stream.readSint16LE();
     flagOnSolve.flag = (NancyFlag)stream.readByte();
     solveSoundDelay = stream.readUint16LE();
     solveSound.read(stream, SoundManager::SoundDescription::kNormal);
     exitScene.readData(stream);
     stream.skip(2); // shouldStopRendering, useless
-    flagOnExit.label = stream.readUint16LE();
+    flagOnExit.label = stream.readSint16LE();
     flagOnExit.flag = (NancyFlag)stream.readByte();
     readRect(stream, exitHotspot);
 
diff --git a/engines/nancy/action/rotatinglockpuzzle.cpp b/engines/nancy/action/rotatinglockpuzzle.cpp
index 72e28e09a9..12cdd20e79 100644
--- a/engines/nancy/action/rotatinglockpuzzle.cpp
+++ b/engines/nancy/action/rotatinglockpuzzle.cpp
@@ -92,13 +92,13 @@ uint16 RotatingLockPuzzle::readData(Common::SeekableReadStream &stream) {
     clickSound.read(stream, SoundManager::SoundDescription::kNormal);
     solveExitScene.readData(stream);
     stream.skip(2); // shouldStopRendering, useless
-    flagOnSolve.label = stream.readUint16LE();
+    flagOnSolve.label = stream.readSint16LE();
     flagOnSolve.flag = (NancyFlag)stream.readByte();
     solveSoundDelay = stream.readUint16LE();
     solveSound.read(stream, SoundManager::SoundDescription::kNormal);
     exitScene.readData(stream);
     stream.skip(2); // shouldStopRendering, useless
-    flagOnExit.label = stream.readUint16LE();
+    flagOnExit.label = stream.readSint16LE();
     flagOnExit.flag = (NancyFlag)stream.readByte();
     readRect(stream, exitHotspot);
 
diff --git a/engines/nancy/action/sliderpuzzle.cpp b/engines/nancy/action/sliderpuzzle.cpp
index e054df242a..69d2dd04e0 100644
--- a/engines/nancy/action/sliderpuzzle.cpp
+++ b/engines/nancy/action/sliderpuzzle.cpp
@@ -95,12 +95,12 @@ uint16 SliderPuzzle::readData(Common::SeekableReadStream &stream) {
     clickSound.read(stream, SoundManager::SoundDescription::kNormal);
     solveExitScene.readData(stream);
     stream.skip(2);
-    flagOnSolve.label = stream.readUint16LE();
+    flagOnSolve.label = stream.readSint16LE();
     flagOnSolve.flag = (NancyFlag)stream.readByte();
     solveSound.read(stream, SoundManager::SoundDescription::kNormal);
     exitScene.readData(stream);
     stream.skip(2);
-    flagOnExit.label = stream.readUint16LE();
+    flagOnExit.label = stream.readSint16LE();
     flagOnExit.flag = (NancyFlag)stream.readByte();
     readRect(stream, exitHotspot);
 
diff --git a/engines/nancy/action/telephone.cpp b/engines/nancy/action/telephone.cpp
index fd477a9e0e..a336d99931 100644
--- a/engines/nancy/action/telephone.cpp
+++ b/engines/nancy/action/telephone.cpp
@@ -87,11 +87,11 @@ uint16 Telephone::readData(Common::SeekableReadStream &stream) {
     dialAgainString = buf2;
     reloadScene.readData(stream);
     stream.skip(2);
-    flagOnReload.label = stream.readUint16LE();
+    flagOnReload.label = stream.readSint16LE();
     flagOnReload.flag = (NancyFlag)stream.readUint16LE();
     exitScene.readData(stream);
     stream.skip(2);
-    flagOnExit.label = stream.readUint16LE();
+    flagOnExit.label = stream.readSint16LE();
     flagOnExit.flag = (NancyFlag)stream.readUint16LE();
     readRect(stream, exitHotspot);
 
@@ -110,7 +110,7 @@ uint16 Telephone::readData(Common::SeekableReadStream &stream) {
         call.text = buf2;
         call.sceneChange.readData(stream);
         stream.skip(2);
-        call.flag.label = stream.readUint16LE();
+        call.flag.label = stream.readSint16LE();
         call.flag.flag = (NancyFlag)stream.readUint16LE();
     }
 


Commit: 628a4bd2b1cc91c5a2898041b0461d401e57f2f7
    https://github.com/scummvm/scummvm/commit/628a4bd2b1cc91c5a2898041b0461d401e57f2f7
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Implement password puzzle

Added support for the PasswordPuzzle action record, which is used in the computer in the teacher's lounge in nancy1. To enable typing the password in, also added support for all keyboard down events in the Input class.

Changed paths:
  A engines/nancy/action/passwordpuzzle.cpp
  A engines/nancy/action/passwordpuzzle.h
    engines/nancy/action/arfactory_v1.cpp
    engines/nancy/action/recordtypes.cpp
    engines/nancy/action/recordtypes.h
    engines/nancy/font.cpp
    engines/nancy/input.cpp
    engines/nancy/input.h
    engines/nancy/module.mk


diff --git a/engines/nancy/action/arfactory_v1.cpp b/engines/nancy/action/arfactory_v1.cpp
index 63442e265a..ccb058cef5 100644
--- a/engines/nancy/action/arfactory_v1.cpp
+++ b/engines/nancy/action/arfactory_v1.cpp
@@ -30,6 +30,7 @@
 #include "engines/nancy/action/rotatinglockpuzzle.h"
 #include "engines/nancy/action/telephone.h"
 #include "engines/nancy/action/sliderpuzzle.h"
+#include "engines/nancy/action/passwordpuzzle.h"
 
 #include "engines/nancy/state/scene.h"
 
@@ -125,7 +126,7 @@ ActionRecord *ActionManager::createActionRecord(uint16 type) {
         case 0x6B:
             return new SliderPuzzle(_engine->scene->getViewport());
         case 0x6C:
-            return new PasswordPuzzle();
+            return new PasswordPuzzle(_engine->scene->getViewport());
         case 0x6E:
             return new AddInventoryNoHS();
         case 0x6F:
diff --git a/engines/nancy/action/passwordpuzzle.cpp b/engines/nancy/action/passwordpuzzle.cpp
new file mode 100644
index 0000000000..c754952e8c
--- /dev/null
+++ b/engines/nancy/action/passwordpuzzle.cpp
@@ -0,0 +1,239 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/nancy/action/passwordpuzzle.h"
+
+#include "engines/nancy/util.h"
+#include "engines/nancy/graphics.h"
+#include "engines/nancy/nancy.h"
+#include "engines/nancy/cursor.h"
+#include "engines/nancy/state/scene.h"
+
+#include "graphics/font.h"
+
+namespace Nancy {
+namespace Action {
+
+void PasswordPuzzle::init() {
+    _drawSurface.create(_screenPosition.width(), _screenPosition.height(), GraphicsManager::pixelFormat);
+    _drawSurface.clear(GraphicsManager::transColor);
+
+    RenderObject::init();
+}
+
+uint16 PasswordPuzzle::readData(Common::SeekableReadStream &stream) {
+    fontID = stream.readUint16LE();
+    cursorBlinkTime = stream.readUint16LE();
+    readRect(stream, nameBounds);
+    readRect(stream, passwordBounds);
+    readRect(stream, _screenPosition);
+
+    char buf[20];
+    stream.read(buf, 20);
+    name = buf;
+    stream.read(buf, 20);
+    password = buf;
+    solveExitScene.readData(stream);
+    stream.skip(2);
+    flagOnSolve.label = stream.readSint16LE();
+    flagOnSolve.flag = (NancyFlag)stream.readByte();
+    solveSound.read(stream, SoundManager::SoundDescription::kNormal);
+    failExitScene.readData(stream);
+    stream.skip(2);
+    flagOnFail.label = stream.readSint16LE();
+    flagOnFail.flag = (NancyFlag)stream.readByte();
+    failSound.read(stream, SoundManager::SoundDescription::kNormal);
+    exitScene.readData(stream);
+    stream.skip(2);
+    flagOnExit.label = stream.readSint16LE();
+    flagOnExit.flag = (NancyFlag)stream.readByte();
+    readRect(stream, exitHotspot);
+
+    return 0xD7;
+}
+
+void PasswordPuzzle::execute(Nancy::NancyEngine *engine) {
+    switch (state) {
+        case kBegin:
+            init();
+            registerGraphics();
+            nextBlinkTime = engine->getTotalPlayTime() + cursorBlinkTime;
+            state = kRun;
+            // fall through
+        case kRun:
+            switch (solveState) {
+                case kNotSolved: {
+                    Common::String &activeField = passwordFieldIsActive ? playerPasswordInput : playerNameInput;
+                    Common::String &correctField = passwordFieldIsActive ? password : name;
+                    Time currentTime = engine->getTotalPlayTime();
+
+                    if (playerHasHitReturn) {
+                        playerHasHitReturn = false;
+
+                        if (activeField.lastChar() == '-') {
+                            activeField.deleteLastChar();
+                            drawText();
+                        }
+
+                        if (activeField.equalsIgnoreCase(correctField)) {
+                            if (!passwordFieldIsActive) {
+                                passwordFieldIsActive = true;
+                            } else {
+                                engine->sound->loadSound(solveSound);
+                                engine->sound->playSound(solveSound.channelID);
+                                solveState = kSolved;
+                            }
+                        } else {
+                            engine->sound->loadSound(failSound);
+                            engine->sound->playSound(failSound.channelID);
+                            solveState = kFailed;
+                        }
+                        
+                        
+                    } else if (currentTime >= nextBlinkTime) {
+                        nextBlinkTime = currentTime + cursorBlinkTime;
+
+                        if (activeField.size() && activeField.lastChar() == '-') {
+                            activeField.deleteLastChar();
+                        } else {
+                            activeField += '-';
+                        }
+
+                        drawText();
+                    }
+
+                    break;
+                }
+                case kFailed:
+                    if (!engine->sound->isSoundPlaying(failSound.channelID)) {
+                        engine->sound->stopSound(failSound.channelID);
+                        state = kActionTrigger;
+                    }
+
+                    break;
+                case kSolved:
+                    if (!engine->sound->isSoundPlaying(solveSound.channelID)) {
+                        engine->sound->stopSound(solveSound.channelID);
+                        state = kActionTrigger;
+                    }
+
+                    break;
+            }
+
+            break;
+        case kActionTrigger:
+            switch (solveState) {
+                case kNotSolved:
+                    if (exitScene.sceneID != 9999) {
+                        engine->scene->changeScene(exitScene.sceneID, exitScene.frameID, exitScene.verticalOffset, exitScene.doNotStartSound);
+                    }
+
+                    engine->scene->setEventFlag(flagOnExit.label, flagOnExit.flag);
+                    break;
+                case kFailed:
+                    if (failExitScene.sceneID != 9999) {
+                        engine->scene->changeScene(failExitScene.sceneID, failExitScene.frameID, failExitScene.verticalOffset, failExitScene.doNotStartSound);
+                    }
+
+                    engine->scene->setEventFlag(flagOnFail.label, flagOnFail.flag);
+                    break;
+                case kSolved:
+                    if (solveExitScene.sceneID != 9999) {
+                        engine->scene->changeScene(solveExitScene.sceneID, solveExitScene.frameID, solveExitScene.verticalOffset, solveExitScene.doNotStartSound);
+                    }
+
+                    engine->scene->setEventFlag(flagOnSolve.label, flagOnSolve.flag);
+                    break;
+            }
+    }
+}
+
+void PasswordPuzzle::handleInput(NancyInput &input) {
+    if (solveState != kNotSolved) {
+        return;
+    }
+
+    if (_engine->scene->getViewport().convertViewportToScreen(exitHotspot).contains(input.mousePos)) {
+        _engine->cursorManager->setCursorType(CursorManager::kExitArrow);
+
+        if (input.input & NancyInput::kLeftMouseButtonUp) {
+            state = kActionTrigger;
+        }
+        return;
+    }
+
+    for (uint i = 0; i < input.otherKbdInput.size(); ++i) {
+        Common::KeyState &key = input.otherKbdInput[i];
+        Common::String &activeField = passwordFieldIsActive ? playerPasswordInput : playerNameInput;
+        Common::String &correctField = passwordFieldIsActive ? password : name;
+        if (key.keycode == Common::KEYCODE_BACKSPACE) {
+            if (activeField.size() && activeField.lastChar() == '-' ? activeField.size() > 1 : true) {
+                if (activeField.lastChar() == '-') {
+                    activeField.deleteChar(activeField.size() -2);
+                } else {
+                    activeField.deleteLastChar();
+                }
+
+                drawText();
+            }
+        } else if (key.keycode == Common::KEYCODE_RETURN) {
+            playerHasHitReturn = true;
+        } else if (Common::isAlnum(key.ascii) || Common::isSpace(key.ascii)) {
+            if (activeField.size() && activeField.lastChar() == '-') {
+                if (activeField.size() <= correctField.size() + 2) {
+                    activeField.deleteLastChar();
+                    activeField += key.ascii;
+                    activeField += '-';
+                }
+            } else {
+                if (activeField.size() <= correctField.size() + 1) {
+                    activeField += key.ascii;
+                }
+            }
+
+            drawText();
+        }
+    }
+}
+
+void PasswordPuzzle::drawText() {
+    _drawSurface.clear(GraphicsManager::transColor);
+    Graphics::Font *font = _engine->graphicsManager->getFont(fontID);
+
+    Common::Rect bounds = nameBounds;
+    bounds = _engine->scene->getViewport().convertViewportToScreen(bounds);
+    bounds = convertToLocal(bounds);
+    Common::Point destPoint(bounds.left, bounds.bottom + 1 - font->getFontHeight());
+    font->drawString(&_drawSurface, playerNameInput, destPoint.x, destPoint.y, bounds.width(), 0);
+
+    bounds = passwordBounds;
+    bounds = _engine->scene->getViewport().convertViewportToScreen(bounds);
+    bounds = convertToLocal(bounds);
+    destPoint.x = bounds.left;
+    destPoint.y = bounds.bottom + 1 - font->getFontHeight();
+    font->drawString(&_drawSurface, playerPasswordInput, destPoint.x, destPoint.y, bounds.width(), 0);
+
+    _needsRedraw = true;
+}
+
+} // End of namespace Action
+} // End of namespace Nancy
diff --git a/engines/nancy/action/passwordpuzzle.h b/engines/nancy/action/passwordpuzzle.h
new file mode 100644
index 0000000000..3e9b27d940
--- /dev/null
+++ b/engines/nancy/action/passwordpuzzle.h
@@ -0,0 +1,89 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef NANCY_ACTION_PASSWORDPUZZLE_H
+#define NANCY_ACTION_PASSWORDPUZZLE_H
+
+#include "engines/nancy/action/recordtypes.h"
+#include "engines/nancy/renderobject.h"
+
+#include "engines/nancy/sound.h"
+#include "engines/nancy/time.h"
+
+#include "common/str.h"
+#include "common/rect.h"
+
+namespace Nancy {
+namespace Action {
+
+class PasswordPuzzle : public ActionRecord, public RenderObject {
+public:
+    enum SolveState { kNotSolved, kFailed, kSolved };
+    PasswordPuzzle(RenderObject &redrawFrom) :
+        RenderObject(redrawFrom),
+        passwordFieldIsActive(false),
+        playerHasHitReturn(false),
+        solveState(kNotSolved) {}
+    virtual ~PasswordPuzzle() {}
+
+    virtual void init() override;
+    
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void execute(Nancy::NancyEngine *engine) override;
+    virtual void handleInput(NancyInput &input) override;
+
+    uint16 fontID; // 0x00
+    Time cursorBlinkTime; // 0x2
+    Common::Rect nameBounds; // 0x4
+    Common::Rect passwordBounds; // 0x14
+    // _screenPosition 0x24
+    Common::String name; // 0x34, 20 bytes long
+    Common::String password; // 0x48, 20 bytes long
+    SceneChangeDesc solveExitScene; // 0x5A
+    FlagDesc flagOnSolve; // 0x66
+    SoundManager::SoundDescription solveSound; // 0x69
+    SceneChangeDesc failExitScene; // 0x8B
+    FlagDesc flagOnFail; // 0x95
+    SoundManager::SoundDescription failSound; // 0x98
+    SceneChangeDesc exitScene; // 0xBA
+    FlagDesc flagOnExit; // 0xC4
+    Common::Rect exitHotspot; // 0xC7
+
+    Common::String playerNameInput;
+    Common::String playerPasswordInput;
+    Time nextBlinkTime;
+    bool passwordFieldIsActive;
+    bool playerHasHitReturn;
+    SolveState solveState;
+
+protected:
+    virtual uint16 getZOrder() const override { return 7; }
+    virtual BlitType getBlitType() const override { return kTrans; }
+    virtual bool isViewportRelative() const override { return true; }
+
+    void drawText();
+};
+
+} // End of namespace Action
+} // End of namespace Nancy
+
+#endif // NANCY_ACTION_PASSWORDPUZZLE_H
diff --git a/engines/nancy/action/recordtypes.cpp b/engines/nancy/action/recordtypes.cpp
index 3eb85bf321..d4ddb7afd0 100644
--- a/engines/nancy/action/recordtypes.cpp
+++ b/engines/nancy/action/recordtypes.cpp
@@ -387,10 +387,6 @@ uint16 LeverPuzzle::readData(Common::SeekableReadStream &stream) {
     return readRaw(stream, 0x192); // TODO
 }
 
-uint16 PasswordPuzzle::readData(Common::SeekableReadStream &stream) {
-    return readRaw(stream, 0xD7); // TODO
-}
-
 uint16 AddInventoryNoHS::readData(Common::SeekableReadStream &stream) {
     return readRaw(stream, 0x2); // TODO
 }
diff --git a/engines/nancy/action/recordtypes.h b/engines/nancy/action/recordtypes.h
index ac4d821a8c..aa5e828a62 100644
--- a/engines/nancy/action/recordtypes.h
+++ b/engines/nancy/action/recordtypes.h
@@ -278,11 +278,6 @@ public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
 };
 
-class PasswordPuzzle : public ActionRecord {
-public:
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
-};
-
 class AddInventoryNoHS : public ActionRecord {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
diff --git a/engines/nancy/font.cpp b/engines/nancy/font.cpp
index df12c2421a..da65d0dc8e 100644
--- a/engines/nancy/font.cpp
+++ b/engines/nancy/font.cpp
@@ -70,8 +70,6 @@ void Font::read(Common::SeekableReadStream &stream, NancyEngine *engine) {
     _semicolonOffset = stream.readUint16LE();
     _slashOffset = stream.readUint16LE();
 
-    
-
     for (uint i = 0; i < 78; ++i) {
         _symbolRects.push_back(Common::Rect());
         Common::Rect &cur = _symbolRects[i];
diff --git a/engines/nancy/input.cpp b/engines/nancy/input.cpp
index bc5fbd6cea..d8fbb5bcb4 100644
--- a/engines/nancy/input.cpp
+++ b/engines/nancy/input.cpp
@@ -35,6 +35,7 @@ void InputManager::processEvents() {
     Common::Event event;
 
     _inputs &= ~(NancyInput::kLeftMouseButtonDown | NancyInput::kLeftMouseButtonUp | NancyInput::kRightMouseButtonDown | NancyInput::kRightMouseButtonUp);
+    _otherKbdInput.clear();
 
     while (_engine->getEventManager()->pollEvent(event)) {
         switch (event.type) {
@@ -45,6 +46,9 @@ void InputManager::processEvents() {
                 } else if (event.kbd.keycode == KEYCODE_q && event.kbd.flags & Common::KBD_CTRL) {
                     // Quit
                     _engine->quitGame();
+                } else {
+                    // Push all other keyboard events into an array and let getInput() callers handle them
+                    _otherKbdInput.push_back(event.kbd);
                 }
                 break;
             case EVENT_CUSTOM_ENGINE_ACTION_START:
@@ -118,6 +122,7 @@ NancyInput InputManager::getInput() const {
     NancyInput ret;
     ret.mousePos = _engine->getEventManager()->getMousePos();
     ret.input = _inputs;
+    ret.otherKbdInput = _otherKbdInput;
     return ret;
 }
 
diff --git a/engines/nancy/input.h b/engines/nancy/input.h
index 94f5df16da..0debac973f 100644
--- a/engines/nancy/input.h
+++ b/engines/nancy/input.h
@@ -26,11 +26,10 @@
 #include "engines/nancy/commontypes.h"
 
 #include "common/rect.h"
+#include "common/array.h"
+#include "common/events.h"
 
 namespace Common {
-template <class T>
-class Array;
-class Keymap;
 typedef class Array<Keymap*> KeymapArray;
 }
 
@@ -58,6 +57,7 @@ struct NancyInput {
 
     Common::Point mousePos;
     uint16 input;
+    Common::Array<Common::KeyState> otherKbdInput;
     
     void eatMouseInput() { mousePos.x = -1; input &= ~(kLeftMouseButton | kRightMouseButton); }
 };
@@ -98,6 +98,7 @@ public:
 private:
     NancyEngine *_engine;
     uint16 _inputs;
+    Common::Array<Common::KeyState> _otherKbdInput;
 };
 
 } // End of namespace Nancy
diff --git a/engines/nancy/module.mk b/engines/nancy/module.mk
index 205d0561ba..c393031ce3 100644
--- a/engines/nancy/module.mk
+++ b/engines/nancy/module.mk
@@ -4,6 +4,7 @@ MODULE_OBJS = \
   action/actionmanager.o \
   action/arfactory_v1.o \
   action/orderingpuzzle.o \
+  action/passwordpuzzle.o \
   action/primaryvideo.o \
   action/recordtypes.o \
   action/rotatinglockpuzzle.o \


Commit: c0ff7498216d84c4a646845b3d0aa23852ef8088
    https://github.com/scummvm/scummvm/commit/c0ff7498216d84c4a646845b3d0aa23852ef8088
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add event flag to PlayDIGISoundAndDie

Added a missing event flag member of the PlayDIGISoundAndDie action record. This allows the player to cut off the chains on nancy1's boiler room lever puzzle.

Changed paths:
    engines/nancy/action/recordtypes.cpp
    engines/nancy/action/recordtypes.h


diff --git a/engines/nancy/action/recordtypes.cpp b/engines/nancy/action/recordtypes.cpp
index d4ddb7afd0..4a61d45583 100644
--- a/engines/nancy/action/recordtypes.cpp
+++ b/engines/nancy/action/recordtypes.cpp
@@ -482,6 +482,9 @@ void ShowInventoryItem::execute(NancyEngine *engine) {
 uint16 PlayDigiSoundAndDie::readData(Common::SeekableReadStream &stream) {
     sound.read(stream, SoundManager::SoundDescription::kDIGI);
     SceneChange::readData(stream);
+    flagOnTrigger.label = stream.readSint16LE();
+    flagOnTrigger.flag = (NancyFlag)stream.readByte();
+    stream.skip(1);
     return 0x2B;
 }
 
@@ -501,6 +504,9 @@ void PlayDigiSoundAndDie::execute(NancyEngine *engine) {
             if (sceneChange.sceneID != 9999) {
                 SceneChange::execute(engine);
             }
+            
+            engine->scene->setEventFlag(flagOnTrigger.label, flagOnTrigger.flag);
+            engine->sound->stopSound(sound.channelID);
             break;
     }
 }
diff --git a/engines/nancy/action/recordtypes.h b/engines/nancy/action/recordtypes.h
index aa5e828a62..e442a7ebfd 100644
--- a/engines/nancy/action/recordtypes.h
+++ b/engines/nancy/action/recordtypes.h
@@ -327,8 +327,8 @@ public:
     // TODO subclass into Play and Stop (?)
 
     SoundManager::SoundDescription sound;
-    // ...
-    // SceneChange elements at 0x1E
+    // SceneChange 0x1E
+    FlagDesc flagOnTrigger;
 };
 
 class PlaySoundPanFrameAnchorAndDie : public ActionRecord {


Commit: 197b055881754ea7af2f56305943d923e33061a8
    https://github.com/scummvm/scummvm/commit/197b055881754ea7af2f56305943d923e33061a8
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Implement lever puzzle

Implemented the LeverPuzzle action record, which is used in the boiler room in nancy1.

Changed paths:
  A engines/nancy/action/leverpuzzle.cpp
  A engines/nancy/action/leverpuzzle.h
    engines/nancy/action/arfactory_v1.cpp
    engines/nancy/action/passwordpuzzle.cpp
    engines/nancy/action/recordtypes.cpp
    engines/nancy/action/recordtypes.h
    engines/nancy/module.mk


diff --git a/engines/nancy/action/arfactory_v1.cpp b/engines/nancy/action/arfactory_v1.cpp
index ccb058cef5..8f7577716e 100644
--- a/engines/nancy/action/arfactory_v1.cpp
+++ b/engines/nancy/action/arfactory_v1.cpp
@@ -31,6 +31,7 @@
 #include "engines/nancy/action/telephone.h"
 #include "engines/nancy/action/sliderpuzzle.h"
 #include "engines/nancy/action/passwordpuzzle.h"
+#include "engines/nancy/action/leverpuzzle.h"
 
 #include "engines/nancy/state/scene.h"
 
@@ -120,7 +121,7 @@ ActionRecord *ActionManager::createActionRecord(uint16 type) {
         case 0x68:
             return new RotatingLockPuzzle(_engine->scene->getViewport());
         case 0x69:
-            return new LeverPuzzle();
+            return new LeverPuzzle(_engine->scene->getViewport());
         case 0x6A:
             return new Telephone(_engine->scene->getViewport());
         case 0x6B:
diff --git a/engines/nancy/action/leverpuzzle.cpp b/engines/nancy/action/leverpuzzle.cpp
new file mode 100644
index 0000000000..6a7bd1d6de
--- /dev/null
+++ b/engines/nancy/action/leverpuzzle.cpp
@@ -0,0 +1,236 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/nancy/action/leverpuzzle.h"
+
+#include "engines/nancy/util.h"
+#include "engines/nancy/graphics.h"
+#include "engines/nancy/resource.h"
+#include "engines/nancy/nancy.h"
+#include "engines/nancy/state/scene.h"
+
+namespace Nancy {
+namespace Action {
+
+void LeverPuzzle::init() {
+    _drawSurface.create(_screenPosition.width(), _screenPosition.height(), GraphicsManager::pixelFormat);
+    _drawSurface.clear(GraphicsManager::transColor);
+
+    Graphics::Surface surf;
+    _engine->_res->loadImage("ciftree", imageName, surf);
+    image.create(surf.w, surf.h, surf.format);
+    image.blitFrom(surf);
+    surf.free();
+}
+
+uint16 LeverPuzzle::readData(Common::SeekableReadStream &stream) {
+    char buf[10];
+    stream.read(buf, 10);
+    imageName = buf;
+
+    for (uint leverID = 0; leverID < 3; ++leverID) {
+        srcRects.push_back(Common::Array<Common::Rect>());
+        for (uint i = 0; i < 4; ++i) {
+            srcRects.back().push_back(Common::Rect());
+            readRect(stream, srcRects.back().back());
+        }
+    }
+
+    for (uint leverID = 0; leverID < 3; ++leverID) {
+        destRects.push_back(Common::Rect());
+        readRect(stream, destRects.back());
+
+        if (leverID == 0) {
+            _screenPosition = destRects.back();
+        } else {
+            _screenPosition.extend(destRects.back());
+        }
+    }
+
+    for (uint leverID = 0; leverID < 3; ++leverID) {
+        playerSequence.push_back(stream.readByte());
+        leverDirection.push_back(true);
+    }
+
+    for (uint leverID = 0; leverID < 3; ++leverID) {
+        correctSequence.push_back(stream.readByte());
+    }
+
+    moveSound.read(stream, SoundManager::SoundDescription::kNormal);
+    noMoveSound.read(stream, SoundManager::SoundDescription::kNormal);
+    solveExitScene.readData(stream);
+    stream.skip(2);
+    flagOnSolve.label = stream.readSint16LE();
+    flagOnSolve.flag = (NancyFlag)stream.readByte();
+    solveSoundDelay = stream.readUint16LE();
+    solveSound.read(stream, SoundManager::SoundDescription::kNormal);
+    exitScene.readData(stream);
+    stream.skip(2);
+    flagOnExit.label = stream.readSint16LE();
+    flagOnExit.flag = (NancyFlag)stream.readByte();
+    readRect(stream, exitHotspot);
+
+    return 0x192;
+}
+
+void LeverPuzzle::execute(Nancy::NancyEngine *engine) {
+    switch (state) {
+        case kBegin:
+            init();
+            registerGraphics();
+            engine->sound->loadSound(moveSound);
+            engine->sound->loadSound(noMoveSound);
+
+            for (uint i = 0; i < 3; ++i) {
+                drawLever(i);
+            }
+
+            state = kRun;
+            // fall through
+        case kRun:
+            switch (solveState) {
+                case kNotSolved:
+                    for (uint i = 0; i < 3; ++i) {
+                        if (playerSequence[i] != correctSequence[i]) {
+                            return;
+                        }
+                    }
+                    
+                    engine->scene->setEventFlag(flagOnSolve.label, flagOnSolve.flag);
+                    solveSoundPlayTime = _engine->getTotalPlayTime() + solveSoundDelay * 1000;
+                    solveState = kPlaySound;
+                    break;
+                case kPlaySound:
+                    if (_engine->getTotalPlayTime() <= solveSoundPlayTime) {
+                        break;
+                    }
+
+                    engine->sound->loadSound(solveSound);
+                    _engine->sound->playSound(solveSound.channelID);
+                    solveState = kWaitForSound;
+                    break;
+                case kWaitForSound:
+                    if (!_engine->sound->isSoundPlaying(solveSound.channelID)) {
+                        _engine->sound->stopSound(solveSound.channelID);
+                        state = kActionTrigger;
+                    }
+
+                    break;
+            }
+
+            break;
+        case kActionTrigger:
+            _engine->sound->stopSound(moveSound.channelID);
+            _engine->sound->stopSound(noMoveSound.channelID);
+            
+            if (solveState == kNotSolved) {
+                if (exitScene.sceneID != 9999) {
+                    _engine->scene->changeScene(exitScene.sceneID, exitScene.frameID, exitScene.verticalOffset, exitScene.doNotStartSound);
+                    _engine->scene->setEventFlag(flagOnExit.label, flagOnExit.flag);
+                }
+            } else {
+                if (solveExitScene.sceneID != 9999) {
+                    _engine->scene->changeScene(solveExitScene.sceneID, solveExitScene.frameID, solveExitScene.verticalOffset, solveExitScene.doNotStartSound);
+                }
+            }
+
+            isDone = true;
+    }
+}
+
+void LeverPuzzle::handleInput(NancyInput &input) {
+    if (solveState != kNotSolved) {
+        return;
+    }
+
+    if (_engine->scene->getViewport().convertViewportToScreen(exitHotspot).contains(input.mousePos)) {
+        _engine->cursorManager->setCursorType(CursorManager::kExitArrow);
+
+        if (input.input & NancyInput::kLeftMouseButtonUp) {
+            state = kActionTrigger;
+        }
+        return;
+    }
+
+    for (uint i = 0; i < 3; ++i) {
+        if (_engine->scene->getViewport().convertViewportToScreen(destRects[i]).contains(input.mousePos)) {
+            _engine->cursorManager->setCursorType(CursorManager::kHotspot);
+            
+            if (input.input & NancyInput::kLeftMouseButtonUp) {
+                bool isMoving = false;
+                // Hardcoded by the original engine
+                switch (i) {
+                    case 0:
+                        isMoving = true;
+                        break;
+                    case 1:
+                        if (playerSequence[0] == 1) {
+                            isMoving = true;
+                        }
+                        break;
+                    case 2:
+                        if (playerSequence[0] == 2) {
+                            isMoving = true;
+                        }
+                        break;
+                }
+
+                if (isMoving) {
+                    _engine->sound->playSound(moveSound.channelID);
+
+                    if (leverDirection[i]) {
+                        // Moving down
+                        if (playerSequence[i] == 3) {
+                            --playerSequence[i];
+                            leverDirection[i] = false;
+                        } else {
+                            ++playerSequence[i];
+                        }
+                    } else {
+                        // Moving up
+                        if (playerSequence[i] == 0) {
+                            ++playerSequence[i];
+                            leverDirection[i] = true;
+                        } else {
+                            --playerSequence[i];
+                        }
+                    }
+
+                    drawLever(i);
+                } else {
+                    _engine->sound->playSound(noMoveSound.channelID);
+                    return;
+                }
+            }
+        }
+    }
+}
+
+void LeverPuzzle::drawLever(uint id) {
+    Common::Point destPoint(destRects[id].left - _screenPosition.left, destRects[id].top - _screenPosition.top);
+    _drawSurface.blitFrom(image, srcRects[id][playerSequence[id]], destPoint);
+    
+    _needsRedraw = true;
+}
+
+} // End of namespace Action
+} // End of namespace Nancy
diff --git a/engines/nancy/action/leverpuzzle.h b/engines/nancy/action/leverpuzzle.h
new file mode 100644
index 0000000000..a2c9c9e0ed
--- /dev/null
+++ b/engines/nancy/action/leverpuzzle.h
@@ -0,0 +1,82 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef NANCY_ACTION_LEVERPUZZLE_H
+#define NANCY_ACTION_LEVERPUZZLE_H
+
+#include "engines/nancy/action/recordtypes.h"
+#include "engines/nancy/renderobject.h"
+
+#include "engines/nancy/sound.h"
+
+#include "common/str.h"
+#include "common/array.h"
+
+#include "graphics/managed_surface.h"
+
+namespace Nancy {
+namespace Action {
+
+class LeverPuzzle : public ActionRecord, public RenderObject {
+public:
+    enum SolveState { kNotSolved, kPlaySound, kWaitForSound };
+    LeverPuzzle(RenderObject &redrawFrom) : RenderObject(redrawFrom) {}
+    virtual ~LeverPuzzle() {}
+
+    virtual void init() override;
+    
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void execute(Nancy::NancyEngine *engine) override;
+    virtual void handleInput(NancyInput &input) override;
+
+    Common::String imageName; // 0x0
+    Common::Array<Common::Array<Common::Rect>> srcRects; // 0xA, 0xC0 bytes
+    Common::Array<Common::Rect> destRects; // 0xCA, 0x30 bytes
+    Common::Array<byte> correctSequence; // 0xFA, 3 bytes
+    SoundManager::SoundDescription moveSound; // 0x100
+    SoundManager::SoundDescription noMoveSound; // 0x122
+    SceneChangeDesc solveExitScene; // 0x144
+    FlagDesc flagOnSolve; // 0x14E
+    uint16 solveSoundDelay; // 0x151
+    SoundManager::SoundDescription solveSound; // 0x153
+    SceneChangeDesc exitScene; // 0x175
+    FlagDesc flagOnExit; // 0x17F
+    Common::Rect exitHotspot; // 0x182
+
+    Common::Array<byte> playerSequence;
+    Common::Array<bool> leverDirection;
+    Graphics::ManagedSurface image;
+    Time solveSoundPlayTime;
+    SolveState solveState = kNotSolved;
+
+protected:
+    virtual uint16 getZOrder() const override { return 7; }
+    virtual BlitType getBlitType() const override { return kTrans; }
+    virtual bool isViewportRelative() const override { return true; }
+
+    void drawLever(uint id);
+};
+
+} // End of namespace Action
+} // End of namespace Nancy
+
+#endif // NANCY_ACTION_LEVERPUZZLE_H
diff --git a/engines/nancy/action/passwordpuzzle.cpp b/engines/nancy/action/passwordpuzzle.cpp
index c754952e8c..44158f9816 100644
--- a/engines/nancy/action/passwordpuzzle.cpp
+++ b/engines/nancy/action/passwordpuzzle.cpp
@@ -164,6 +164,8 @@ void PasswordPuzzle::execute(Nancy::NancyEngine *engine) {
                     engine->scene->setEventFlag(flagOnSolve.label, flagOnSolve.flag);
                     break;
             }
+
+            isDone = true;
     }
 }
 
diff --git a/engines/nancy/action/recordtypes.cpp b/engines/nancy/action/recordtypes.cpp
index 4a61d45583..9e6912e67e 100644
--- a/engines/nancy/action/recordtypes.cpp
+++ b/engines/nancy/action/recordtypes.cpp
@@ -383,10 +383,6 @@ uint16 WinGame::readData(Common::SeekableReadStream &stream) {
     return 1;
 }
 
-uint16 LeverPuzzle::readData(Common::SeekableReadStream &stream) {
-    return readRaw(stream, 0x192); // TODO
-}
-
 uint16 AddInventoryNoHS::readData(Common::SeekableReadStream &stream) {
     return readRaw(stream, 0x2); // TODO
 }
diff --git a/engines/nancy/action/recordtypes.h b/engines/nancy/action/recordtypes.h
index e442a7ebfd..9b5a5e4478 100644
--- a/engines/nancy/action/recordtypes.h
+++ b/engines/nancy/action/recordtypes.h
@@ -273,11 +273,6 @@ public:
     byte winData = 0;
 };
 
-class LeverPuzzle : public ActionRecord {
-public:
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
-};
-
 class AddInventoryNoHS : public ActionRecord {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
diff --git a/engines/nancy/module.mk b/engines/nancy/module.mk
index c393031ce3..6cc0aef924 100644
--- a/engines/nancy/module.mk
+++ b/engines/nancy/module.mk
@@ -3,6 +3,7 @@ MODULE := engines/nancy
 MODULE_OBJS = \
   action/actionmanager.o \
   action/arfactory_v1.o \
+  action/leverpuzzle.o \
   action/orderingpuzzle.o \
   action/passwordpuzzle.o \
   action/primaryvideo.o \


Commit: 5d00c8d195a9aa05db19d3ce972f11dd01e66a26
    https://github.com/scummvm/scummvm/commit/5d00c8d195a9aa05db19d3ce972f11dd01e66a26
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Fix two primary video instances being active simultaneously

Added a better check for cases where two or more primary videos are in the same scene and one sets event flags that activate the other.

Changed paths:
    engines/nancy/action/primaryvideo.cpp
    engines/nancy/action/primaryvideo.h


diff --git a/engines/nancy/action/primaryvideo.cpp b/engines/nancy/action/primaryvideo.cpp
index 15d0b9db48..99baafb1c7 100644
--- a/engines/nancy/action/primaryvideo.cpp
+++ b/engines/nancy/action/primaryvideo.cpp
@@ -37,10 +37,13 @@
 namespace Nancy {
 namespace Action {
 
-bool PlayPrimaryVideoChan0::isExitingScene = false;
+PlayPrimaryVideoChan0 *PlayPrimaryVideoChan0::activePrimaryVideo = nullptr;
 
 PlayPrimaryVideoChan0::~PlayPrimaryVideoChan0() {
     _decoder.close();
+	if (activePrimaryVideo == this) {
+		activePrimaryVideo = nullptr;
+	}
     _engine->scene->getTextbox().setVisible(false);
 }
 
@@ -159,14 +162,12 @@ uint16 PlayPrimaryVideoChan0::readData(Common::SeekableReadStream &stream) {
         }
     }
 
-    isExitingScene = false;
-
     bytesRead = stream.pos() - bytesRead;
     return bytesRead;
 }
 
 void PlayPrimaryVideoChan0::execute(NancyEngine *engine) {
-    if (isExitingScene) {
+	if (activePrimaryVideo != this && activePrimaryVideo != nullptr) {
         return;
     }
 
@@ -177,6 +178,7 @@ void PlayPrimaryVideoChan0::execute(NancyEngine *engine) {
             engine->sound->loadSound(sound);
             engine->sound->playSound(sound.channelID);
             state = kRun;
+		    activePrimaryVideo = this;
             // fall through
         case kRun:
             if (!hasDrawnTextbox) {
@@ -265,7 +267,6 @@ void PlayPrimaryVideoChan0::execute(NancyEngine *engine) {
                     // Continue to next dialogue scene
                     SceneChange::execute(engine);
                 }
-                isExitingScene = true;
             }
 
             break;
@@ -309,7 +310,7 @@ void PlayPrimaryVideoChan0::addConditionalResponses(NancyEngine *engine) {
 }
 
 void PlayPrimaryVideoChan0::addGoodbye(NancyEngine *engine) {
-    for (auto res : nancy1Goodbyes) {
+    for (auto &res : nancy1Goodbyes) {
         if (res.characterID == goodbyeResponseCharacterID) {
             Common::File file;
             char snd[10];
diff --git a/engines/nancy/action/primaryvideo.h b/engines/nancy/action/primaryvideo.h
index aedc860380..231a18b3fe 100644
--- a/engines/nancy/action/primaryvideo.h
+++ b/engines/nancy/action/primaryvideo.h
@@ -98,8 +98,8 @@ public:
     bool hasDrawnTextbox = false;
     int16 pickedResponse = -1;
 
-    // Used to avoid showing first frame of unrelated primary video between scenes
-    static bool isExitingScene;
+    // Used to avoid clashes between multiple instances in the same scene
+	static PlayPrimaryVideoChan0 *activePrimaryVideo;
 
 protected:
     virtual uint16 getZOrder() const override { return 8; }


Commit: 5d215d46b488261f493019b71395b0f7381cca30
    https://github.com/scummvm/scummvm/commit/5d215d46b488261f493019b71395b0f7381cca30
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Secondary video fixes

Fixed a couple of visual glitches in secondary video where NPCs would get drawn as a black rectangle, or not drawn at all.

Changed paths:
    engines/nancy/action/secondaryvideo.cpp
    engines/nancy/action/secondaryvideo.h


diff --git a/engines/nancy/action/secondaryvideo.cpp b/engines/nancy/action/secondaryvideo.cpp
index c088fec9b4..128e2d6e32 100644
--- a/engines/nancy/action/secondaryvideo.cpp
+++ b/engines/nancy/action/secondaryvideo.cpp
@@ -67,11 +67,12 @@ void PlaySecondaryVideo::updateGraphics() {
     }
 
     if (_isPlaying) {
+        if (!_decoder.isPlaying()) {
+            _decoder.start();
+        }
+        
         switch (hoverState) {
             case kNoHover:
-                if (!_decoder.isPlaying()) {
-                    _decoder.start();
-                }
                 if (_isHovered) {
                     _decoder.seekToFrame(onHoverFirstFrame);
                     
diff --git a/engines/nancy/action/secondaryvideo.h b/engines/nancy/action/secondaryvideo.h
index ed57016a85..b18f85632e 100644
--- a/engines/nancy/action/secondaryvideo.h
+++ b/engines/nancy/action/secondaryvideo.h
@@ -79,7 +79,7 @@ protected:
 
     HoverState hoverState = kNoHover;
     AVFDecoder _decoder;
-    uint _currentViewportFrame = 0;
+    int _currentViewportFrame = -1;
     bool _isPlaying = false;
     bool _isHovered = false;
 };


Commit: 7d93c7996e481500d527938fb0aa20dec35b7b99
    https://github.com/scummvm/scummvm/commit/7d93c7996e481500d527938fb0aa20dec35b7b99
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Clear input after exiting console

Fixed an issue where the input manager would still think the player was pressing ctrl even after closing the debug console.

Changed paths:
    engines/nancy/console.cpp
    engines/nancy/input.cpp
    engines/nancy/input.h


diff --git a/engines/nancy/console.cpp b/engines/nancy/console.cpp
index 4d954a267c..677fedd938 100644
--- a/engines/nancy/console.cpp
+++ b/engines/nancy/console.cpp
@@ -124,6 +124,10 @@ void NancyConsole::postEnter() {
 		_imageFile.clear();
 		_imageCifTree.clear();
 	}
+
+	// After calling the console, action end events get sent to it and the input manager
+	// can still think a keyboard button is being held when it is not; clearing all inputs fixes that
+	_vm->input->forceCleanInput();
 }
 
 bool NancyConsole::Cmd_cifHexDump(int argc, const char **argv) {
diff --git a/engines/nancy/input.cpp b/engines/nancy/input.cpp
index d8fbb5bcb4..2157b62027 100644
--- a/engines/nancy/input.cpp
+++ b/engines/nancy/input.cpp
@@ -126,6 +126,11 @@ NancyInput InputManager::getInput() const {
     return ret;
 }
 
+void InputManager::forceCleanInput() {
+    _inputs = 0;
+    _otherKbdInput.clear();
+}
+
 void InputManager::initKeymaps(Common::KeymapArray &keymaps) {
     using namespace Common;
 	using namespace Nancy;
diff --git a/engines/nancy/input.h b/engines/nancy/input.h
index 0debac973f..dce3cc5dce 100644
--- a/engines/nancy/input.h
+++ b/engines/nancy/input.h
@@ -92,6 +92,7 @@ public:
     void processEvents();
 
     NancyInput getInput() const;
+    void forceCleanInput();
 
     static void initKeymaps(Common::KeymapArray &keymaps);
     


Commit: d64a309fb34a2505976236a72c48615a2b8f6230
    https://github.com/scummvm/scummvm/commit/d64a309fb34a2505976236a72c48615a2b8f6230
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Small code refactor

Renamed and moved a few struct definitions to commontypes.h, added some convenience functions to Scene and removed a couple of unused variables.

Changed paths:
  A engines/nancy/commontypes.cpp
    engines/nancy/action/leverpuzzle.cpp
    engines/nancy/action/leverpuzzle.h
    engines/nancy/action/orderingpuzzle.cpp
    engines/nancy/action/orderingpuzzle.h
    engines/nancy/action/passwordpuzzle.cpp
    engines/nancy/action/passwordpuzzle.h
    engines/nancy/action/primaryvideo.cpp
    engines/nancy/action/primaryvideo.h
    engines/nancy/action/recordtypes.cpp
    engines/nancy/action/recordtypes.h
    engines/nancy/action/responses.cpp
    engines/nancy/action/rotatinglockpuzzle.cpp
    engines/nancy/action/rotatinglockpuzzle.h
    engines/nancy/action/secondaryvideo.cpp
    engines/nancy/action/secondaryvideo.h
    engines/nancy/action/sliderpuzzle.cpp
    engines/nancy/action/sliderpuzzle.h
    engines/nancy/action/staticbitmapanim.cpp
    engines/nancy/action/staticbitmapanim.h
    engines/nancy/action/telephone.cpp
    engines/nancy/action/telephone.h
    engines/nancy/commontypes.h
    engines/nancy/module.mk
    engines/nancy/sound.cpp
    engines/nancy/sound.h
    engines/nancy/state/logo.cpp
    engines/nancy/state/map.cpp
    engines/nancy/state/scene.cpp
    engines/nancy/state/scene.h
    engines/nancy/ui/viewport.cpp
    engines/nancy/util.h


diff --git a/engines/nancy/action/leverpuzzle.cpp b/engines/nancy/action/leverpuzzle.cpp
index 6a7bd1d6de..5612567ecc 100644
--- a/engines/nancy/action/leverpuzzle.cpp
+++ b/engines/nancy/action/leverpuzzle.cpp
@@ -26,6 +26,7 @@
 #include "engines/nancy/graphics.h"
 #include "engines/nancy/resource.h"
 #include "engines/nancy/nancy.h"
+#include "engines/nancy/sound.h"
 #include "engines/nancy/state/scene.h"
 
 namespace Nancy {
@@ -75,14 +76,14 @@ uint16 LeverPuzzle::readData(Common::SeekableReadStream &stream) {
         correctSequence.push_back(stream.readByte());
     }
 
-    moveSound.read(stream, SoundManager::SoundDescription::kNormal);
-    noMoveSound.read(stream, SoundManager::SoundDescription::kNormal);
+    moveSound.read(stream, SoundDescription::kNormal);
+    noMoveSound.read(stream, SoundDescription::kNormal);
     solveExitScene.readData(stream);
     stream.skip(2);
     flagOnSolve.label = stream.readSint16LE();
     flagOnSolve.flag = (NancyFlag)stream.readByte();
     solveSoundDelay = stream.readUint16LE();
-    solveSound.read(stream, SoundManager::SoundDescription::kNormal);
+    solveSound.read(stream, SoundDescription::kNormal);
     exitScene.readData(stream);
     stream.skip(2);
     flagOnExit.label = stream.readSint16LE();
@@ -115,7 +116,7 @@ void LeverPuzzle::execute(Nancy::NancyEngine *engine) {
                         }
                     }
                     
-                    engine->scene->setEventFlag(flagOnSolve.label, flagOnSolve.flag);
+                    engine->scene->setEventFlag(flagOnSolve);
                     solveSoundPlayTime = _engine->getTotalPlayTime() + solveSoundDelay * 1000;
                     solveState = kPlaySound;
                     break;
@@ -143,14 +144,10 @@ void LeverPuzzle::execute(Nancy::NancyEngine *engine) {
             _engine->sound->stopSound(noMoveSound.channelID);
             
             if (solveState == kNotSolved) {
-                if (exitScene.sceneID != 9999) {
-                    _engine->scene->changeScene(exitScene.sceneID, exitScene.frameID, exitScene.verticalOffset, exitScene.doNotStartSound);
-                    _engine->scene->setEventFlag(flagOnExit.label, flagOnExit.flag);
-                }
+                _engine->scene->changeScene(exitScene);
+                _engine->scene->setEventFlag(flagOnExit);
             } else {
-                if (solveExitScene.sceneID != 9999) {
-                    _engine->scene->changeScene(solveExitScene.sceneID, solveExitScene.frameID, solveExitScene.verticalOffset, solveExitScene.doNotStartSound);
-                }
+                _engine->scene->changeScene(solveExitScene);
             }
 
             isDone = true;
diff --git a/engines/nancy/action/leverpuzzle.h b/engines/nancy/action/leverpuzzle.h
index a2c9c9e0ed..d1f44f8b30 100644
--- a/engines/nancy/action/leverpuzzle.h
+++ b/engines/nancy/action/leverpuzzle.h
@@ -23,10 +23,10 @@
 #ifndef NANCY_ACTION_LEVERPUZZLE_H
 #define NANCY_ACTION_LEVERPUZZLE_H
 
-#include "engines/nancy/action/recordtypes.h"
+#include "engines/nancy/action/actionrecord.h"
 #include "engines/nancy/renderobject.h"
 
-#include "engines/nancy/sound.h"
+#include "engines/nancy/commontypes.h"
 
 #include "common/str.h"
 #include "common/array.h"
@@ -52,14 +52,14 @@ public:
     Common::Array<Common::Array<Common::Rect>> srcRects; // 0xA, 0xC0 bytes
     Common::Array<Common::Rect> destRects; // 0xCA, 0x30 bytes
     Common::Array<byte> correctSequence; // 0xFA, 3 bytes
-    SoundManager::SoundDescription moveSound; // 0x100
-    SoundManager::SoundDescription noMoveSound; // 0x122
-    SceneChangeDesc solveExitScene; // 0x144
-    FlagDesc flagOnSolve; // 0x14E
+    SoundDescription moveSound; // 0x100
+    SoundDescription noMoveSound; // 0x122
+    SceneChangeDescription solveExitScene; // 0x144
+    EventFlagDescription flagOnSolve; // 0x14E
     uint16 solveSoundDelay; // 0x151
-    SoundManager::SoundDescription solveSound; // 0x153
-    SceneChangeDesc exitScene; // 0x175
-    FlagDesc flagOnExit; // 0x17F
+    SoundDescription solveSound; // 0x153
+    SceneChangeDescription exitScene; // 0x175
+    EventFlagDescription flagOnExit; // 0x17F
     Common::Rect exitHotspot; // 0x182
 
     Common::Array<byte> playerSequence;
diff --git a/engines/nancy/action/orderingpuzzle.cpp b/engines/nancy/action/orderingpuzzle.cpp
index 5331ca81f2..45ebc33e97 100644
--- a/engines/nancy/action/orderingpuzzle.cpp
+++ b/engines/nancy/action/orderingpuzzle.cpp
@@ -30,6 +30,7 @@
 #include "engines/nancy/resource.h"
 #include "engines/nancy/input.h"
 #include "engines/nancy/cursor.h"
+#include "engines/nancy/sound.h"
 
 #include "graphics/surface.h"
 
@@ -88,13 +89,13 @@ uint16 OrderingPuzzle::readData(Common::SeekableReadStream &stream) {
         correctSequence.push_back(stream.readByte());
     }
 
-    clickSound.read(stream, SoundManager::SoundDescription::kNormal);
+    clickSound.read(stream, SoundDescription::kNormal);
     solveExitScene.readData(stream);
     stream.skip(2); // shouldStopRendering, useless
     flagOnSolve.label = stream.readSint16LE();
     flagOnSolve.flag = (NancyFlag)stream.readByte();
     solveSoundDelay = stream.readUint16LE();
-    solveSound.read(stream, SoundManager::SoundDescription::kNormal);
+    solveSound.read(stream, SoundDescription::kNormal);
     exitScene.readData(stream);
     stream.skip(2); // shouldStopRendering, useless
     flagOnExit.label = stream.readSint16LE();
@@ -126,7 +127,7 @@ void OrderingPuzzle::execute(Nancy::NancyEngine *engine) {
                         }
                     }
 
-                    _engine->scene->setEventFlag(flagOnSolve.label, flagOnSolve.flag);
+                    _engine->scene->setEventFlag(flagOnSolve);
                     solveSoundPlayTime = _engine->getTotalPlayTime() + solveSoundDelay * 1000;
                     solveState = kPlaySound;
                     // fall through
@@ -151,17 +152,14 @@ void OrderingPuzzle::execute(Nancy::NancyEngine *engine) {
             _engine->sound->stopSound(solveSound.channelID);
 
             if (solveState == kNotSolved) {
-                if (exitScene.sceneID != 9999) {
-                    _engine->scene->changeScene(exitScene.sceneID, exitScene.frameID, exitScene.verticalOffset, exitScene.doNotStartSound);
-                    _engine->scene->setEventFlag(flagOnExit.label, flagOnExit.flag);
-                }
+                _engine->scene->changeScene(exitScene);
+                _engine->scene->setEventFlag(flagOnExit);
             } else {
-                if (solveExitScene.sceneID != 9999) {
-                    _engine->scene->changeScene(solveExitScene.sceneID, solveExitScene.frameID, solveExitScene.verticalOffset, solveExitScene.doNotStartSound);
-                }
+                _engine->scene->changeScene(solveExitScene);
             }
 
             isDone = true;
+            break;
     }
 }
 
diff --git a/engines/nancy/action/orderingpuzzle.h b/engines/nancy/action/orderingpuzzle.h
index 2626d6169d..f78adca366 100644
--- a/engines/nancy/action/orderingpuzzle.h
+++ b/engines/nancy/action/orderingpuzzle.h
@@ -23,10 +23,10 @@
 #ifndef NANCY_ACTION_ORDERINGPUZZLE_H
 #define NANCY_ACTION_ORDERINGPUZZLE_H
 
-#include "engines/nancy/action/recordtypes.h"
+#include "engines/nancy/action/actionrecord.h"
 #include "engines/nancy/renderobject.h"
 
-#include "engines/nancy/sound.h"
+#include "engines/nancy/commontypes.h"
 
 #include "common/str.h"
 #include "common/array.h"
@@ -54,13 +54,13 @@ public:
     Common::Array<Common::Rect> destRects; // 0xFC, 15
     uint16 sequenceLength; // 0x1EC;
     Common::Array<byte> correctSequence; // 0x1EE, 15 bytes
-    Nancy::SoundManager::SoundDescription clickSound; // 0x1FD, kNormal
-    SceneChangeDesc solveExitScene; // 0x21F
-    FlagDesc flagOnSolve; // 0x229
+    Nancy::SoundDescription clickSound; // 0x1FD, kNormal
+    SceneChangeDescription solveExitScene; // 0x21F
+    EventFlagDescription flagOnSolve; // 0x229
     uint16 solveSoundDelay; // 0x22C 
-    Nancy::SoundManager::SoundDescription solveSound; // 0x22E
-    SceneChangeDesc exitScene; // 0x250
-    FlagDesc flagOnExit; // 0x25A
+    Nancy::SoundDescription solveSound; // 0x22E
+    SceneChangeDescription exitScene; // 0x250
+    EventFlagDescription flagOnExit; // 0x25A
     Common::Rect exitHotspot; // 0x25D
 
     SolveState solveState = kNotSolved;
diff --git a/engines/nancy/action/passwordpuzzle.cpp b/engines/nancy/action/passwordpuzzle.cpp
index 44158f9816..5473d6a84d 100644
--- a/engines/nancy/action/passwordpuzzle.cpp
+++ b/engines/nancy/action/passwordpuzzle.cpp
@@ -26,6 +26,7 @@
 #include "engines/nancy/graphics.h"
 #include "engines/nancy/nancy.h"
 #include "engines/nancy/cursor.h"
+#include "engines/nancy/sound.h"
 #include "engines/nancy/state/scene.h"
 
 #include "graphics/font.h"
@@ -56,12 +57,12 @@ uint16 PasswordPuzzle::readData(Common::SeekableReadStream &stream) {
     stream.skip(2);
     flagOnSolve.label = stream.readSint16LE();
     flagOnSolve.flag = (NancyFlag)stream.readByte();
-    solveSound.read(stream, SoundManager::SoundDescription::kNormal);
+    solveSound.read(stream, SoundDescription::kNormal);
     failExitScene.readData(stream);
     stream.skip(2);
     flagOnFail.label = stream.readSint16LE();
     flagOnFail.flag = (NancyFlag)stream.readByte();
-    failSound.read(stream, SoundManager::SoundDescription::kNormal);
+    failSound.read(stream, SoundDescription::kNormal);
     exitScene.readData(stream);
     stream.skip(2);
     flagOnExit.label = stream.readSint16LE();
@@ -143,25 +144,16 @@ void PasswordPuzzle::execute(Nancy::NancyEngine *engine) {
         case kActionTrigger:
             switch (solveState) {
                 case kNotSolved:
-                    if (exitScene.sceneID != 9999) {
-                        engine->scene->changeScene(exitScene.sceneID, exitScene.frameID, exitScene.verticalOffset, exitScene.doNotStartSound);
-                    }
-
-                    engine->scene->setEventFlag(flagOnExit.label, flagOnExit.flag);
+                    engine->scene->changeScene(exitScene);
+                    engine->scene->setEventFlag(flagOnExit);
                     break;
                 case kFailed:
-                    if (failExitScene.sceneID != 9999) {
-                        engine->scene->changeScene(failExitScene.sceneID, failExitScene.frameID, failExitScene.verticalOffset, failExitScene.doNotStartSound);
-                    }
-
-                    engine->scene->setEventFlag(flagOnFail.label, flagOnFail.flag);
+                    engine->scene->changeScene(failExitScene);
+                    engine->scene->setEventFlag(flagOnFail.label);
                     break;
                 case kSolved:
-                    if (solveExitScene.sceneID != 9999) {
-                        engine->scene->changeScene(solveExitScene.sceneID, solveExitScene.frameID, solveExitScene.verticalOffset, solveExitScene.doNotStartSound);
-                    }
-
-                    engine->scene->setEventFlag(flagOnSolve.label, flagOnSolve.flag);
+                    engine->scene->changeScene(solveExitScene);
+                    engine->scene->setEventFlag(flagOnSolve.label);
                     break;
             }
 
diff --git a/engines/nancy/action/passwordpuzzle.h b/engines/nancy/action/passwordpuzzle.h
index 3e9b27d940..4f516b2cfb 100644
--- a/engines/nancy/action/passwordpuzzle.h
+++ b/engines/nancy/action/passwordpuzzle.h
@@ -23,10 +23,10 @@
 #ifndef NANCY_ACTION_PASSWORDPUZZLE_H
 #define NANCY_ACTION_PASSWORDPUZZLE_H
 
-#include "engines/nancy/action/recordtypes.h"
+#include "engines/nancy/action/actionrecord.h"
 #include "engines/nancy/renderobject.h"
 
-#include "engines/nancy/sound.h"
+#include "engines/nancy/commontypes.h"
 #include "engines/nancy/time.h"
 
 #include "common/str.h"
@@ -58,14 +58,14 @@ public:
     // _screenPosition 0x24
     Common::String name; // 0x34, 20 bytes long
     Common::String password; // 0x48, 20 bytes long
-    SceneChangeDesc solveExitScene; // 0x5A
-    FlagDesc flagOnSolve; // 0x66
-    SoundManager::SoundDescription solveSound; // 0x69
-    SceneChangeDesc failExitScene; // 0x8B
-    FlagDesc flagOnFail; // 0x95
-    SoundManager::SoundDescription failSound; // 0x98
-    SceneChangeDesc exitScene; // 0xBA
-    FlagDesc flagOnExit; // 0xC4
+    SceneChangeDescription solveExitScene; // 0x5A
+    EventFlagDescription flagOnSolve; // 0x66
+    SoundDescription solveSound; // 0x69
+    SceneChangeDescription failExitScene; // 0x8B
+    EventFlagDescription flagOnFail; // 0x95
+    SoundDescription failSound; // 0x98
+    SceneChangeDescription exitScene; // 0xBA
+    EventFlagDescription flagOnExit; // 0xC4
     Common::Rect exitHotspot; // 0xC7
 
     Common::String playerNameInput;
diff --git a/engines/nancy/action/primaryvideo.cpp b/engines/nancy/action/primaryvideo.cpp
index 99baafb1c7..59c02592df 100644
--- a/engines/nancy/action/primaryvideo.cpp
+++ b/engines/nancy/action/primaryvideo.cpp
@@ -92,14 +92,14 @@ uint16 PlayPrimaryVideoChan0::readData(Common::SeekableReadStream &stream) {
     UI::Textbox::assembleTextLine(rawText, text, 1500);
     delete[] rawText;
 
-    sound.read(stream, SoundManager::SoundDescription::kNormal);
-    responseGenericSound.read(stream, SoundManager::SoundDescription::kNormal);
+    sound.read(stream, SoundDescription::kNormal);
+    responseGenericSound.read(stream, SoundDescription::kNormal);
     stream.skip(1);
     conditionalResponseCharacterID = stream.readByte();
     goodbyeResponseCharacterID = stream.readByte();
     numSceneChanges = stream.readByte();
     shouldPopScene = stream.readByte() == 1;
-    SceneChange::readData(stream);
+    sceneChange.readData(stream);
 
     stream.seek(bytesRead + 0x69C);
 
@@ -157,8 +157,8 @@ uint16 PlayPrimaryVideoChan0::readData(Common::SeekableReadStream &stream) {
             }
 
             flagsStruct.type = (FlagsStruct::ConditionType)stream.readByte();
-            flagsStruct.label = stream.readSint16LE();
-            flagsStruct.flag = (NancyFlag)stream.readByte();
+            flagsStruct.flagDesc.label = stream.readSint16LE();
+            flagsStruct.flagDesc.flag = (NancyFlag)stream.readByte();
         }
     }
 
@@ -202,7 +202,7 @@ void PlayPrimaryVideoChan0::execute(NancyEngine *engine) {
             }
 
             if (!engine->sound->isSoundPlaying(sound.channelID)) {
-                _engine->sound->stopSound(sound.channelID);
+                engine->sound->stopSound(sound.channelID);
                 if (responses.size() == 0) {
                     // NPC has finished talking with no responses available, auto-advance to next scene
                     state = kActionTrigger;
@@ -238,13 +238,13 @@ void PlayPrimaryVideoChan0::execute(NancyEngine *engine) {
                 if (conditionsSatisfied) {
                     switch (flags.type) {
                         case FlagsStruct::kEventFlags:
-                            engine->scene->setEventFlag(flags.label, flags.flag);
+                            engine->scene->setEventFlag(flags.flagDesc);
                             break;
                         case FlagsStruct::kInventory:
-                            if (flags.flag == kTrue) {
-                                engine->scene->addItemToInventory(flags.label);
+                            if (flags.flagDesc.flag == kTrue) {
+                                engine->scene->addItemToInventory(flags.flagDesc.label);
                             } else {
-                                engine->scene->removeItemFromInventory(flags.label);
+                                engine->scene->removeItemFromInventory(flags.flagDesc.label);
                             }
                             break;
                         default:
@@ -255,18 +255,19 @@ void PlayPrimaryVideoChan0::execute(NancyEngine *engine) {
             
             if (pickedResponse != -1) {
                 // Set response's event flag, if any
-                engine->scene->setEventFlag(responses[pickedResponse].flagDesc.label, responses[pickedResponse].flagDesc.flag);
+                engine->scene->setEventFlag(responses[pickedResponse].flagDesc);
             }
 
             if (!engine->sound->isSoundPlaying(responseGenericSound.channelID)) {
-                _engine->sound->stopSound(responseGenericSound.channelID);
+                engine->sound->stopSound(responseGenericSound.channelID);
                 if (shouldPopScene) {
                     // Exit dialogue
                     engine->scene->popScene();
                 } else {
                     // Continue to next dialogue scene
-                    SceneChange::execute(engine);
+                    engine->scene->changeScene(sceneChange);
                 }
+                isDone = true;
             }
 
             break;
diff --git a/engines/nancy/action/primaryvideo.h b/engines/nancy/action/primaryvideo.h
index 231a18b3fe..ca6465c934 100644
--- a/engines/nancy/action/primaryvideo.h
+++ b/engines/nancy/action/primaryvideo.h
@@ -23,10 +23,11 @@
 #ifndef NANCY_ACTION_PRIMARYVIDEO_H
 #define NANCY_ACTION_PRIMARYVIDEO_H
 
-#include "engines/nancy/action/recordtypes.h"
+#include "engines/nancy/action/actionrecord.h"
 #include "engines/nancy/renderobject.h"
 
 #include "engines/nancy/video.h"
+#include "engines/nancy/commontypes.h"
 
 #include "common/str.h"
 #include "common/array.h"
@@ -38,7 +39,7 @@ class NancyEngine;
 namespace Action {
 
 // ActionRecord subclass that handles all NPC dialog and nancy1's intro video
-class PlayPrimaryVideoChan0 : public SceneChange, public RenderObject {
+class PlayPrimaryVideoChan0 : public ActionRecord, public RenderObject {
 
 struct ConditionFlags {
     byte unknown[5];
@@ -48,8 +49,8 @@ struct ResponseStruct {
     Common::Array<ConditionFlags> conditionFlags; // 0x02
     Common::String text; // 0x06
     Common::String soundName; // 0x196
-    SceneChangeDesc sceneChange; // 0x1A0
-    FlagDesc flagDesc; // 0x1A8
+    SceneChangeDescription sceneChange; // 0x1A0
+    EventFlagDescription flagDesc; // 0x1A8
 };
 
 struct FlagsStruct {
@@ -57,8 +58,7 @@ struct FlagsStruct {
     Common::Array<ConditionFlags> conditionFlags;
 
     ConditionType type;
-    int16 label;
-    NancyFlag flag;
+    EventFlagDescription flagDesc;
 };
 
 public:
@@ -81,14 +81,14 @@ public:
     // _screenPosition 0x2D
     Common::String text; // 0x3D
 
-    SoundManager::SoundDescription sound; // 0x619
-    SoundManager::SoundDescription responseGenericSound; // 0x63B
+    SoundDescription sound; // 0x619
+    SoundDescription responseGenericSound; // 0x63B
 
     byte conditionalResponseCharacterID; // 0x65E
     byte goodbyeResponseCharacterID; // 0x65F
     byte numSceneChanges; // 0x660, not sure
     bool shouldPopScene; // 0x661
-    // SceneChange data is at 0x662
+    SceneChangeDescription sceneChange; // 0x662
 
     Common::Array<ResponseStruct> responses;
     Common::Array<FlagsStruct> flagsStructs;
diff --git a/engines/nancy/action/recordtypes.cpp b/engines/nancy/action/recordtypes.cpp
index 9e6912e67e..3b0955c3bc 100644
--- a/engines/nancy/action/recordtypes.cpp
+++ b/engines/nancy/action/recordtypes.cpp
@@ -20,7 +20,7 @@
  *
  */
 
-#include "engines/nancy/action/recordtypes.h"
+#include "engines/nancy/action/actionrecord.h"
 #include "engines/nancy/action/actionmanager.h"
 
 #include "engines/nancy/state/scene.h"
@@ -40,44 +40,13 @@
 namespace Nancy {
 namespace Action {
 
-void HotspotDesc::readData(Common::SeekableReadStream &stream) {
-    frameID = stream.readUint16LE();
-    readRect(stream, coords);
-}
-
-void BitmapDesc::readData(Common::SeekableReadStream &stream) {
-    frameID = stream.readUint16LE();
-    readRect(stream, src);
-    readRect(stream, dest);
-}
-
-void EventFlagsDesc::readData(Common::SeekableReadStream &stream) {
-    for (uint i = 0; i < 10; ++i) {
-        descs[i].label = stream.readSint16LE();
-        descs[i].flag = (NancyFlag)stream.readUint16LE();
-    }
-}
-
-void EventFlagsDesc::execute(NancyEngine *engine) {
-    for (uint i = 0; i < 10; ++i) {
-        engine->scene->setEventFlag(descs[i].label, descs[i].flag);
-    }
-}
-
-void SceneChangeDesc::readData(Common::SeekableReadStream &stream) {
-    sceneID = stream.readUint16LE();
-    frameID = stream.readUint16LE();
-    verticalOffset = stream.readUint16LE();
-    doNotStartSound = (bool)(stream.readUint16LE());
-}
-
 uint16 SceneChange::readData(Common::SeekableReadStream &stream) {
     sceneChange.readData(stream);
     return 8;
 }
 
 void SceneChange::execute(NancyEngine *engine) {
-    engine->scene->changeScene(sceneChange.sceneID, sceneChange.frameID, sceneChange.verticalOffset, sceneChange.doNotStartSound);
+    engine->scene->changeScene(sceneChange);
     isDone = true;
 }
 
@@ -86,8 +55,8 @@ uint16 HotMultiframeSceneChange::readData(Common::SeekableReadStream &stream) {
     uint16 numHotspots = stream.readUint16LE();
 
     for (uint i = 0; i < numHotspots; ++i) {
-        hotspots.push_back(HotspotDesc());
-        HotspotDesc &newDesc = hotspots[i];
+        hotspots.push_back(HotspotDescription());
+        HotspotDescription &newDesc = hotspots[i];
         newDesc.readData(stream);
     }
 
@@ -217,7 +186,7 @@ void MapCallHot1Fr::execute(NancyEngine *engine) {
 uint16 MapCallHotMultiframe::readData(Common::SeekableReadStream &stream) {
     uint16 numDescs = stream.readUint16LE();
     for (uint i = 0; i < numDescs; ++i) {
-        hotspots.push_back(HotspotDesc());
+        hotspots.push_back(HotspotDescription());
         hotspots[i].readData(stream);
     }
 
@@ -330,8 +299,8 @@ uint16 EventFlagsMultiHS::readData(Common::SeekableReadStream &stream) {
     uint16 returnSize = EventFlags::readData(stream);
     uint16 numHotspots = stream.readUint16LE();
     for (uint16 i = 0; i < numHotspots; ++i) {
-        hotspots.push_back(HotspotDesc());
-        HotspotDesc &newDesc = hotspots[i];
+        hotspots.push_back(HotspotDescription());
+        HotspotDescription &newDesc = hotspots[i];
         newDesc.readData(stream);
     }
     returnSize += numHotspots * 0x12 + 0x2;
@@ -400,7 +369,7 @@ uint16 DifficultyLevel::readData(Common::SeekableReadStream &stream) {
 
 void DifficultyLevel::execute(NancyEngine *engine) {
     engine->scene->setDifficulty(difficulty);
-    engine->scene->setEventFlag(flag.label, flag.flag);
+    engine->scene->setEventFlag(flag);
     isDone = true;
 }
 
@@ -425,7 +394,7 @@ uint16 ShowInventoryItem::readData(Common::SeekableReadStream &stream) {
     uint16 numFrames = stream.readUint16LE();
 
     for (uint i = 0; i < numFrames; ++i) {
-        bitmaps.push_back(BitmapDesc());
+        bitmaps.push_back(BitmapDescription());
         bitmaps[i].readData(stream);
     }
 
@@ -476,8 +445,8 @@ void ShowInventoryItem::execute(NancyEngine *engine) {
 }
 
 uint16 PlayDigiSoundAndDie::readData(Common::SeekableReadStream &stream) {
-    sound.read(stream, SoundManager::SoundDescription::kDIGI);
-    SceneChange::readData(stream);
+    sound.read(stream, SoundDescription::kDIGI);
+    sceneChange.readData(stream);
     flagOnTrigger.label = stream.readSint16LE();
     flagOnTrigger.flag = (NancyFlag)stream.readByte();
     stream.skip(1);
@@ -498,11 +467,13 @@ void PlayDigiSoundAndDie::execute(NancyEngine *engine) {
             break;
         case kActionTrigger:
             if (sceneChange.sceneID != 9999) {
-                SceneChange::execute(engine);
+                engine->scene->changeScene(sceneChange);
             }
             
-            engine->scene->setEventFlag(flagOnTrigger.label, flagOnTrigger.flag);
+            engine->scene->setEventFlag(flagOnTrigger);
             engine->sound->stopSound(sound.channelID);
+
+            isDone = true;
             break;
     }
 }
@@ -521,7 +492,7 @@ uint16 PlaySoundMultiHS::readData(Common::SeekableReadStream &stream) {
 
 uint16 HintSystem::readData(Common::SeekableReadStream &stream) {
     characterID = stream.readByte();
-    genericSound.read(stream, SoundManager::SoundDescription::kNormal);
+    genericSound.read(stream, SoundDescription::kNormal);
     return 0x23;
 }
 
@@ -553,9 +524,7 @@ void HintSystem::execute(Nancy::NancyEngine *engine) {
             engine->scene->useHint(hintID, hintWeight);
             engine->scene->getTextbox().clear();
 
-            if (sceneChange.sceneID != 9999) {
-                engine->scene->changeScene(sceneChange.sceneID, sceneChange.frameID, sceneChange.verticalOffset, sceneChange.doNotStartSound);
-            }
+            engine->scene->changeScene(sceneChange);
 
             isDone = true;
             break;
diff --git a/engines/nancy/action/recordtypes.h b/engines/nancy/action/recordtypes.h
index 9b5a5e4478..6a11abcbcd 100644
--- a/engines/nancy/action/recordtypes.h
+++ b/engines/nancy/action/recordtypes.h
@@ -27,7 +27,7 @@
 #include "engines/nancy/commontypes.h"
 #include "engines/nancy/renderobject.h"
 
-#include "engines/nancy/sound.h"
+#include "engines/nancy/commontypes.h"
 
 #include "common/stream.h"
 #include "common/array.h"
@@ -39,53 +39,12 @@ class NancyEngine;
 
 namespace Action {
 
-// Describes a hotspot
-struct HotspotDesc {
-    uint16 frameID = 0;
-    Common::Rect coords;
-
-    void readData(Common::SeekableReadStream &stream);
-};
-
-// Describes a single bitmap draw
-struct BitmapDesc {
-    uint16 frameID = 0;
-    Common::Rect src;
-    Common::Rect dest;
-
-    void readData(Common::SeekableReadStream &stream);
-};
-
-// Describes a single event flag change or comparison
-struct FlagDesc {
-    int16 label;
-    NancyFlag flag;
-};
-
-// Describes 10 event flag changes to be executed when an action is triggered
-struct EventFlagsDesc {
-    FlagDesc descs[10];
-
-    void readData(Common::SeekableReadStream &stream);
-    void execute(Nancy::NancyEngine *engine);
-};
-
-// Describes a scene transition
-struct SceneChangeDesc {
-    uint16 sceneID = 0;
-    uint16 frameID = 0;
-    uint16 verticalOffset = 0;
-    bool doNotStartSound = false;
-
-    void readData(Common::SeekableReadStream &stream);
-};
-
 class SceneChange : public ActionRecord {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
     virtual void execute(Nancy::NancyEngine *engine) override;
 
-    SceneChangeDesc sceneChange;
+    SceneChangeDescription sceneChange;
 };
 
 class HotMultiframeSceneChange : public SceneChange {
@@ -93,7 +52,7 @@ public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
     virtual void execute(Nancy::NancyEngine *engine) override;
 
-    Common::Array<HotspotDesc> hotspots;
+    Common::Array<HotspotDescription> hotspots;
 };
 
 class Hot1FrSceneChange : public SceneChange {
@@ -101,7 +60,7 @@ public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
     virtual void execute(Nancy::NancyEngine *engine) override;
 
-    HotspotDesc hotspotDesc;
+    HotspotDescription hotspotDesc;
 };
 
 class Hot1FrExitSceneChange : public Hot1FrSceneChange {
@@ -146,7 +105,7 @@ public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
     virtual void execute(Nancy::NancyEngine *engine) override;
 
-    HotspotDesc hotspotDesc;
+    HotspotDescription hotspotDesc;
 };
 
 class MapCallHotMultiframe : public MapCall {
@@ -154,7 +113,7 @@ public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
     virtual void execute(Nancy::NancyEngine *engine) override;
 
-    Common::Array<HotspotDesc> hotspots;
+    Common::Array<HotspotDescription> hotspots;
 };
 
 class MapLocationAccess : public ActionRecord {
@@ -234,7 +193,7 @@ public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
     virtual void execute(Nancy::NancyEngine *engine) override;
 
-    EventFlagsDesc flags;
+    MultiEventFlagDescription flags;
 };
 
 class EventFlagsMultiHS : public EventFlags {
@@ -242,7 +201,7 @@ public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
     virtual void execute(Nancy::NancyEngine *engine) override;
 
-    Common::Array<HotspotDesc> hotspots;
+    Common::Array<HotspotDescription> hotspots;
 };
 
 class LoseGame : public ActionRecord {
@@ -289,7 +248,7 @@ public:
     virtual void execute(Nancy::NancyEngine *engine) override;
 
     uint16 difficulty = 0;
-    FlagDesc flag;
+    EventFlagDescription flag;
 };
 
 class ShowInventoryItem : public ActionRecord, public RenderObject {
@@ -304,7 +263,7 @@ public:
  
     uint16 objectID = 0;
     Common::String imageName;
-    Common::Array<BitmapDesc> bitmaps;
+    Common::Array<BitmapDescription> bitmaps;
 
     int16 drawnFrameID = -1;
     Graphics::ManagedSurface _fullSurface;
@@ -315,15 +274,15 @@ protected:
     virtual bool isViewportRelative() const override { return true; }
 };
 
-class PlayDigiSoundAndDie : public SceneChange {
+class PlayDigiSoundAndDie : public ActionRecord {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
     virtual void execute(Nancy::NancyEngine *engine) override;
     // TODO subclass into Play and Stop (?)
 
-    SoundManager::SoundDescription sound;
-    // SceneChange 0x1E
-    FlagDesc flagOnTrigger;
+    SoundDescription sound;
+    SceneChangeDescription sceneChange;
+    EventFlagDescription flagOnTrigger;
 };
 
 class PlaySoundPanFrameAnchorAndDie : public ActionRecord {
@@ -342,10 +301,10 @@ public:
     virtual void execute(Nancy::NancyEngine *engine) override;
 
     byte characterID; // 0x00
-    SoundManager::SoundDescription genericSound; // 0x01
+    SoundDescription genericSound; // 0x01
 
     Common::String text;
-    SceneChangeDesc sceneChange;
+    SceneChangeDescription sceneChange;
     uint16 hintID;
     int16 hintWeight;
 
diff --git a/engines/nancy/action/responses.cpp b/engines/nancy/action/responses.cpp
index 62969f0281..cd68be3faa 100644
--- a/engines/nancy/action/responses.cpp
+++ b/engines/nancy/action/responses.cpp
@@ -37,7 +37,7 @@ struct ConditionalResponseDesc {
     byte characterID; // 0: Daryl, 1: Connie, 2: Hal, 3: Hulk
     uint fileOffset;
     uint16 sceneID;
-    FlagDesc conditions[7];
+    EventFlagDescription conditions[7];
 };
 
 struct GoodbyeDesc {
@@ -49,8 +49,8 @@ struct GoodbyeDesc {
 struct HintDesc {
     byte characterID; // 0: Ned, 1: Bess, 2: George
     byte hintID;
-    FlagDesc flagConditions[4];
-    FlagDesc inventoryCondition[2];
+    EventFlagDescription flagConditions[4];
+    EventFlagDescription inventoryCondition[2];
 };
 
 static const uint nancy1ResponseBaseFileOffset = 0xB1FE0; // TODO there could be more than one version of the exe
diff --git a/engines/nancy/action/rotatinglockpuzzle.cpp b/engines/nancy/action/rotatinglockpuzzle.cpp
index 12cdd20e79..c10647b8b2 100644
--- a/engines/nancy/action/rotatinglockpuzzle.cpp
+++ b/engines/nancy/action/rotatinglockpuzzle.cpp
@@ -26,6 +26,7 @@
 #include "engines/nancy/nancy.h"
 #include "engines/nancy/resource.h"
 #include "engines/nancy/graphics.h"
+#include "engines/nancy/sound.h"
 #include "engines/nancy/state/scene.h"
 
 #include "common/random.h"
@@ -89,13 +90,13 @@ uint16 RotatingLockPuzzle::readData(Common::SeekableReadStream &stream) {
 
     stream.skip(8 - numDials);
 
-    clickSound.read(stream, SoundManager::SoundDescription::kNormal);
+    clickSound.read(stream, SoundDescription::kNormal);
     solveExitScene.readData(stream);
     stream.skip(2); // shouldStopRendering, useless
     flagOnSolve.label = stream.readSint16LE();
     flagOnSolve.flag = (NancyFlag)stream.readByte();
     solveSoundDelay = stream.readUint16LE();
-    solveSound.read(stream, SoundManager::SoundDescription::kNormal);
+    solveSound.read(stream, SoundDescription::kNormal);
     exitScene.readData(stream);
     stream.skip(2); // shouldStopRendering, useless
     flagOnExit.label = stream.readSint16LE();
@@ -129,7 +130,7 @@ void RotatingLockPuzzle::execute(Nancy::NancyEngine *engine) {
                         }
                     }
 
-                    _engine->scene->setEventFlag(flagOnSolve.label, flagOnSolve.flag);
+                    _engine->scene->setEventFlag(flagOnSolve);
                     solveSoundPlayTime = _engine->getTotalPlayTime() + solveSoundDelay * 1000;
                     solveState = kPlaySound;
                     // fall through
@@ -154,14 +155,10 @@ void RotatingLockPuzzle::execute(Nancy::NancyEngine *engine) {
             _engine->sound->stopSound(solveSound.channelID);
 
             if (solveState == kNotSolved) {
-                if (exitScene.sceneID != 9999) {
-                    _engine->scene->changeScene(exitScene.sceneID, exitScene.frameID, exitScene.verticalOffset, exitScene.doNotStartSound);
-                    _engine->scene->setEventFlag(flagOnExit.label, flagOnExit.flag);
-                }
+                _engine->scene->changeScene(exitScene);
+                _engine->scene->setEventFlag(flagOnExit);
             } else {
-                if (solveExitScene.sceneID != 9999) {
-                    _engine->scene->changeScene(solveExitScene.sceneID, solveExitScene.frameID, solveExitScene.verticalOffset, solveExitScene.doNotStartSound);
-                }
+                _engine->scene->changeScene(solveExitScene);
             }
 
             isDone = true;
diff --git a/engines/nancy/action/rotatinglockpuzzle.h b/engines/nancy/action/rotatinglockpuzzle.h
index f6c05adb26..352ed0a1f7 100644
--- a/engines/nancy/action/rotatinglockpuzzle.h
+++ b/engines/nancy/action/rotatinglockpuzzle.h
@@ -23,10 +23,10 @@
 #ifndef NANCY_ACTION_ROTATINGLOCKPUZZLE_H
 #define NANCY_ACTION_ROTATINGLOCKPUZZLE_H
 
-#include "engines/nancy/action/recordtypes.h"
+#include "engines/nancy/action/actionrecord.h"
 #include "engines/nancy/renderobject.h"
 
-#include "engines/nancy/sound.h"
+#include "engines/nancy/commontypes.h"
 #include "engines/nancy/time.h"
 
 #include "common/str.h"
@@ -55,13 +55,13 @@ public:
     Common::Array<Common::Rect> upHotspots; // 0x12C, 8
     Common::Array<Common::Rect> downHotspots; // 0x1AC, 8
     Common::Array<byte> correctSequence; // 0x22C
-    Nancy::SoundManager::SoundDescription clickSound; // 0x234, kNormal
-    SceneChangeDesc solveExitScene; // 0x256
-    FlagDesc flagOnSolve; // 0x260
+    Nancy::SoundDescription clickSound; // 0x234, kNormal
+    SceneChangeDescription solveExitScene; // 0x256
+    EventFlagDescription flagOnSolve; // 0x260
     uint16 solveSoundDelay; // 0x263
-    Nancy::SoundManager::SoundDescription solveSound; // 0x265
-    SceneChangeDesc exitScene; // 0x287
-    FlagDesc flagOnExit; // 0x291
+    Nancy::SoundDescription solveSound; // 0x265
+    SceneChangeDescription exitScene; // 0x287
+    EventFlagDescription flagOnExit; // 0x291
     Common::Rect exitHotspot; // 0x294
 
     SolveState solveState = kNotSolved;
diff --git a/engines/nancy/action/secondaryvideo.cpp b/engines/nancy/action/secondaryvideo.cpp
index 128e2d6e32..ecb94bffa5 100644
--- a/engines/nancy/action/secondaryvideo.cpp
+++ b/engines/nancy/action/secondaryvideo.cpp
@@ -32,6 +32,7 @@
 #include "engines/nancy/cursor.h"
 #include "engines/nancy/input.h"
 #include "engines/nancy/graphics.h"
+#include "engines/nancy/sound.h"
 
 #include "common/system.h"
 
@@ -147,7 +148,7 @@ uint16 PlaySecondaryVideo::readData(Common::SeekableReadStream &stream) {
     onHoverLastFrame = stream.readUint16LE();
     onHoverEndFirstFrame = stream.readUint16LE();
     onHoverEndLastFrame = stream.readUint16LE();
-    SceneChange::readData(stream);
+    sceneChange.readData(stream);
     stream.skip(1);
 
     uint16 numVideoDescs = stream.readUint16LE();
@@ -199,7 +200,8 @@ void PlaySecondaryVideo::execute(NancyEngine *engine) {
         }
         case kActionTrigger:
             engine->scene->pushScene();
-            SceneChange::execute(engine);
+            engine->scene->changeScene(sceneChange);
+            isDone = true;
             break;
     }
 }
@@ -217,8 +219,8 @@ uint16 PlaySecondaryMovie::readData(Common::SeekableReadStream &stream) {
     }
 
     triggerFlags.readData(stream);
-    sound.read(stream, SoundManager::SoundDescription::kNormal);
-    SceneChange::readData(stream);
+    sound.read(stream, SoundDescription::kNormal);
+    sceneChange.readData(stream);
 
     uint16 numVideoDescs = stream.readUint16LE();
     for (uint i = 0; i < numVideoDescs; ++i) {
@@ -262,7 +264,7 @@ void PlaySecondaryMovie::updateGraphics() {
         // Set flag if not drawing new frame
         for (auto f : frameFlags) {
             if (_decoder.getCurFrame() == f.frameID) {
-                _engine->scene->setEventFlag(f.flagDesc.label, f.flagDesc.flag);
+                _engine->scene->setEventFlag(f.flagDesc);
             }
         }
     }
@@ -310,7 +312,8 @@ void PlaySecondaryMovie::execute(NancyEngine *engine) {
         }
         case kActionTrigger:
             triggerFlags.execute(engine);
-            SceneChange::execute(engine);
+            engine->scene->changeScene(sceneChange);
+            isDone = true;
             break;
     }
 }
diff --git a/engines/nancy/action/secondaryvideo.h b/engines/nancy/action/secondaryvideo.h
index b18f85632e..903879ba8f 100644
--- a/engines/nancy/action/secondaryvideo.h
+++ b/engines/nancy/action/secondaryvideo.h
@@ -23,10 +23,11 @@
 #ifndef NANCY_ACTION_SECONDARYVIDEO_H
 #define NANCY_ACTION_SECONDARYVIDEO_H
 
-#include "engines/nancy/action/recordtypes.h"
+#include "engines/nancy/action/actionrecord.h"
 #include "engines/nancy/renderobject.h"
 
 #include "engines/nancy/video.h"
+#include "engines/nancy/commontypes.h"
 
 #include "common/str.h"
 #include "common/array.h"
@@ -45,7 +46,7 @@ struct SecondaryVideoDesc {
 
 // ActionRecord that shows NPC animations outside of dialogue. Supports
 // different animations depending on whether the NPC is hovered by the mouse
-class PlaySecondaryVideo : public SceneChange, public RenderObject {
+class PlaySecondaryVideo : public ActionRecord, public RenderObject {
 public:
     enum HoverState { kNoHover, kHover, kEndHover };
 
@@ -68,7 +69,7 @@ public:
     uint16 onHoverLastFrame = 0; // 0x24
     uint16 onHoverEndFirstFrame = 0; // 0x26
     uint16 onHoverEndLastFrame = 0; // 0x28
-    // SceneChange data is at 0x2A
+    SceneChangeDescription sceneChange; // 0x2A
     // unknown byte
     Common::Array<SecondaryVideoDesc> videoDescs; // 0x35
 
@@ -84,11 +85,11 @@ protected:
     bool _isHovered = false;
 };
 
-class PlaySecondaryMovie : public SceneChange, public RenderObject {
+class PlaySecondaryMovie : public ActionRecord, public RenderObject {
 public:
     struct FlagAtFrame {
         int16 frameID;
-        FlagDesc flagDesc;
+        EventFlagDescription flagDesc;
     };
 
     PlaySecondaryMovie(RenderObject &redrawFrom) : RenderObject(redrawFrom) {}
@@ -104,11 +105,11 @@ public:
     Common::String videoName; // 0x00
 
     FlagAtFrame frameFlags[15]; // 0x26
-    EventFlagsDesc triggerFlags; // 0x80
+    MultiEventFlagDescription triggerFlags; // 0x80
 
-    SoundManager::SoundDescription sound; // 0xA8
+    SoundDescription sound; // 0xA8
 
-    // SceneChange data at 0xCA
+    SceneChangeDescription sceneChange; // 0xCA
     Common::Array<SecondaryVideoDesc> videoDescs; // 0xD4
 
 protected:
diff --git a/engines/nancy/action/sliderpuzzle.cpp b/engines/nancy/action/sliderpuzzle.cpp
index 69d2dd04e0..de1886d440 100644
--- a/engines/nancy/action/sliderpuzzle.cpp
+++ b/engines/nancy/action/sliderpuzzle.cpp
@@ -92,12 +92,12 @@ uint16 SliderPuzzle::readData(Common::SeekableReadStream &stream) {
 
     stream.skip((6 - height) * 6 * 2);
 
-    clickSound.read(stream, SoundManager::SoundDescription::kNormal);
+    clickSound.read(stream, SoundDescription::kNormal);
     solveExitScene.readData(stream);
     stream.skip(2);
     flagOnSolve.label = stream.readSint16LE();
     flagOnSolve.flag = (NancyFlag)stream.readByte();
-    solveSound.read(stream, SoundManager::SoundDescription::kNormal);
+    solveSound.read(stream, SoundDescription::kNormal);
     exitScene.readData(stream);
     stream.skip(2);
     flagOnExit.label = stream.readSint16LE();
@@ -165,18 +165,12 @@ void SliderPuzzle::execute(Nancy::NancyEngine *engine) {
         case kActionTrigger:
             switch (solveState) {
                 case kNotSolved:
-                    if (exitScene.sceneID != 9999) {
-                        engine->scene->changeScene(exitScene.sceneID, exitScene.frameID, exitScene.verticalOffset, exitScene.doNotStartSound);
-                    }
-
-                    engine->scene->setEventFlag(flagOnExit.label, flagOnExit.flag);
+                    engine->scene->changeScene(exitScene);
+                    engine->scene->setEventFlag(flagOnExit);
                     break;
                 case kWaitForSound:
-                    if (exitScene.sceneID != 9999) {
-                        engine->scene->changeScene(solveExitScene.sceneID, solveExitScene.frameID, solveExitScene.verticalOffset, solveExitScene.doNotStartSound);
-                    }
-
-                    engine->scene->setEventFlag(flagOnSolve.label, flagOnSolve.flag);
+                    engine->scene->changeScene(solveExitScene);
+                    engine->scene->setEventFlag(flagOnSolve);
                     playerHasTriedPuzzle = false;
                     break;
             }
diff --git a/engines/nancy/action/sliderpuzzle.h b/engines/nancy/action/sliderpuzzle.h
index 2c36a0494d..2ee2fe5c22 100644
--- a/engines/nancy/action/sliderpuzzle.h
+++ b/engines/nancy/action/sliderpuzzle.h
@@ -23,10 +23,10 @@
 #ifndef NANCY_ACTION_SLIDERPUZZLE_H
 #define NANCY_ACTION_SLIDERPUZZLE_H
 
-#include "engines/nancy/action/recordtypes.h"
+#include "engines/nancy/action/actionrecord.h"
 #include "engines/nancy/renderobject.h"
 
-#include "engines/nancy/sound.h"
+#include "engines/nancy/commontypes.h"
 
 #include "common/str.h"
 #include "common/array.h"
@@ -55,12 +55,12 @@ public:
     Common::Array<Common::Array<Common::Rect>> srcRects; // 0x0E, size 0x240
     Common::Array<Common::Array<Common::Rect>> destRects; // 0x24E, size 0x240
     Common::Array<Common::Array<int16>> correctTileOrder; // 0x48E, size 0x48
-    SoundManager::SoundDescription clickSound; // 0x4D6
-    SceneChangeDesc solveExitScene; // 0x4F8
-    FlagDesc flagOnSolve; // 0x502
-    SoundManager::SoundDescription solveSound; // 0x505
-    SceneChangeDesc exitScene; // 0x527
-    FlagDesc flagOnExit; // 0x531
+    SoundDescription clickSound; // 0x4D6
+    SceneChangeDescription solveExitScene; // 0x4F8
+    EventFlagDescription flagOnSolve; // 0x502
+    SoundDescription solveSound; // 0x505
+    SceneChangeDescription exitScene; // 0x527
+    EventFlagDescription flagOnExit; // 0x531
     Common::Rect exitHotspot; // 0x534
 
     SolveState solveState = kNotSolved;
diff --git a/engines/nancy/action/staticbitmapanim.cpp b/engines/nancy/action/staticbitmapanim.cpp
index 9de6894ec3..9ea3cd29f7 100644
--- a/engines/nancy/action/staticbitmapanim.cpp
+++ b/engines/nancy/action/staticbitmapanim.cpp
@@ -22,6 +22,7 @@
 
 #include "engines/nancy/action/staticbitmapanim.h"
 
+#include "engines/nancy/sound.h"
 #include "engines/nancy/state/scene.h"
 
 #include "engines/nancy/nancy.h"
@@ -62,9 +63,9 @@ uint16 PlayIntStaticBitmapAnimation::readData(Common::SeekableReadStream &stream
     zOrder = stream.readUint16LE();
     updateCondition.label = stream.readSint16LE();
     updateCondition.flag = (NancyFlag)stream.readUint16LE();
-    SceneChange::readData(stream);
+    sceneChange.readData(stream);
     triggerFlags.readData(stream);
-    sound.read(stream, SoundManager::SoundDescription::kNormal);
+    sound.read(stream, SoundDescription::kNormal);
     uint numFrames = stream.readUint16LE();
 
     for (uint i = firstFrame; i <= loopLastFrame; ++i) {
@@ -73,8 +74,8 @@ uint16 PlayIntStaticBitmapAnimation::readData(Common::SeekableReadStream &stream
     }
 
     for (uint i = 0; i < numFrames; ++i) {
-        bitmaps.push_back(BitmapDesc());
-        BitmapDesc &rects = bitmaps[i];
+        bitmaps.push_back(BitmapDescription());
+        BitmapDescription &rects = bitmaps[i];
         rects.frameID = stream.readUint16LE();
         readRect(stream, rects.src);
         readRect(stream, rects.dest);
@@ -155,7 +156,8 @@ void PlayIntStaticBitmapAnimation::execute(NancyEngine *engine) {
         case kActionTrigger:
             triggerFlags.execute(engine);
             if (doNotChangeScene == kFalse) {
-                SceneChange::execute(engine);
+                engine->scene->changeScene(sceneChange);
+                isDone = true;
             }
             break;
     }
diff --git a/engines/nancy/action/staticbitmapanim.h b/engines/nancy/action/staticbitmapanim.h
index 49c1bff434..40eb4c15ac 100644
--- a/engines/nancy/action/staticbitmapanim.h
+++ b/engines/nancy/action/staticbitmapanim.h
@@ -23,10 +23,10 @@
 #ifndef NANCY_ACTION_STATICBITMAPANIM_H
 #define NANCY_ACTION_STATICBITMAPANIM_H
 
-#include "engines/nancy/action/recordtypes.h"
+#include "engines/nancy/action/actionrecord.h"
 #include "engines/nancy/renderobject.h"
 
-#include "engines/nancy/sound.h"
+#include "engines/nancy/commontypes.h"
 
 #include "common/str.h"
 #include "common/array.h"
@@ -36,7 +36,7 @@ namespace Action {
 
 // ActionRecord subclass describing a short "flipbook" animation from a single bitmap
 // Can also play sound, but this has not yet been implemented
-class PlayIntStaticBitmapAnimation : public SceneChange, public RenderObject {
+class PlayIntStaticBitmapAnimation : public ActionRecord, public RenderObject {
 public:
     PlayIntStaticBitmapAnimation(RenderObject &redrawFrom) : RenderObject(redrawFrom) {}
     virtual ~PlayIntStaticBitmapAnimation() { _fullSurface.free(); }
@@ -57,17 +57,17 @@ public:
     uint16 loopLastFrame; // 0x18
     Time frameTime;
     uint16 zOrder; // 0x1C
-    FlagDesc updateCondition; // 0x1E
-    // SceneChange
-    EventFlagsDesc triggerFlags; // 0x2A
+    EventFlagDescription updateCondition; // 0x1E
+    SceneChangeDescription sceneChange;
+    MultiEventFlagDescription triggerFlags; // 0x2A
 
-    Nancy::SoundManager::SoundDescription sound; // 0x52
+    Nancy::SoundDescription sound; // 0x52
 
     // Describes a single frame in this animation
     Common::Array<Common::Rect> srcRects;
     // Describes how the animation will be displayed on a single
     // frame of the viewport
-    Common::Array<BitmapDesc> bitmaps;
+    Common::Array<BitmapDescription> bitmaps;
 
     int16 currentFrame = -1;
     int16 currentViewportFrame = -1;
diff --git a/engines/nancy/action/telephone.cpp b/engines/nancy/action/telephone.cpp
index a336d99931..16af425e79 100644
--- a/engines/nancy/action/telephone.cpp
+++ b/engines/nancy/action/telephone.cpp
@@ -68,12 +68,12 @@ uint16 Telephone::readData(Common::SeekableReadStream &stream) {
         }
     }
 
-    genericDialogueSound.read(stream, SoundManager::SoundDescription::kNormal);
-    genericButtonSound.read(stream, SoundManager::SoundDescription::kNormal);
-    ringSound.read(stream, SoundManager::SoundDescription::kNormal);
-    dialToneSound.read(stream, SoundManager::SoundDescription::kNormal);
-    dialAgainSound.read(stream, SoundManager::SoundDescription::kNormal);
-    hangUpSound.read(stream, SoundManager::SoundDescription::kNormal);
+    genericDialogueSound.read(stream, SoundDescription::kNormal);
+    genericButtonSound.read(stream, SoundDescription::kNormal);
+    ringSound.read(stream, SoundDescription::kNormal);
+    dialToneSound.read(stream, SoundDescription::kNormal);
+    dialAgainSound.read(stream, SoundDescription::kNormal);
+    hangUpSound.read(stream, SoundDescription::kNormal);
 
     for (uint i = 0; i < 12; ++i) {
         stream.read(buf, 10);
@@ -219,33 +219,23 @@ void Telephone::execute(NancyEngine *engine) {
         case kActionTrigger:
             switch (callState) {
                 case kBadNumber:
-                    if (reloadScene.sceneID != 9999) {
-                        engine->scene->changeScene(reloadScene.sceneID, reloadScene.frameID, reloadScene.verticalOffset, reloadScene.doNotStartSound);
-                    }
-
+                    engine->scene->changeScene(reloadScene);
                     calledNumber.clear();
-                    _engine->scene->setEventFlag(flagOnReload.label, flagOnReload.flag);
+                    _engine->scene->setEventFlag(flagOnReload);
                     state = kRun;
                     callState = kWaiting;
 
                     break;
                 case kCall: {
                     PhoneCall &call = calls[selected];
-
-                    if (call.sceneChange.sceneID != 9999) {
-                        _engine->scene->changeScene(call.sceneChange.sceneID, call.sceneChange.frameID, call.sceneChange.verticalOffset, call.sceneChange.doNotStartSound);
-                    }
-
-                    _engine->scene->setEventFlag(call.flag.label, call.flag.flag);
+                    _engine->scene->changeScene(call.sceneChange);
+                    _engine->scene->setEventFlag(call.flag);
 
                     break;
                 }
                 case kHangUp:
-                    if (exitScene.sceneID != 9999) {
-                        _engine->scene->changeScene(exitScene.sceneID, exitScene.frameID, exitScene.verticalOffset, exitScene.doNotStartSound);
-                    }
-
-                    _engine->scene->setEventFlag(flagOnExit.label, flagOnExit.flag);
+                    _engine->scene->changeScene(exitScene);
+                    _engine->scene->setEventFlag(flagOnExit);
                     
                     break;
                 default:
diff --git a/engines/nancy/action/telephone.h b/engines/nancy/action/telephone.h
index 29dd03077e..c05aca6348 100644
--- a/engines/nancy/action/telephone.h
+++ b/engines/nancy/action/telephone.h
@@ -23,10 +23,10 @@
 #ifndef NANCY_ACTION_TELEPHONE_H
 #define NANCY_ACTION_TELEPHONE_H
 
-#include "engines/nancy/action/recordtypes.h"
+#include "engines/nancy/action/actionrecord.h"
 #include "engines/nancy/renderobject.h"
 
-#include "engines/nancy/sound.h"
+#include "engines/nancy/commontypes.h"
 
 #include "common/str.h"
 #include "common/array.h"
@@ -43,9 +43,9 @@ public:
         Common::Array<byte> phoneNumber; // 0x0, 11 bytes
         Common::String soundName; // 0xB
         Common::String text; // 0x15, 0xC8 bytes
-        SceneChangeDesc sceneChange; // 0xDD
+        SceneChangeDescription sceneChange; // 0xDD
         // shouldStopRendering
-        FlagDesc flag; // 0xE7
+        EventFlagDescription flag; // 0xE7
     };
 
     enum CallState { kWaiting, kButtonPress, kRinging, kBadNumber, kCall, kHangUp };
@@ -65,19 +65,19 @@ public:
     Common::String imageName; // 0x00
     Common::Array<Common::Rect> srcRects; // 0xA, 12
     Common::Array<Common::Rect> destRects; // 0xCA, 12
-    SoundManager::SoundDescription genericDialogueSound; // 0x18A
-    SoundManager::SoundDescription genericButtonSound; // 0x1AC
-    SoundManager::SoundDescription ringSound; // 0x1CE
-    SoundManager::SoundDescription dialToneSound; // 0x1F0
-    SoundManager::SoundDescription dialAgainSound; // 0x212
-    SoundManager::SoundDescription hangUpSound; // 0x234
+    SoundDescription genericDialogueSound; // 0x18A
+    SoundDescription genericButtonSound; // 0x1AC
+    SoundDescription ringSound; // 0x1CE
+    SoundDescription dialToneSound; // 0x1F0
+    SoundDescription dialAgainSound; // 0x212
+    SoundDescription hangUpSound; // 0x234
     Common::Array<Common::String> buttonSoundNames; // 0x256, 12 * 0xA
     Common::String addressBookString; // 0x2CE, 0xC8 long
     Common::String dialAgainString; // 0x396
-    SceneChangeDesc reloadScene; // 0x45E
-    FlagDesc flagOnReload; // 0x468 ??
-    SceneChangeDesc exitScene; // 0x46C
-    FlagDesc flagOnExit; // 0x476
+    SceneChangeDescription reloadScene; // 0x45E
+    EventFlagDescription flagOnReload; // 0x468 ??
+    SceneChangeDescription exitScene; // 0x46C
+    EventFlagDescription flagOnExit; // 0x476
     Common::Rect exitHotspot; // 0x47A
     // 0x48A numConvos
     Common::Array<PhoneCall> calls; // 0x48C
diff --git a/engines/nancy/commontypes.cpp b/engines/nancy/commontypes.cpp
new file mode 100644
index 0000000000..da1ddfb0c8
--- /dev/null
+++ b/engines/nancy/commontypes.cpp
@@ -0,0 +1,100 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/nancy/commontypes.h"
+
+#include "engines/nancy/util.h"
+#include "engines/nancy/nancy.h"
+#include "engines/nancy/state/scene.h"
+
+#include "common/stream.h"
+
+namespace Nancy {
+
+void SceneChangeDescription::readData(Common::SeekableReadStream &stream) {
+    sceneID = stream.readUint16LE();
+    frameID = stream.readUint16LE();
+    verticalOffset = stream.readUint16LE();
+    doNotStartSound = (bool)(stream.readUint16LE());
+}
+
+void HotspotDescription::readData(Common::SeekableReadStream &stream) {
+    frameID = stream.readUint16LE();
+    readRect(stream, coords);
+}
+
+void BitmapDescription::readData(Common::SeekableReadStream &stream) {
+    frameID = stream.readUint16LE();
+    readRect(stream, src);
+    readRect(stream, dest);
+}
+
+void MultiEventFlagDescription::readData(Common::SeekableReadStream &stream) {
+    for (uint i = 0; i < 10; ++i) {
+        descs[i].label = stream.readSint16LE();
+        descs[i].flag = (NancyFlag)stream.readUint16LE();
+    }
+}
+
+void MultiEventFlagDescription::execute(NancyEngine *engine) {
+    for (uint i = 0; i < 10; ++i) {
+        engine->scene->setEventFlag(descs[i]);
+    }
+}
+
+void SoundDescription::read(Common::SeekableReadStream &stream, Type type) {
+	char buf[10];
+
+	stream.read(buf, 10);
+	name = buf;
+
+	if (type == SoundDescription::kScene) {
+		stream.skip(4);
+	}
+	channelID = stream.readUint16LE();
+
+	// The difference between these is a couple members found at the same position
+	// whose purpose I don't understand, so for now just skip them
+	switch (type) {
+		case kNormal:
+			stream.skip(8);
+			break;
+		case kMenu:
+			stream.skip(6);	
+			break;
+		case kScene:
+			// fall through
+		case kDIGI:
+			stream.skip(4);
+			break;
+	}
+
+	numLoops = stream.readUint16LE();
+	if (stream.readUint16LE() != 0) { // loop indefinitely
+		numLoops = 0;
+	}
+	stream.skip(2);
+	volume = stream.readUint16LE();
+	stream.skip(6);
+}
+
+} // End of namespace Nancy
diff --git a/engines/nancy/commontypes.h b/engines/nancy/commontypes.h
index 21d816dea7..f6f8052232 100644
--- a/engines/nancy/commontypes.h
+++ b/engines/nancy/commontypes.h
@@ -24,12 +24,73 @@
 #define NANCY_COMMONYPES_H
 
 #include "common/scummsys.h"
+#include "common/rect.h"
+#include "common/str.h"
+
+namespace Common {
+class SeekableReadStream;
+}
 
 namespace Nancy {
 
+class NancyEngine;
+
 enum NancyFlag { kFalse = 1, kTrue = 2 };
 enum MovementDirection : byte { kUp = 1, kDown = 2, kLeft = 4, kRight = 8, kMoveFast = 16 };
 
+// Describes a scene transition
+struct SceneChangeDescription {
+    uint16 sceneID = 0;
+    uint16 frameID = 0;
+    uint16 verticalOffset = 0;
+    bool doNotStartSound = false;
+
+    void readData(Common::SeekableReadStream &stream);
+};
+
+// Describes a single event flag change or comparison
+struct EventFlagDescription {
+    int16 label;
+    NancyFlag flag;
+};
+
+// Describes a hotspot
+struct HotspotDescription {
+    uint16 frameID = 0;
+    Common::Rect coords;
+
+    void readData(Common::SeekableReadStream &stream);
+};
+
+// Describes a single bitmap draw
+struct BitmapDescription {
+    uint16 frameID = 0;
+    Common::Rect src;
+    Common::Rect dest;
+
+    void readData(Common::SeekableReadStream &stream);
+};
+
+// Describes 10 event flag changes to be executed when an action is triggered
+struct MultiEventFlagDescription {
+    EventFlagDescription descs[10];
+
+    void readData(Common::SeekableReadStream &stream);
+    void execute(Nancy::NancyEngine *engine);
+};
+
+// Descrbes a single sound. Combines four different structs found in the data in one
+struct SoundDescription {
+    enum Type { kNormal, kMenu, kDIGI, kScene };
+
+    Common::String name;
+    uint16 channelID;
+    uint16 numLoops;
+    uint16 volume;
+
+    void read(Common::SeekableReadStream &stream, Type type);
+};
+
 } // End of namespace Nancy
 
 #endif // NANCY_COMMONYPES_H
diff --git a/engines/nancy/module.mk b/engines/nancy/module.mk
index 6cc0aef924..888024a3f3 100644
--- a/engines/nancy/module.mk
+++ b/engines/nancy/module.mk
@@ -21,6 +21,7 @@ MODULE_OBJS = \
   state/logo.o \
   state/map.o \
   state/scene.o \
+  commontypes.o \
   console.o \
   cursor.o \
   decompress.o \
diff --git a/engines/nancy/sound.cpp b/engines/nancy/sound.cpp
index 6552eadaa3..1d0b879cf5 100644
--- a/engines/nancy/sound.cpp
+++ b/engines/nancy/sound.cpp
@@ -176,42 +176,6 @@ Audio::SeekableAudioStream *SoundManager::makeHISStream(Common::SeekableReadStre
 		return Audio::makeVorbisStream(subStream, DisposeAfterUse::YES);
 }
 
-void SoundManager::SoundDescription::read(Common::SeekableReadStream &stream, Type type) {
-	char buf[10];
-
-	stream.read(buf, 10);
-	name = buf;
-
-	if (type == SoundDescription::kScene) {
-		stream.skip(4);
-	}
-	channelID = stream.readUint16LE();
-
-	// The difference between these is a couple members found at the same position
-	// whose purpose I don't understand, so for now just skip them
-	switch (type) {
-		case kNormal:
-			stream.skip(8);
-			break;
-		case kMenu:
-			stream.skip(6);	
-			break;
-		case kScene:
-			// fall through
-		case kDIGI:
-			stream.skip(4);
-			break;
-	}
-
-	numLoops = stream.readUint16LE();
-	if (stream.readUint16LE() != 0) { // loop indefinitely
-		numLoops = 0;
-	}
-	stream.skip(2);
-	volume = stream.readUint16LE();
-	stream.skip(6);
-}
-
 SoundManager::SoundManager(NancyEngine *engine) :
 		_engine(engine) {
 	_mixer = _engine->_system->getMixer();
diff --git a/engines/nancy/sound.h b/engines/nancy/sound.h
index 6ea0b895b3..e54cfce4ce 100644
--- a/engines/nancy/sound.h
+++ b/engines/nancy/sound.h
@@ -23,6 +23,8 @@
 #ifndef NANCY_SOUND_H
 #define NANCY_SOUND_H
 
+#include "engines/nancy/commontypes.h"
+
 #include "common/types.h"
 #include "common/str.h"
 
@@ -42,18 +44,6 @@ class NancyEngine;
 
 class SoundManager {
 public:
-    // Combines four different structs in one
-    struct SoundDescription {
-        enum Type { kNormal, kMenu, kDIGI, kScene };
-
-        Common::String name;
-        uint16 channelID;
-        uint16 numLoops;
-        uint16 volume;
-
-        void read(Common::SeekableReadStream &stream, Type type);
-    };
-
     SoundManager(NancyEngine *engine);
     ~SoundManager();
 
diff --git a/engines/nancy/state/logo.cpp b/engines/nancy/state/logo.cpp
index 8d1392ac2f..d9a053ed1a 100644
--- a/engines/nancy/state/logo.cpp
+++ b/engines/nancy/state/logo.cpp
@@ -66,21 +66,21 @@ void Logo::init() {
 }
 
 void Logo::startSound() {
-	SoundManager::SoundDescription desc;
-	desc.read(*_engine->getBootChunkStream("MSND"), SoundManager::SoundDescription::kMenu);
+	SoundDescription desc;
+	desc.read(*_engine->getBootChunkStream("MSND"), SoundDescription::kMenu);
 	_engine->sound->loadSound(desc);
 	MSNDchannelID = desc.channelID;
-	desc.read(*_engine->getBootChunkStream("BUOK"), SoundManager::SoundDescription::kNormal);
+	desc.read(*_engine->getBootChunkStream("BUOK"), SoundDescription::kNormal);
 	_engine->sound->loadSound(desc);
-	desc.read(*_engine->getBootChunkStream("BUDE"), SoundManager::SoundDescription::kNormal);
+	desc.read(*_engine->getBootChunkStream("BUDE"), SoundDescription::kNormal);
 	_engine->sound->loadSound(desc);
-	desc.read(*_engine->getBootChunkStream("BULS"), SoundManager::SoundDescription::kNormal);
+	desc.read(*_engine->getBootChunkStream("BULS"), SoundDescription::kNormal);
 	_engine->sound->loadSound(desc);
-	desc.read(*_engine->getBootChunkStream("GLOB"), SoundManager::SoundDescription::kNormal);
+	desc.read(*_engine->getBootChunkStream("GLOB"), SoundDescription::kNormal);
 	_engine->sound->loadSound(desc);
-	desc.read(*_engine->getBootChunkStream("CURT"), SoundManager::SoundDescription::kNormal);
+	desc.read(*_engine->getBootChunkStream("CURT"), SoundDescription::kNormal);
 	_engine->sound->loadSound(desc);
-	desc.read(*_engine->getBootChunkStream("CANT"), SoundManager::SoundDescription::kNormal);
+	desc.read(*_engine->getBootChunkStream("CANT"), SoundDescription::kNormal);
 	_engine->sound->loadSound(desc);
 
 	_engine->sound->playSound(MSNDchannelID);
diff --git a/engines/nancy/state/map.cpp b/engines/nancy/state/map.cpp
index 5ddfcc0b58..ed092cdb71 100644
--- a/engines/nancy/state/map.cpp
+++ b/engines/nancy/state/map.cpp
@@ -73,8 +73,8 @@ void Map::init() {
 
     // Load the audio
     chunk->seek(0x18 + _mapID * 0x20, SEEK_SET);
-    SoundManager::SoundDescription sound;
-    sound.read(*chunk, SoundManager::SoundDescription::kMenu);
+    SoundDescription sound;
+    sound.read(*chunk, SoundDescription::kMenu);
     _engine->sound->loadSound(sound);
     _engine->sound->playSound(sound.channelID);
 
@@ -142,9 +142,9 @@ void Map::run() {
 
 void Map::stopSound() {
     Common::SeekableReadStream *chunk = _engine->getBootChunkStream("MAP");
-    SoundManager::SoundDescription sound;
+    SoundDescription sound;
     chunk->seek(0x18 + _mapID * 0x20, SEEK_SET);
-    sound.read(*chunk, SoundManager::SoundDescription::kMenu);
+    sound.read(*chunk, SoundDescription::kMenu);
     _engine->sound->stopSound(sound.channelID);
 }
 
diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index b0e7ae1863..794981e97f 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -68,6 +68,10 @@ void Scene::process() {
 }
 
 void Scene::changeScene(uint16 id, uint16 frame, uint16 verticalOffset, bool noSound) {
+    if (id == 9999) {
+        return;
+    }
+
     _sceneState.nextScene.sceneID = id;
     _sceneState.nextScene.frameID = frame;
     _sceneState.nextScene.verticalOffset = verticalOffset;
@@ -75,6 +79,10 @@ void Scene::changeScene(uint16 id, uint16 frame, uint16 verticalOffset, bool noS
     _state = kLoadNew;
 }
 
+void Scene::changeScene(const SceneChangeDescription &sceneDescription) {
+    changeScene(sceneDescription.sceneID, sceneDescription.frameID, sceneDescription.verticalOffset, sceneDescription.doNotStartSound);
+}
+
 void Scene::pushScene() {
     _sceneState.pushedScene = _sceneState.currentScene;
     _sceneState.isScenePushed = true;
@@ -327,7 +335,7 @@ void Scene::readSceneSummary(Common::SeekableReadStream &stream) {
     stream.seek(3, SEEK_CUR);
     _sceneState.summary.videoFormat = stream.readUint16LE();
 
-    _sceneState.summary.sound.read(stream, SoundManager::SoundDescription::kScene);
+    _sceneState.summary.sound.read(stream, SoundDescription::kScene);
 
     stream.seek(0x72);
     _sceneState.summary.verticalScrollDelta = stream.readUint16LE();
@@ -379,6 +387,10 @@ void Scene::setEventFlag(int16 label, NancyFlag flag) {
     }
 }
 
+void Scene::setEventFlag(EventFlagDescription eventFlag) {
+    setEventFlag(eventFlag.label, eventFlag.flag);
+}
+
 bool Scene::getEventFlag(int16 label, NancyFlag flag) const {
     if (label > -1) {
         return _flags.eventFlags[label] == flag;
@@ -387,6 +399,10 @@ bool Scene::getEventFlag(int16 label, NancyFlag flag) const {
     }
 }
 
+bool Scene::getEventFlag(EventFlagDescription eventFlag) const {
+    return getEventFlag(eventFlag.label, eventFlag.flag);
+}
+
 void Scene::setLogicCondition(int16 label, NancyFlag flag) {
     if (label > -1) {
         _flags.logicConditions[label].flag = flag;
diff --git a/engines/nancy/state/scene.h b/engines/nancy/state/scene.h
index e49d9d1f84..ddfc4e86f3 100644
--- a/engines/nancy/state/scene.h
+++ b/engines/nancy/state/scene.h
@@ -71,7 +71,7 @@ public:
         //
         uint16 videoFormat;                     // 0x3E, value is 1 or 2
         Common::String audioFile;               
-        SoundManager::SoundDescription sound;   // 0x40
+        SoundDescription sound;   // 0x40
         //
         uint16 verticalScrollDelta;             // 0x72
         uint16 horizontalEdgeSize;              // 0x74
@@ -96,6 +96,7 @@ public:
     void process();
 
     void changeScene(uint16 id, uint16 frame, uint16 verticalOffset, bool noSound);
+    void changeScene(const SceneChangeDescription &sceneDescription);
     void pushScene();
     void popScene();
 
@@ -109,7 +110,9 @@ public:
     NancyFlag hasItem(int16 id) const { return _flags.items[id]; }
 
     void setEventFlag(int16 label, NancyFlag flag = kTrue);
+    void setEventFlag(EventFlagDescription eventFlag);
     bool getEventFlag(int16 label, NancyFlag flag = kTrue) const;
+    bool getEventFlag(EventFlagDescription eventFlag) const;
 
     void setLogicCondition(int16 label, NancyFlag flag = kTrue);
     bool getLogicCondition(int16 label, NancyFlag flag = kTrue) const;
@@ -236,13 +239,6 @@ protected:
 
     bool isComingFromMenu = true;
     bool hasLoadedFromSavefile = false;
-
-    bool orderingPuzzleIsActive = false;
-    bool rotatingLockPuzzleIsActive = false;
-    bool leverPuzzleIsActive = false;
-    bool sliderPuzzleIsActive = false;
-    bool passwordPuzzleIsActive = false;
-    bool telephoneIsActive = false;
 };
 
 } // End of namespace State
diff --git a/engines/nancy/ui/viewport.cpp b/engines/nancy/ui/viewport.cpp
index 7f8146483a..9d5db84815 100644
--- a/engines/nancy/ui/viewport.cpp
+++ b/engines/nancy/ui/viewport.cpp
@@ -189,7 +189,10 @@ void Viewport::loadVideo(const Common::String &filename, uint frameNr, uint vert
     enableEdges(kUp | kDown | kLeft | kRight);
     
     setFrame(frameNr);
-    setVerticalScroll(verticalScroll); 
+    setVerticalScroll(verticalScroll);
+
+    _movementLastFrame = 0;
+    _nextMovementTime = 0;
 }
 
 void Viewport::setFrame(uint frameNr) {
diff --git a/engines/nancy/util.h b/engines/nancy/util.h
index 871130ff97..c1bc0711dc 100644
--- a/engines/nancy/util.h
+++ b/engines/nancy/util.h
@@ -27,7 +27,7 @@
 
 namespace Nancy {
 
-static void readRect(Common::SeekableReadStream &stream, Common::Rect &inRect) {
+inline void readRect(Common::SeekableReadStream &stream, Common::Rect &inRect) {
     inRect.left = stream.readSint32LE();
     inRect.top = stream.readSint32LE();
     inRect.right = stream.readSint32LE();


Commit: c8dbb03b8911f205040b74f8fdcdb9ba2649228d
    https://github.com/scummvm/scummvm/commit/c8dbb03b8911f205040b74f8fdcdb9ba2649228d
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Fix segfault on exit

Fixed an issue where RenderObjects would get registered twice in the graphics manager and sometimes cause a segmentation fault when closing the interpreter.

Changed paths:
    engines/nancy/graphics.cpp
    engines/nancy/state/scene.cpp


diff --git a/engines/nancy/graphics.cpp b/engines/nancy/graphics.cpp
index d4ef0e3e2c..46e233d805 100644
--- a/engines/nancy/graphics.cpp
+++ b/engines/nancy/graphics.cpp
@@ -109,6 +109,7 @@ void GraphicsManager::removeObject(RenderObject *object) {
     for (auto &r : _objects) {
         if (r == object) {
             _objects.erase(&r);
+            break;
         }
     }
 }
diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index 794981e97f..b2af32df96 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -242,10 +242,6 @@ void Scene::load() {
     _sceneState.currentScene = _sceneState.nextScene;
     _timers.sceneTime = 0;
 
-    if (_engine->getGameState() != _engine->getPreviousGameState()) {
-        registerGraphics();
-    }
-
     _state = kStartSound;
 }
 


Commit: 6a4e1ecb8e361b2b65570c0431e283bd6b0e4f68
    https://github.com/scummvm/scummvm/commit/6a4e1ecb8e361b2b65570c0431e283bd6b0e4f68
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Implement repeating action records

Implemented the finishExecution function, which allows some action records with the correct execType to restart after they've been triggered. Since this can result in objects getting added to the graphics manager twice, also added checks to the addObject function to avoid duplicates.

Changed paths:
    engines/nancy/action/actionmanager.cpp
    engines/nancy/action/actionrecord.h
    engines/nancy/action/leverpuzzle.cpp
    engines/nancy/action/orderingpuzzle.cpp
    engines/nancy/action/passwordpuzzle.cpp
    engines/nancy/action/primaryvideo.cpp
    engines/nancy/action/recordtypes.cpp
    engines/nancy/action/rotatinglockpuzzle.cpp
    engines/nancy/action/secondaryvideo.cpp
    engines/nancy/action/sliderpuzzle.cpp
    engines/nancy/action/staticbitmapanim.cpp
    engines/nancy/action/telephone.cpp
    engines/nancy/commontypes.cpp
    engines/nancy/graphics.cpp


diff --git a/engines/nancy/action/actionmanager.cpp b/engines/nancy/action/actionmanager.cpp
index 41e7864caf..86399ac788 100644
--- a/engines/nancy/action/actionmanager.cpp
+++ b/engines/nancy/action/actionmanager.cpp
@@ -108,7 +108,7 @@ bool ActionManager::addNewActionRecord(Common::SeekableReadStream &inputData) {
     delete[] descBuf;
 
     newRecord->type = inputData.readByte(); // redundant
-    newRecord->execType = inputData.readByte();
+    newRecord->execType = (ActionRecord::ExecutionType)inputData.readByte();
 
     uint16 localChunkSize = newRecord->readData(inputData);
     localChunkSize += 0x32;
diff --git a/engines/nancy/action/actionrecord.h b/engines/nancy/action/actionrecord.h
index 528185fff8..868d149244 100644
--- a/engines/nancy/action/actionrecord.h
+++ b/engines/nancy/action/actionrecord.h
@@ -81,9 +81,10 @@ class ActionRecord {
     friend class ActionManager;
 public:
     enum ExecutionState { kBegin, kRun, kActionTrigger };
+    enum ExecutionType { kOneShot = 1, kRepeating = 2 };
     ActionRecord() :
         type(0),
-        execType(0),
+        execType(kOneShot),
         isActive(0),
         isDone(false),
         hasHotspot(false),
@@ -104,11 +105,33 @@ protected:
         stream.skip(bytes);
         return bytes;
     }
+    
+    void finishExecution() {
+        switch (execType) {
+            case kOneShot:
+                isDone = true;
+                state = kBegin;
+                break;
+            case kRepeating:
+                isDone = false;
+                isActive = false;
+                state = kBegin;
+
+                for (uint i = 0; i < dependencies.size(); ++i) {
+                    dependencies[i].satisfied = false;
+                }
+
+                break;
+            default:
+                state = kBegin;
+                break;
+        }
+    }
 
 public:
     Common::String description;                     // 0x00
     byte type;                                      // 0x30
-    byte execType;                                  // 0x31
+    ExecutionType execType;                         // 0x31
     // 0x32 data
     Common::Array<DependencyRecord> dependencies;   // 0x36
     // 0x3A numDependencies
diff --git a/engines/nancy/action/leverpuzzle.cpp b/engines/nancy/action/leverpuzzle.cpp
index 5612567ecc..53468456ac 100644
--- a/engines/nancy/action/leverpuzzle.cpp
+++ b/engines/nancy/action/leverpuzzle.cpp
@@ -150,7 +150,7 @@ void LeverPuzzle::execute(Nancy::NancyEngine *engine) {
                 _engine->scene->changeScene(solveExitScene);
             }
 
-            isDone = true;
+            finishExecution();
     }
 }
 
diff --git a/engines/nancy/action/orderingpuzzle.cpp b/engines/nancy/action/orderingpuzzle.cpp
index 45ebc33e97..5f3b126d69 100644
--- a/engines/nancy/action/orderingpuzzle.cpp
+++ b/engines/nancy/action/orderingpuzzle.cpp
@@ -158,7 +158,7 @@ void OrderingPuzzle::execute(Nancy::NancyEngine *engine) {
                 _engine->scene->changeScene(solveExitScene);
             }
 
-            isDone = true;
+            finishExecution();
             break;
     }
 }
diff --git a/engines/nancy/action/passwordpuzzle.cpp b/engines/nancy/action/passwordpuzzle.cpp
index 5473d6a84d..14b911c7f4 100644
--- a/engines/nancy/action/passwordpuzzle.cpp
+++ b/engines/nancy/action/passwordpuzzle.cpp
@@ -157,7 +157,7 @@ void PasswordPuzzle::execute(Nancy::NancyEngine *engine) {
                     break;
             }
 
-            isDone = true;
+            finishExecution();
     }
 }
 
diff --git a/engines/nancy/action/primaryvideo.cpp b/engines/nancy/action/primaryvideo.cpp
index 59c02592df..f92148234a 100644
--- a/engines/nancy/action/primaryvideo.cpp
+++ b/engines/nancy/action/primaryvideo.cpp
@@ -267,7 +267,8 @@ void PlayPrimaryVideoChan0::execute(NancyEngine *engine) {
                     // Continue to next dialogue scene
                     engine->scene->changeScene(sceneChange);
                 }
-                isDone = true;
+                
+                finishExecution();
             }
 
             break;
diff --git a/engines/nancy/action/recordtypes.cpp b/engines/nancy/action/recordtypes.cpp
index 3b0955c3bc..52b39f1543 100644
--- a/engines/nancy/action/recordtypes.cpp
+++ b/engines/nancy/action/recordtypes.cpp
@@ -155,10 +155,9 @@ uint16 MapCall::readData(Common::SeekableReadStream &stream) {
 }
 
 void MapCall::execute(NancyEngine *engine) {
-    execType = 2;
+    execType = kRepeating;
     engine->scene->requestStateChange(NancyEngine::kMap);
-    // call base, depends on execType
-    state = kBegin;
+    finishExecution();
 }
 
 uint16 MapCallHot1Fr::readData(Common::SeekableReadStream &stream) {
@@ -326,8 +325,7 @@ void EventFlagsMultiHS::execute(NancyEngine *engine) {
         case kActionTrigger:
             hasHotspot = false;
             EventFlags::execute(engine);
-            break;
-        default:
+            finishExecution();
             break;
     }
 }
@@ -439,7 +437,7 @@ void ShowInventoryItem::execute(NancyEngine *engine) {
             engine->scene->addItemToInventory(objectID);
             setVisible(false);
             hasHotspot = false;
-            isDone = true;
+            finishExecution();
             break;
     }
 }
@@ -473,7 +471,7 @@ void PlayDigiSoundAndDie::execute(NancyEngine *engine) {
             engine->scene->setEventFlag(flagOnTrigger);
             engine->sound->stopSound(sound.channelID);
 
-            isDone = true;
+            finishExecution();
             break;
     }
 }
diff --git a/engines/nancy/action/rotatinglockpuzzle.cpp b/engines/nancy/action/rotatinglockpuzzle.cpp
index c10647b8b2..e290d89f0e 100644
--- a/engines/nancy/action/rotatinglockpuzzle.cpp
+++ b/engines/nancy/action/rotatinglockpuzzle.cpp
@@ -161,7 +161,7 @@ void RotatingLockPuzzle::execute(Nancy::NancyEngine *engine) {
                 _engine->scene->changeScene(solveExitScene);
             }
 
-            isDone = true;
+            finishExecution();
     }
 }
 
diff --git a/engines/nancy/action/secondaryvideo.cpp b/engines/nancy/action/secondaryvideo.cpp
index ecb94bffa5..c21502f061 100644
--- a/engines/nancy/action/secondaryvideo.cpp
+++ b/engines/nancy/action/secondaryvideo.cpp
@@ -201,7 +201,7 @@ void PlaySecondaryVideo::execute(NancyEngine *engine) {
         case kActionTrigger:
             engine->scene->pushScene();
             engine->scene->changeScene(sceneChange);
-            isDone = true;
+            finishExecution();
             break;
     }
 }
@@ -313,7 +313,7 @@ void PlaySecondaryMovie::execute(NancyEngine *engine) {
         case kActionTrigger:
             triggerFlags.execute(engine);
             engine->scene->changeScene(sceneChange);
-            isDone = true;
+            finishExecution();
             break;
     }
 }
diff --git a/engines/nancy/action/sliderpuzzle.cpp b/engines/nancy/action/sliderpuzzle.cpp
index de1886d440..49ce4c6e3e 100644
--- a/engines/nancy/action/sliderpuzzle.cpp
+++ b/engines/nancy/action/sliderpuzzle.cpp
@@ -176,7 +176,7 @@ void SliderPuzzle::execute(Nancy::NancyEngine *engine) {
             }
 
             engine->sound->stopSound(clickSound.channelID);
-            isDone = true;
+            finishExecution();
     }
 }
 
diff --git a/engines/nancy/action/staticbitmapanim.cpp b/engines/nancy/action/staticbitmapanim.cpp
index 9ea3cd29f7..d820ea2003 100644
--- a/engines/nancy/action/staticbitmapanim.cpp
+++ b/engines/nancy/action/staticbitmapanim.cpp
@@ -157,7 +157,7 @@ void PlayIntStaticBitmapAnimation::execute(NancyEngine *engine) {
             triggerFlags.execute(engine);
             if (doNotChangeScene == kFalse) {
                 engine->scene->changeScene(sceneChange);
-                isDone = true;
+                finishExecution();
             }
             break;
     }
diff --git a/engines/nancy/action/telephone.cpp b/engines/nancy/action/telephone.cpp
index 16af425e79..960e306808 100644
--- a/engines/nancy/action/telephone.cpp
+++ b/engines/nancy/action/telephone.cpp
@@ -243,7 +243,7 @@ void Telephone::execute(NancyEngine *engine) {
 
             }
 
-            isDone = true;
+            finishExecution();
             _engine->scene->getTextbox().clear();
     }
 }
diff --git a/engines/nancy/commontypes.cpp b/engines/nancy/commontypes.cpp
index da1ddfb0c8..97731c3132 100644
--- a/engines/nancy/commontypes.cpp
+++ b/engines/nancy/commontypes.cpp
@@ -72,6 +72,8 @@ void SoundDescription::read(Common::SeekableReadStream &stream, Type type) {
 	}
 	channelID = stream.readUint16LE();
 
+	// 0xE is soundPlayFormat, but I have no idea what that does yet
+
 	// The difference between these is a couple members found at the same position
 	// whose purpose I don't understand, so for now just skip them
 	switch (type) {
diff --git a/engines/nancy/graphics.cpp b/engines/nancy/graphics.cpp
index 46e233d805..a9978bc999 100644
--- a/engines/nancy/graphics.cpp
+++ b/engines/nancy/graphics.cpp
@@ -102,6 +102,16 @@ void GraphicsManager::onPause(bool pause) {
 }
 
 void GraphicsManager::addObject(RenderObject *object) {
+    for (auto &r : _objects) {
+        if (r == object) {
+            return;
+        }
+
+        if (r->getZOrder() > object->getZOrder()) {
+            break;
+        }
+    }
+    
     _objects.insert(object);
 }
 


Commit: 7b2aa161aec54d689f2115a82e3e6b1e29e56c43
    https://github.com/scummvm/scummvm/commit/7b2aa161aec54d689f2115a82e3e6b1e29e56c43
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Implement PlaySoundMultiHS action record

Implemented the PlaySoundMultiHS action record, which is used in the diner jukebox in nancy1.

Changed paths:
    engines/nancy/action/recordtypes.cpp
    engines/nancy/action/recordtypes.h


diff --git a/engines/nancy/action/recordtypes.cpp b/engines/nancy/action/recordtypes.cpp
index 52b39f1543..f5c96396bd 100644
--- a/engines/nancy/action/recordtypes.cpp
+++ b/engines/nancy/action/recordtypes.cpp
@@ -481,11 +481,49 @@ uint16 PlaySoundPanFrameAnchorAndDie::readData(Common::SeekableReadStream &strea
 }
 
 uint16 PlaySoundMultiHS::readData(Common::SeekableReadStream &stream) {
-    stream.seek(0x2F, SEEK_CUR);
-    uint16 size = stream.readUint16LE() * 0x12 + 0x31;
-    stream.seek(-0x31, SEEK_CUR);
+    sound.read(stream, SoundDescription::kNormal);
+    sceneChange.readData(stream);
+    flag.label = stream.readSint16LE();
+    flag.flag = (NancyFlag)stream.readByte();
+    stream.skip(2);
+    uint16 numHotspots = stream.readUint16LE();
 
-    return readRaw(stream, size); // TODO
+    for (uint i = 0; i < numHotspots; ++i) {
+        hotspots.push_back(HotspotDescription());
+        hotspots.back().frameID = stream.readUint16LE();
+        readRect(stream, hotspots.back().coords);
+    }
+
+    return 0x31 + numHotspots * 0x12;
+}
+
+void PlaySoundMultiHS::execute(Nancy::NancyEngine *engine) {
+    switch (state) {
+        case kBegin:
+            state = kRun;
+            // fall through
+        case kRun: {
+            hasHotspot = false;
+            uint currentFrame = engine->scene->getSceneInfo().frameID;
+
+            for (uint i = 0; i < hotspots.size(); ++i) {
+                if (hotspots[i].frameID == currentFrame) {
+                    hotspot = hotspots[i].coords;
+                    hasHotspot = true;
+                    break;
+                }
+            }
+
+            break;
+        }
+        case kActionTrigger:
+            engine->sound->loadSound(sound);
+            engine->sound->playSound(sound.channelID);
+            engine->scene->changeScene(sceneChange);
+            engine->scene->setEventFlag(flag);
+            finishExecution();
+            break;
+    }
 }
 
 uint16 HintSystem::readData(Common::SeekableReadStream &stream) {
diff --git a/engines/nancy/action/recordtypes.h b/engines/nancy/action/recordtypes.h
index 6a11abcbcd..099c2fd054 100644
--- a/engines/nancy/action/recordtypes.h
+++ b/engines/nancy/action/recordtypes.h
@@ -293,6 +293,12 @@ public:
 class PlaySoundMultiHS : public ActionRecord {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void execute(Nancy::NancyEngine *engine) override;
+
+    SoundDescription sound; // 0x0
+    SceneChangeDescription sceneChange; // 0x22
+    EventFlagDescription flag; // 0x2A
+    Common::Array<HotspotDescription> hotspots; // 0x31
 };
 
 class HintSystem : public ActionRecord {


Commit: d3804a31389ac2d7c1ba9986c6e7afc931399a1c
    https://github.com/scummvm/scummvm/commit/d3804a31389ac2d7c1ba9986c6e7afc931399a1c
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Implement PlayStaticBitmapAnimation action record

The PlayStaticBitmapAnimation and PlayIntStaticBitmapAnimation action records are nearly identical, so the two are now merged into a single PlayStaticBitmapAnimation class.

Changed paths:
    engines/nancy/action/arfactory_v1.cpp
    engines/nancy/action/recordtypes.cpp
    engines/nancy/action/recordtypes.h
    engines/nancy/action/staticbitmapanim.cpp
    engines/nancy/action/staticbitmapanim.h


diff --git a/engines/nancy/action/arfactory_v1.cpp b/engines/nancy/action/arfactory_v1.cpp
index 8f7577716e..32b594bc91 100644
--- a/engines/nancy/action/arfactory_v1.cpp
+++ b/engines/nancy/action/arfactory_v1.cpp
@@ -69,9 +69,9 @@ ActionRecord *ActionManager::createActionRecord(uint16 type) {
         case 0x2B:
             return new PlaySecondaryMovie(_engine->scene->getViewport());
         case 0x2C:
-            return new PlayStaticBitmapAnimation();
+            return new PlayStaticBitmapAnimation(false, _engine->scene->getViewport()); // PlayStaticBitmapAnimation
         case 0x2D:
-            return new PlayIntStaticBitmapAnimation(_engine->scene->getViewport());
+            return new PlayStaticBitmapAnimation(true, _engine->scene->getViewport()); // PlayIntStaticBitmapAnimation
         case 0x32:
             return new MapCall();
         case 0x33:
diff --git a/engines/nancy/action/recordtypes.cpp b/engines/nancy/action/recordtypes.cpp
index f5c96396bd..ddcbe0a902 100644
--- a/engines/nancy/action/recordtypes.cpp
+++ b/engines/nancy/action/recordtypes.cpp
@@ -126,29 +126,6 @@ uint16 StartStopPlayerScrolling::readData(Common::SeekableReadStream &stream) {
     return 1;
 }
 
-uint16 PlayStaticBitmapAnimation::readData(Common::SeekableReadStream &stream) {
-    // TODO
-    uint16 bytesRead = stream.pos();
-    byte *seek = bitmapData;
-    
-    uint16 currentSize = 0x72;
-    stream.read(seek, currentSize);
-
-    seek += currentSize;
-    currentSize = (uint16)(bitmapData[0x18]) - (uint16)(bitmapData[0x16]);
-    ++currentSize;
-    currentSize *= 16;
-    stream.read(seek, currentSize);
-
-    seek += 0x252;
-    currentSize = (uint16)(bitmapData[0x70]);
-    currentSize *= 34;
-    stream.read(seek, currentSize);
-
-    bytesRead= stream.pos() - bytesRead;
-    return bytesRead;
-}
-
 uint16 MapCall::readData(Common::SeekableReadStream &stream) {
     stream.skip(1);
     return 1;
diff --git a/engines/nancy/action/recordtypes.h b/engines/nancy/action/recordtypes.h
index 099c2fd054..eb2621de62 100644
--- a/engines/nancy/action/recordtypes.h
+++ b/engines/nancy/action/recordtypes.h
@@ -85,13 +85,6 @@ public:
     byte type = 0;
 };
 
-class PlayStaticBitmapAnimation : public ActionRecord {
-public:
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
-
-    byte bitmapData[0xA88];
-};
-
 class MapCall : public ActionRecord {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
diff --git a/engines/nancy/action/staticbitmapanim.cpp b/engines/nancy/action/staticbitmapanim.cpp
index d820ea2003..15e2760f49 100644
--- a/engines/nancy/action/staticbitmapanim.cpp
+++ b/engines/nancy/action/staticbitmapanim.cpp
@@ -34,7 +34,7 @@
 namespace Nancy {
 namespace Action {
 
-void PlayIntStaticBitmapAnimation::init() {
+void PlayStaticBitmapAnimation::init() {
     Graphics::Surface surf;
     _engine->_res->loadImage("ciftree", imageName, surf);
 
@@ -46,7 +46,7 @@ void PlayIntStaticBitmapAnimation::init() {
     RenderObject::init();
 }
 
-uint16 PlayIntStaticBitmapAnimation::readData(Common::SeekableReadStream &stream) {
+uint16 PlayStaticBitmapAnimation::readData(Common::SeekableReadStream &stream) {
     char name[10];
     stream.read(name, 10);
     imageName = Common::String(name);
@@ -61,30 +61,37 @@ uint16 PlayIntStaticBitmapAnimation::readData(Common::SeekableReadStream &stream
     loopLastFrame = stream.readUint16LE();
     frameTime = Common::Rational(1000, stream.readUint16LE()).toInt();
     zOrder = stream.readUint16LE();
-    updateCondition.label = stream.readSint16LE();
-    updateCondition.flag = (NancyFlag)stream.readUint16LE();
+    if (isInterruptible) {
+        interruptCondition.label = stream.readSint16LE();
+        interruptCondition.flag = (NancyFlag)stream.readUint16LE();
+    } else {
+        interruptCondition.label = -1;
+        interruptCondition.flag = kFalse;
+    }
     sceneChange.readData(stream);
     triggerFlags.readData(stream);
     sound.read(stream, SoundDescription::kNormal);
-    uint numFrames = stream.readUint16LE();
+    uint numViewportFrames = stream.readUint16LE();
 
     for (uint i = firstFrame; i <= loopLastFrame; ++i) {
         srcRects.push_back(Common::Rect());
         readRect(stream, srcRects[i]);
     }
 
-    for (uint i = 0; i < numFrames; ++i) {
+    for (uint i = 0; i < numViewportFrames; ++i) {
         bitmaps.push_back(BitmapDescription());
-        BitmapDescription &rects = bitmaps[i];
+        BitmapDescription &rects = bitmaps.back();
         rects.frameID = stream.readUint16LE();
         readRect(stream, rects.src);
         readRect(stream, rects.dest);
     }
 
-    return 0x76 + numFrames * 0x22 + (loopLastFrame - firstFrame + 1) * 16;
+    uint baseSize = isInterruptible ? 0x76 : 0x72;
+
+    return baseSize + numViewportFrames * 0x22 + (loopLastFrame - firstFrame + 1) * 16;
 }
 
-void PlayIntStaticBitmapAnimation::execute(NancyEngine *engine) {
+void PlayStaticBitmapAnimation::execute(NancyEngine *engine) {
     uint32 currentFrameTime = engine->getTotalPlayTime();
     switch (state) {
         case kBegin:
@@ -98,7 +105,7 @@ void PlayIntStaticBitmapAnimation::execute(NancyEngine *engine) {
             // Check the timer to see if we need to draw the next animation frame
             if (nextFrameTime <= currentFrameTime) {
                 // World's worst if statement
-                if (engine->scene->getEventFlag(updateCondition.label, updateCondition.flag) ||
+                if (engine->scene->getEventFlag(interruptCondition) ||
                     (   (((currentFrame == loopLastFrame) && (isReverse == kFalse) && (isLooping == kFalse)) ||
                         ((currentFrame == loopFirstFrame) && (isReverse == kTrue) && (isLooping == kFalse))) &&
                             !engine->sound->isSoundPlaying(sound.channelID))   ) {
@@ -163,7 +170,7 @@ void PlayIntStaticBitmapAnimation::execute(NancyEngine *engine) {
     }
 }
 
-void PlayIntStaticBitmapAnimation::setFrame(uint frame) {
+void PlayStaticBitmapAnimation::setFrame(uint frame) {
     currentFrame = frame;
     _drawSurface.create(_fullSurface, srcRects[frame]);
     _needsRedraw = true;
diff --git a/engines/nancy/action/staticbitmapanim.h b/engines/nancy/action/staticbitmapanim.h
index 40eb4c15ac..32389ab3ee 100644
--- a/engines/nancy/action/staticbitmapanim.h
+++ b/engines/nancy/action/staticbitmapanim.h
@@ -35,11 +35,13 @@ namespace Nancy {
 namespace Action {
 
 // ActionRecord subclass describing a short "flipbook" animation from a single bitmap
-// Can also play sound, but this has not yet been implemented
-class PlayIntStaticBitmapAnimation : public ActionRecord, public RenderObject {
+// Also supports sound and getting interrupted by an event flag.
+// This class covers both the PlayStaticBitmapAnimation and PlayIntStaticBitmapAnimation
+// action record types, whose functionality is nearly identical
+class PlayStaticBitmapAnimation : public ActionRecord, public RenderObject {
 public:
-    PlayIntStaticBitmapAnimation(RenderObject &redrawFrom) : RenderObject(redrawFrom) {}
-    virtual ~PlayIntStaticBitmapAnimation() { _fullSurface.free(); }
+    PlayStaticBitmapAnimation(bool interruptible, RenderObject &redrawFrom) : RenderObject(redrawFrom), isInterruptible(interruptible) {}
+    virtual ~PlayStaticBitmapAnimation() { _fullSurface.free(); }
 
     virtual void init() override;
 
@@ -57,7 +59,7 @@ public:
     uint16 loopLastFrame; // 0x18
     Time frameTime;
     uint16 zOrder; // 0x1C
-    EventFlagDescription updateCondition; // 0x1E
+    EventFlagDescription interruptCondition; // 0x1E
     SceneChangeDescription sceneChange;
     MultiEventFlagDescription triggerFlags; // 0x2A
 
@@ -72,6 +74,7 @@ public:
     int16 currentFrame = -1;
     int16 currentViewportFrame = -1;
     Time nextFrameTime;
+    bool isInterruptible;
     
 protected:
     virtual uint16 getZOrder() const override { return zOrder; }


Commit: 74b045bd52086047518d0c7705ff5320e9e67090
    https://github.com/scummvm/scummvm/commit/74b045bd52086047518d0c7705ff5320e9e67090
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Primary video fixes

Fixed an issue where the dialogue would abruptly end where it shouldn't, and another where it should, but it changed to a different scene instead.

Changed paths:
    engines/nancy/action/primaryvideo.cpp
    engines/nancy/action/primaryvideo.h


diff --git a/engines/nancy/action/primaryvideo.cpp b/engines/nancy/action/primaryvideo.cpp
index f92148234a..be8451dac4 100644
--- a/engines/nancy/action/primaryvideo.cpp
+++ b/engines/nancy/action/primaryvideo.cpp
@@ -39,6 +39,72 @@ namespace Action {
 
 PlayPrimaryVideoChan0 *PlayPrimaryVideoChan0::activePrimaryVideo = nullptr;
 
+void PlayPrimaryVideoChan0::ConditionFlag::read(Common::SeekableReadStream &stream) {
+    type = (ConditionType)stream.readByte();
+    flag.label = stream.readSint16LE();
+    flag.flag = (NancyFlag)stream.readByte();
+    orFlag = stream.readByte();
+}
+
+bool PlayPrimaryVideoChan0::ConditionFlag::isSatisfied(NancyEngine *engine) const {
+    switch (type) {
+        case ConditionFlag::kEventFlags:
+            return engine->scene->getEventFlag(flag);
+        case ConditionFlag::kInventory:
+            return engine->scene->hasItem(flag.label) == flag.flag;
+        default:
+            return false;
+    }
+}
+
+void PlayPrimaryVideoChan0::ConditionFlag::set(NancyEngine *engine) const {
+    switch (type) {
+        case ConditionFlag::kEventFlags:
+            engine->scene->setEventFlag(flag);
+            break;
+        case ConditionFlag::kInventory:
+            if (flag.flag == kTrue) {
+                engine->scene->addItemToInventory(flag.label);
+            } else {
+                engine->scene->removeItemFromInventory(flag.label);
+            }
+            break;
+        default:
+            break;
+    }
+}
+
+void PlayPrimaryVideoChan0::ConditionFlags::read(Common::SeekableReadStream &stream) {
+    uint16 numFlags = stream.readUint16LE();
+    
+    for (uint i = 0; i < numFlags; ++i) {
+        conditionFlags.push_back(ConditionFlag());
+        conditionFlags.back().read(stream);
+    }
+}
+
+bool PlayPrimaryVideoChan0::ConditionFlags::isSatisfied(NancyEngine *engine) const {
+    bool orFlag = false;
+
+    for (uint i = 0; i < conditionFlags.size(); ++i) {
+        const ConditionFlag &cur = conditionFlags[i];
+        
+        if (!cur.isSatisfied(engine)) {
+            if (orFlag) {
+                return false;
+            } else {
+                orFlag = true;
+            }
+        }
+    }
+
+    if (orFlag) {
+        return false;
+    } else {
+        return true;
+    }
+}
+
 PlayPrimaryVideoChan0::~PlayPrimaryVideoChan0() {
     _decoder.close();
 	if (activePrimaryVideo == this) {
@@ -97,8 +163,8 @@ uint16 PlayPrimaryVideoChan0::readData(Common::SeekableReadStream &stream) {
     stream.skip(1);
     conditionalResponseCharacterID = stream.readByte();
     goodbyeResponseCharacterID = stream.readByte();
-    numSceneChanges = stream.readByte();
-    shouldPopScene = stream.readByte() == 1;
+    isDialogueExitScene = (NancyFlag)stream.readByte();
+    doNotPop = (NancyFlag)stream.readByte();
     sceneChange.readData(stream);
 
     stream.seek(bytesRead + 0x69C);
@@ -106,17 +172,9 @@ uint16 PlayPrimaryVideoChan0::readData(Common::SeekableReadStream &stream) {
     uint16 numResponses = stream.readUint16LE();
     if (numResponses > 0) {
         for (uint i = 0; i < numResponses; ++i) {
-            uint16 numConditionFlags = stream.readUint16LE();
             responses.push_back(ResponseStruct());
             ResponseStruct &response = responses[i];
-
-            if (numConditionFlags > 0) {
-                for (uint16 j = 0; j < numConditionFlags; ++j) {
-                    response.conditionFlags.push_back(ConditionFlags());
-                    ConditionFlags &flags = response.conditionFlags[j];
-                    stream.read(flags.unknown, 5);
-                }
-            }
+            response.conditionFlags.read(stream);
             rawText = new char[400];
             stream.read(rawText, 400);
             UI::Textbox::assembleTextLine(rawText, response.text, 400);
@@ -141,24 +199,12 @@ uint16 PlayPrimaryVideoChan0::readData(Common::SeekableReadStream &stream) {
     uint16 numFlagsStructs = stream.readUint16LE();
     if (numFlagsStructs > 0)  {
         for (uint16 i = 0; i < numFlagsStructs; ++i) {
-            uint16 numConditionFlags = stream.readUint16LE();
             flagsStructs.push_back(FlagsStruct());
-            FlagsStruct &flagsStruct = flagsStructs[flagsStructs.size()-1];
-
-            if (numConditionFlags > 0) {
-                // Not sure about this
-                if (numConditionFlags > 0) {
-                    for (uint16 j = 0; j < numConditionFlags; ++j) {
-                        flagsStruct.conditionFlags.push_back(ConditionFlags());
-                        ConditionFlags &flags = flagsStruct.conditionFlags[flagsStruct.conditionFlags.size()-1];
-                        stream.read(flags.unknown, 5);
-                    }
-                }
-            }
-
-            flagsStruct.type = (FlagsStruct::ConditionType)stream.readByte();
-            flagsStruct.flagDesc.label = stream.readSint16LE();
-            flagsStruct.flagDesc.flag = (NancyFlag)stream.readByte();
+            FlagsStruct &flagsStruct = flagsStructs.back();
+            flagsStruct.conditions.read(stream);
+            flagsStruct.flagToSet.type = (ConditionFlag::ConditionType)stream.readByte();
+            flagsStruct.flagToSet.flag.label = stream.readSint16LE();
+            flagsStruct.flagToSet.flag.flag = (NancyFlag)stream.readByte();
         }
     }
 
@@ -197,7 +243,9 @@ void PlayPrimaryVideoChan0::execute(NancyEngine *engine) {
 
                 for (uint i = 0; i < responses.size(); ++i) {
                     auto &res = responses[i];
-                    engine->scene->getTextbox().addTextLine(res.text);
+                    if (res.conditionFlags.isSatisfied(engine)) {
+                        engine->scene->getTextbox().addTextLine(res.text);
+                    }
                 }
             }
 
@@ -217,7 +265,6 @@ void PlayPrimaryVideoChan0::execute(NancyEngine *engine) {
 
                     if (pickedResponse != -1) {
                         // Player has picked response, play sound file and change state
-                        sceneChange = responses[pickedResponse].sceneChange;
                         responseGenericSound.name = responses[pickedResponse].soundName;
                         // TODO this is probably not correct
                         engine->sound->loadSound(responseGenericSound);
@@ -230,26 +277,8 @@ void PlayPrimaryVideoChan0::execute(NancyEngine *engine) {
         case kActionTrigger:
             // process flags structs
             for (auto flags : flagsStructs) {
-                bool conditionsSatisfied = true;
-                if (flags.conditionFlags.size()) {
-                    error("Condition flags not evaluated, please fix");
-                }
-
-                if (conditionsSatisfied) {
-                    switch (flags.type) {
-                        case FlagsStruct::kEventFlags:
-                            engine->scene->setEventFlag(flags.flagDesc);
-                            break;
-                        case FlagsStruct::kInventory:
-                            if (flags.flagDesc.flag == kTrue) {
-                                engine->scene->addItemToInventory(flags.flagDesc.label);
-                            } else {
-                                engine->scene->removeItemFromInventory(flags.flagDesc.label);
-                            }
-                            break;
-                        default:
-                            break;
-                    }
+                if (flags.conditions.isSatisfied(engine)) {
+                    flags.flagToSet.set(engine);
                 }
             }
             
@@ -260,12 +289,18 @@ void PlayPrimaryVideoChan0::execute(NancyEngine *engine) {
 
             if (!engine->sound->isSoundPlaying(responseGenericSound.channelID)) {
                 engine->sound->stopSound(responseGenericSound.channelID);
-                if (shouldPopScene) {
-                    // Exit dialogue
-                    engine->scene->popScene();
+                
+                if (pickedResponse != -1) {
+                    engine->scene->changeScene(responses[pickedResponse].sceneChange);
                 } else {
-                    // Continue to next dialogue scene
-                    engine->scene->changeScene(sceneChange);
+                    // Evaluate scene branch structs here
+
+                    if (isDialogueExitScene == kFalse) {
+                        engine->scene->changeScene(sceneChange);
+                    } else if (doNotPop == kFalse) {
+                        // Exit dialogue
+                        engine->scene->popScene();
+                    }
                 }
                 
                 finishExecution();
diff --git a/engines/nancy/action/primaryvideo.h b/engines/nancy/action/primaryvideo.h
index ca6465c934..2d57bab72d 100644
--- a/engines/nancy/action/primaryvideo.h
+++ b/engines/nancy/action/primaryvideo.h
@@ -41,12 +41,27 @@ namespace Action {
 // ActionRecord subclass that handles all NPC dialog and nancy1's intro video
 class PlayPrimaryVideoChan0 : public ActionRecord, public RenderObject {
 
+struct ConditionFlag {
+enum ConditionType : byte { kNone = 0, kEventFlags = 1, kInventory = 2 };
+
+    ConditionType type;
+    EventFlagDescription flag;
+    bool orFlag;
+
+    void read(Common::SeekableReadStream &stream);
+    bool isSatisfied(NancyEngine *engine) const;
+    void set(NancyEngine *engine) const;
+};
+
 struct ConditionFlags {
-    byte unknown[5];
+    Common::Array<ConditionFlag> conditionFlags;
+
+    void read(Common::SeekableReadStream &stream);
+    bool isSatisfied(NancyEngine *engine) const;
 };
 
 struct ResponseStruct {
-    Common::Array<ConditionFlags> conditionFlags; // 0x02
+    ConditionFlags conditionFlags; // 0x01
     Common::String text; // 0x06
     Common::String soundName; // 0x196
     SceneChangeDescription sceneChange; // 0x1A0
@@ -54,11 +69,8 @@ struct ResponseStruct {
 };
 
 struct FlagsStruct {
-    enum ConditionType : byte { kNone = 0, kEventFlags = 1, kInventory = 2 };
-    Common::Array<ConditionFlags> conditionFlags;
-
-    ConditionType type;
-    EventFlagDescription flagDesc;
+    ConditionFlags conditions;
+    ConditionFlag flagToSet;
 };
 
 public:
@@ -86,12 +98,12 @@ public:
 
     byte conditionalResponseCharacterID; // 0x65E
     byte goodbyeResponseCharacterID; // 0x65F
-    byte numSceneChanges; // 0x660, not sure
-    bool shouldPopScene; // 0x661
+    NancyFlag isDialogueExitScene; // 0x660
+    NancyFlag doNotPop; // 0x661
     SceneChangeDescription sceneChange; // 0x662
 
-    Common::Array<ResponseStruct> responses;
-    Common::Array<FlagsStruct> flagsStructs;
+    Common::Array<ResponseStruct> responses; // 0x69E
+    Common::Array<FlagsStruct> flagsStructs; // 0x6AA
 
     AVFDecoder _decoder;
 


Commit: 145d5fd7359d0b146d4fecefa5592ef346e73e77
    https://github.com/scummvm/scummvm/commit/145d5fd7359d0b146d4fecefa5592ef346e73e77
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add ActionRecord and Scene debug channels

Added debug channels for action records and scenes, and added proper output to their respective loading functions.

Changed paths:
    engines/nancy/action/actionmanager.cpp
    engines/nancy/action/actionrecord.h
    engines/nancy/action/arfactory_v1.cpp
    engines/nancy/action/leverpuzzle.h
    engines/nancy/action/orderingpuzzle.h
    engines/nancy/action/passwordpuzzle.h
    engines/nancy/action/primaryvideo.h
    engines/nancy/action/recordtypes.h
    engines/nancy/action/rotatinglockpuzzle.h
    engines/nancy/action/secondaryvideo.h
    engines/nancy/action/sliderpuzzle.h
    engines/nancy/action/staticbitmapanim.h
    engines/nancy/action/telephone.h
    engines/nancy/nancy.cpp
    engines/nancy/nancy.h
    engines/nancy/state/scene.cpp
    engines/nancy/state/scene.h


diff --git a/engines/nancy/action/actionmanager.cpp b/engines/nancy/action/actionmanager.cpp
index 86399ac788..c2cf0f939f 100644
--- a/engines/nancy/action/actionmanager.cpp
+++ b/engines/nancy/action/actionmanager.cpp
@@ -147,6 +147,80 @@ bool ActionManager::addNewActionRecord(Common::SeekableReadStream &inputData) {
     }
 
     _records.push_back(newRecord);
+
+    debugC(1, kDebugActionRecord, "Loaded action record %i, type %s, typeID %i, description \"%s\", execType == %s",
+            _records.size() - 1,
+            newRecord->getRecordTypeName().c_str(),
+            newRecord->type,
+            newRecord->description.c_str(),
+            newRecord->execType == ActionRecord::kRepeating ? "kRepeating" : "kOneShot");
+    for (uint i = 0; i < newRecord->dependencies.size(); ++i) {
+        debugCN(1, kDebugActionRecord, "\tDependency %i: type ", i);
+        switch (newRecord->dependencies[i].type) {
+            case kNone :debugCN(1, kDebugActionRecord, "kNone"); break;
+            case kInventory :
+                debugCN(1, kDebugActionRecord, "kInventory, item ID %i %s",
+                            newRecord->dependencies[i].label,
+                            newRecord->dependencies[i].condition == kTrue ? "is in possession" : "is not in possession");
+                break;
+            case kEventFlag :
+                debugCN(1, kDebugActionRecord, "kEventFlag, flag ID %i == %s",
+                            newRecord->dependencies[i].label,
+                            newRecord->dependencies[i].condition == kTrue ? "true" : "false");
+                break;
+            case kLogicCondition :
+                debugCN(1, kDebugActionRecord, "kLogicCondition, logic condition ID %i == %s",
+                            newRecord->dependencies[i].label,
+                            newRecord->dependencies[i].condition == kTrue ? "true" : "false");
+                break;
+            case kTotalTime :
+                debugCN(1, kDebugActionRecord, "kTotalTime, %i hours, %i minutes, %i seconds, %i milliseconds",
+                            newRecord->dependencies[i].hours,
+                            newRecord->dependencies[i].minutes,
+                            newRecord->dependencies[i].seconds,
+                            newRecord->dependencies[i].milliseconds);
+                break;
+            case kSceneTime :
+                debugCN(1, kDebugActionRecord, "kSceneTime, %i hours, %i minutes, %i seconds, %i milliseconds",
+                            newRecord->dependencies[i].hours,
+                            newRecord->dependencies[i].minutes,
+                            newRecord->dependencies[i].seconds,
+                            newRecord->dependencies[i].milliseconds);
+                break;
+            case kPlayerTime :
+                debugCN(1, kDebugActionRecord, "kPlayerTime, %i days, %i hours, %i minutes, %i seconds",
+                            newRecord->dependencies[i].hours,
+                            newRecord->dependencies[i].minutes,
+                            newRecord->dependencies[i].seconds,
+                            newRecord->dependencies[i].milliseconds);
+                break;
+            case kSceneCount :
+                debugCN(1, kDebugActionRecord, "kSceneCount, scene ID %i, hit count %s %i",
+                            newRecord->dependencies[i].hours,
+                            newRecord->dependencies[i].milliseconds == 1 ? ">" : newRecord->dependencies[i].milliseconds == 2 ? "<" : "==",
+                            newRecord->dependencies[i].seconds);
+                break;
+            case kResetOnNewDay : debugCN(1, kDebugActionRecord, "kResetOnNewDay"); break;
+            case kUseItem :
+                debugCN(1, kDebugActionRecord, "kUseItem, item ID %i %s",
+                            newRecord->dependencies[i].label,
+                            newRecord->dependencies[i].condition == kTrue ? "is held" : "is not held");
+                break;
+            case kTimeOfDay :
+                debugCN(1, kDebugActionRecord, "kTimeOfDay, %s",
+                            newRecord->dependencies[i].label == 0 ? "day" : newRecord->dependencies[i].label == 1 ? "night" : "dusk/dawn");
+                break;
+            case kTimerNotDone : debugCN(1, kDebugActionRecord, "kTimerNotDone"); break;
+            case kTimerDone : debugCN(1, kDebugActionRecord, "kTimerDone"); break;
+            case kDifficultyLevel :
+                debugCN(1, kDebugActionRecord, "kDifficultyLevel, level %i", newRecord->dependencies[i].condition);
+                break;
+            default: debugCN(1, kDebugActionRecord, "unknown"); break;
+        }
+        debugC(1, kDebugActionRecord, ", orFlag == %s", newRecord->dependencies[i].orFlag == true ? "true" : "false");
+    }
+
+
     return true;
 }
 
diff --git a/engines/nancy/action/actionrecord.h b/engines/nancy/action/actionrecord.h
index 868d149244..cd57af9ea8 100644
--- a/engines/nancy/action/actionrecord.h
+++ b/engines/nancy/action/actionrecord.h
@@ -128,6 +128,9 @@ protected:
         }
     }
 
+    // Used for debugging
+    virtual Common::String getRecordTypeName() const =0;
+
 public:
     Common::String description;                     // 0x00
     byte type;                                      // 0x30
diff --git a/engines/nancy/action/arfactory_v1.cpp b/engines/nancy/action/arfactory_v1.cpp
index 32b594bc91..711f6fd089 100644
--- a/engines/nancy/action/arfactory_v1.cpp
+++ b/engines/nancy/action/arfactory_v1.cpp
@@ -63,9 +63,9 @@ ActionRecord *ActionManager::createActionRecord(uint16 type) {
         case 0x28:
             return new PlayPrimaryVideoChan0(_engine->scene->getViewport());
         case 0x29:
-            return new PlaySecondaryVideo(_engine->scene->getViewport());
+            return new PlaySecondaryVideo('0', _engine->scene->getViewport());
         case 0x2A:
-            return new PlaySecondaryVideo(_engine->scene->getViewport());
+            return new PlaySecondaryVideo('1', _engine->scene->getViewport());
         case 0x2B:
             return new PlaySecondaryMovie(_engine->scene->getViewport());
         case 0x2C:
diff --git a/engines/nancy/action/leverpuzzle.h b/engines/nancy/action/leverpuzzle.h
index d1f44f8b30..ca6798dcd1 100644
--- a/engines/nancy/action/leverpuzzle.h
+++ b/engines/nancy/action/leverpuzzle.h
@@ -69,6 +69,8 @@ public:
     SolveState solveState = kNotSolved;
 
 protected:
+    virtual Common::String getRecordTypeName() const override { return "LeverPuzzle"; }
+
     virtual uint16 getZOrder() const override { return 7; }
     virtual BlitType getBlitType() const override { return kTrans; }
     virtual bool isViewportRelative() const override { return true; }
diff --git a/engines/nancy/action/orderingpuzzle.h b/engines/nancy/action/orderingpuzzle.h
index f78adca366..4c819632a8 100644
--- a/engines/nancy/action/orderingpuzzle.h
+++ b/engines/nancy/action/orderingpuzzle.h
@@ -70,6 +70,8 @@ public:
     Time solveSoundPlayTime;
 
 protected:
+    virtual Common::String getRecordTypeName() const override { return "OrderingPuzzle"; }
+    
     virtual uint16 getZOrder() const override { return 7; }
     virtual BlitType getBlitType() const override { return kTrans; }
     virtual bool isViewportRelative() const override { return true; }
diff --git a/engines/nancy/action/passwordpuzzle.h b/engines/nancy/action/passwordpuzzle.h
index 4f516b2cfb..4901e58a44 100644
--- a/engines/nancy/action/passwordpuzzle.h
+++ b/engines/nancy/action/passwordpuzzle.h
@@ -76,6 +76,8 @@ public:
     SolveState solveState;
 
 protected:
+    virtual Common::String getRecordTypeName() const override { return "PasswordPuzzle"; }
+    
     virtual uint16 getZOrder() const override { return 7; }
     virtual BlitType getBlitType() const override { return kTrans; }
     virtual bool isViewportRelative() const override { return true; }
diff --git a/engines/nancy/action/primaryvideo.h b/engines/nancy/action/primaryvideo.h
index 2d57bab72d..aa75611ab3 100644
--- a/engines/nancy/action/primaryvideo.h
+++ b/engines/nancy/action/primaryvideo.h
@@ -114,6 +114,8 @@ public:
 	static PlayPrimaryVideoChan0 *activePrimaryVideo;
 
 protected:
+    virtual Common::String getRecordTypeName() const override { return "PlayPrimaryVideoChan0"; }
+    
     virtual uint16 getZOrder() const override { return 8; }
     virtual BlitType getBlitType() const override { return kNoTrans; }
     virtual bool isViewportRelative() const override { return true; }
diff --git a/engines/nancy/action/recordtypes.h b/engines/nancy/action/recordtypes.h
index eb2621de62..fbb38119f8 100644
--- a/engines/nancy/action/recordtypes.h
+++ b/engines/nancy/action/recordtypes.h
@@ -45,6 +45,9 @@ public:
     virtual void execute(Nancy::NancyEngine *engine) override;
 
     SceneChangeDescription sceneChange;
+
+protected:
+    virtual Common::String getRecordTypeName() const override { return "SceneChange"; }
 };
 
 class HotMultiframeSceneChange : public SceneChange {
@@ -53,6 +56,9 @@ public:
     virtual void execute(Nancy::NancyEngine *engine) override;
 
     Common::Array<HotspotDescription> hotspots;
+
+protected:
+    virtual Common::String getRecordTypeName() const override { return "HotMultiframeSceneChange"; }
 };
 
 class Hot1FrSceneChange : public SceneChange {
@@ -61,20 +67,32 @@ public:
     virtual void execute(Nancy::NancyEngine *engine) override;
 
     HotspotDescription hotspotDesc;
+
+protected:
+    virtual Common::String getRecordTypeName() const override { return "Hot1FrSceneChange"; }
 };
 
 class Hot1FrExitSceneChange : public Hot1FrSceneChange {
     virtual CursorManager::CursorType getHoverCursor() const override { return CursorManager::kExitArrow; }
+
+protected:
+    virtual Common::String getRecordTypeName() const override { return "Hot1FrExitSceneChange"; }
 };
 
 class HotMultiframeMultisceneChange : public ActionRecord {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
+
+protected:
+    virtual Common::String getRecordTypeName() const override { return "HotMultiframeMultisceneChange"; }
 };
 
 class StartFrameNextScene : public ActionRecord {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
+
+protected:
+    virtual Common::String getRecordTypeName() const override { return "StartFrameNextScene"; }
 };
 
 class StartStopPlayerScrolling : public ActionRecord {
@@ -83,6 +101,9 @@ public:
     // TODO add a Start and Stop subclass
 
     byte type = 0;
+
+protected:
+    virtual Common::String getRecordTypeName() const override { return "StartStopPlayerScrolling"; }
 };
 
 class MapCall : public ActionRecord {
@@ -91,6 +112,9 @@ public:
     virtual void execute(Nancy::NancyEngine *engine) override;
 
     virtual CursorManager::CursorType getHoverCursor() const override { return CursorManager::kExitArrow; }
+
+protected:
+    virtual Common::String getRecordTypeName() const override { return "MapCall"; }
 };
 
 class MapCallHot1Fr : public MapCall {
@@ -99,6 +123,9 @@ public:
     virtual void execute(Nancy::NancyEngine *engine) override;
 
     HotspotDescription hotspotDesc;
+    
+protected:
+    virtual Common::String getRecordTypeName() const override { return "MapCallHot1Fr"; }
 };
 
 class MapCallHotMultiframe : public MapCall {
@@ -107,21 +134,33 @@ public:
     virtual void execute(Nancy::NancyEngine *engine) override;
 
     Common::Array<HotspotDescription> hotspots;
+    
+protected:
+    virtual Common::String getRecordTypeName() const override { return "MapCallHotMultiframe"; }
 };
 
 class MapLocationAccess : public ActionRecord {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    
+protected:
+    virtual Common::String getRecordTypeName() const override { return "MapLocationAccess"; }
 };
 
 class MapSound : public ActionRecord {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    
+protected:
+    virtual Common::String getRecordTypeName() const override { return "MapSound"; }
 };
 
 class MapAviOverride : public ActionRecord {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    
+protected:
+    virtual Common::String getRecordTypeName() const override { return "MapAviOverride"; }
 };
 
 class MapAviOverrideOff : public ActionRecord {
@@ -129,11 +168,17 @@ public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
 
     byte overrideOffData = 0;
+    
+protected:
+    virtual Common::String getRecordTypeName() const override { return "MapAviOverrideOff"; }
 };
 
 class TextBoxWrite : public ActionRecord {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    
+protected:
+    virtual Common::String getRecordTypeName() const override { return "TextBoxWrite"; }
 };
 
 class TextBoxClear : public ActionRecord {
@@ -141,11 +186,17 @@ public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
 
     byte clearData = 0;
+    
+protected:
+    virtual Common::String getRecordTypeName() const override { return "TextBoxClear"; }
 };
 
 class BumpPlayerClock : public ActionRecord {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    
+protected:
+    virtual Common::String getRecordTypeName() const override { return "BumpPlayerClock"; }
 };
 
 class SaveContinueGame : public ActionRecord {
@@ -153,6 +204,9 @@ public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
 
     byte saveContinueData = 0;
+    
+protected:
+    virtual Common::String getRecordTypeName() const override { return "SaveContinueGame"; }
 };
 
 class TurnOffMainRendering : public ActionRecord {
@@ -160,6 +214,9 @@ public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
 
     byte turnOffData = 0;
+    
+protected:
+    virtual Common::String getRecordTypeName() const override { return "TurnOffMainRendering"; }
 };
 
 class TurnOnMainRendering : public ActionRecord {
@@ -167,18 +224,27 @@ public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
 
     byte turnOnData = 0;
+    
+protected:
+    virtual Common::String getRecordTypeName() const override { return "TurnOnMainRendering"; }
 };
 
 class ResetAndStartTimer : public ActionRecord {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
     virtual void execute(Nancy::NancyEngine *engine) override;
+    
+protected:
+    virtual Common::String getRecordTypeName() const override { return "ResetAndStartTimer"; }
 };
 
 class StopTimer : public ActionRecord {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
     virtual void execute(Nancy::NancyEngine *engine) override;
+    
+protected:
+    virtual Common::String getRecordTypeName() const override { return "StopTimer"; }
 };
 
 class EventFlags : public ActionRecord {
@@ -187,6 +253,9 @@ public:
     virtual void execute(Nancy::NancyEngine *engine) override;
 
     MultiEventFlagDescription flags;
+    
+protected:
+    virtual Common::String getRecordTypeName() const override { return "EventFlags"; }
 };
 
 class EventFlagsMultiHS : public EventFlags {
@@ -195,6 +264,9 @@ public:
     virtual void execute(Nancy::NancyEngine *engine) override;
 
     Common::Array<HotspotDescription> hotspots;
+    
+protected:
+    virtual Common::String getRecordTypeName() const override { return "EventFlagsMultiHS"; }
 };
 
 class LoseGame : public ActionRecord {
@@ -202,6 +274,9 @@ public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
 
     byte loseData = 0;
+    
+protected:
+    virtual Common::String getRecordTypeName() const override { return "LoseGame"; }
 };
 
 class PushScene : public ActionRecord {
@@ -209,6 +284,9 @@ public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
 
     byte pushData = 0;
+    
+protected:
+    virtual Common::String getRecordTypeName() const override { return "PushScene"; }
 };
 
 class PopScene : public ActionRecord {
@@ -216,6 +294,9 @@ public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
 
     byte popData = 0;
+    
+protected:
+    virtual Common::String getRecordTypeName() const override { return "PopScene"; }
 };
 
 class WinGame : public ActionRecord {
@@ -223,16 +304,25 @@ public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
 
     byte winData = 0;
+    
+protected:
+    virtual Common::String getRecordTypeName() const override { return "WinGame"; }
 };
 
 class AddInventoryNoHS : public ActionRecord {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    
+protected:
+    virtual Common::String getRecordTypeName() const override { return "AddInventoryNoHS"; }
 };
 
 class RemoveInventoryNoHS : public ActionRecord {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    
+protected:
+    virtual Common::String getRecordTypeName() const override { return "RemoveInventoryNoHS"; }
 };
 
 class DifficultyLevel : public ActionRecord {
@@ -242,6 +332,9 @@ public:
 
     uint16 difficulty = 0;
     EventFlagDescription flag;
+    
+protected:
+    virtual Common::String getRecordTypeName() const override { return "DifficultyLevel"; }
 };
 
 class ShowInventoryItem : public ActionRecord, public RenderObject {
@@ -262,6 +355,8 @@ public:
     Graphics::ManagedSurface _fullSurface;
     
 protected:
+    virtual Common::String getRecordTypeName() const override { return "ShowInventoryItem"; }
+
     virtual uint16 getZOrder() const override { return 9; }
     virtual BlitType getBlitType() const override { return kNoTrans; }
     virtual bool isViewportRelative() const override { return true; }
@@ -276,11 +371,17 @@ public:
     SoundDescription sound;
     SceneChangeDescription sceneChange;
     EventFlagDescription flagOnTrigger;
+    
+protected:
+    virtual Common::String getRecordTypeName() const override { return "PlayDigiSoundAndDie"; }
 };
 
 class PlaySoundPanFrameAnchorAndDie : public ActionRecord {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    
+protected:
+    virtual Common::String getRecordTypeName() const override { return "PlaySoundPanFrameAnchorAndDie"; }
 };
 
 class PlaySoundMultiHS : public ActionRecord {
@@ -292,6 +393,9 @@ public:
     SceneChangeDescription sceneChange; // 0x22
     EventFlagDescription flag; // 0x2A
     Common::Array<HotspotDescription> hotspots; // 0x31
+    
+protected:
+    virtual Common::String getRecordTypeName() const override { return "PlaySoundMultiHS"; }
 };
 
 class HintSystem : public ActionRecord {
@@ -309,6 +413,9 @@ public:
 
     void selectHint(Nancy::NancyEngine *engine);
     void getHint(uint hint, uint difficulty);
+    
+protected:
+    virtual Common::String getRecordTypeName() const override { return "HintSystem"; }
 };
 
 } // End of namespace Action
diff --git a/engines/nancy/action/rotatinglockpuzzle.h b/engines/nancy/action/rotatinglockpuzzle.h
index 352ed0a1f7..4ca0ec5bbb 100644
--- a/engines/nancy/action/rotatinglockpuzzle.h
+++ b/engines/nancy/action/rotatinglockpuzzle.h
@@ -71,6 +71,8 @@ public:
 
 
 protected:
+    virtual Common::String getRecordTypeName() const override { return "RotatingLockPuzzle"; }
+
     virtual uint16 getZOrder() const override { return 7; }
     virtual BlitType getBlitType() const override { return kTrans; }
     virtual bool isViewportRelative() const override { return true; }
diff --git a/engines/nancy/action/secondaryvideo.h b/engines/nancy/action/secondaryvideo.h
index 903879ba8f..b8c2f070ac 100644
--- a/engines/nancy/action/secondaryvideo.h
+++ b/engines/nancy/action/secondaryvideo.h
@@ -50,7 +50,7 @@ class PlaySecondaryVideo : public ActionRecord, public RenderObject {
 public:
     enum HoverState { kNoHover, kHover, kEndHover };
 
-    PlaySecondaryVideo(RenderObject &redrawFrom) : RenderObject(redrawFrom) {}
+    PlaySecondaryVideo(char chan, RenderObject &redrawFrom) : RenderObject(redrawFrom), channel(chan) {}
     virtual ~PlaySecondaryVideo() { _decoder.close(); }
 
     virtual void init() override;
@@ -74,6 +74,8 @@ public:
     Common::Array<SecondaryVideoDesc> videoDescs; // 0x35
 
 protected:
+    virtual Common::String getRecordTypeName() const override { return Common::String("PlaySecondaryVideoChan" + channel); }
+
     virtual uint16 getZOrder() const override { return 8; }
     virtual BlitType getBlitType() const override { return kTrans; }
     virtual bool isViewportRelative() const override { return true; }
@@ -83,6 +85,8 @@ protected:
     int _currentViewportFrame = -1;
     bool _isPlaying = false;
     bool _isHovered = false;
+
+    char channel;
 };
 
 class PlaySecondaryMovie : public ActionRecord, public RenderObject {
@@ -113,6 +117,8 @@ public:
     Common::Array<SecondaryVideoDesc> videoDescs; // 0xD4
 
 protected:
+    virtual Common::String getRecordTypeName() const override { return "PlaySecondaryMovie"; }
+
     virtual uint16 getZOrder() const override { return 8; }
     virtual BlitType getBlitType() const override { return kNoTrans; }
     virtual bool isViewportRelative() const override { return true; }
diff --git a/engines/nancy/action/sliderpuzzle.h b/engines/nancy/action/sliderpuzzle.h
index 2ee2fe5c22..f7144a6a0c 100644
--- a/engines/nancy/action/sliderpuzzle.h
+++ b/engines/nancy/action/sliderpuzzle.h
@@ -70,6 +70,8 @@ public:
     static bool playerHasTriedPuzzle;
 
 protected:
+    virtual Common::String getRecordTypeName() const override { return "SliderPuzzle"; }
+
     virtual uint16 getZOrder() const override { return 7; }
     virtual BlitType getBlitType() const override { return kTrans; }
     virtual bool isViewportRelative() const override { return true; }
diff --git a/engines/nancy/action/staticbitmapanim.h b/engines/nancy/action/staticbitmapanim.h
index 32389ab3ee..ee8e60557e 100644
--- a/engines/nancy/action/staticbitmapanim.h
+++ b/engines/nancy/action/staticbitmapanim.h
@@ -77,6 +77,8 @@ public:
     bool isInterruptible;
     
 protected:
+    virtual Common::String getRecordTypeName() const override { return isInterruptible ? "PlayIntStaticBitmapAnimation" : "PlayStaticBitmapAnimation"; }
+
     virtual uint16 getZOrder() const override { return zOrder; }
     virtual BlitType getBlitType() const override { return isTransparent == kTrue ? kTrans : kNoTrans; }
     virtual bool isViewportRelative() const override { return true; }
diff --git a/engines/nancy/action/telephone.h b/engines/nancy/action/telephone.h
index c05aca6348..b8265e2c3e 100644
--- a/engines/nancy/action/telephone.h
+++ b/engines/nancy/action/telephone.h
@@ -88,6 +88,8 @@ public:
     uint selected;
 
 protected:
+    virtual Common::String getRecordTypeName() const override { return "Telephone"; }
+
     virtual uint16 getZOrder() const override { return 7; }
     virtual BlitType getBlitType() const override { return kTrans; }
     virtual bool isViewportRelative() const override { return true; }
diff --git a/engines/nancy/nancy.cpp b/engines/nancy/nancy.cpp
index cefce166f2..a7abe09dfa 100644
--- a/engines/nancy/nancy.cpp
+++ b/engines/nancy/nancy.cpp
@@ -61,16 +61,9 @@ NancyEngine::NancyEngine(OSystem *syst, const NancyGameDescription *gd) :
 {
 	_system = syst;
 
-	DebugMan.addDebugChannel(kDebugSchedule, "Schedule", "Script Schedule debug level");
 	DebugMan.addDebugChannel(kDebugEngine, "Engine", "Engine debug level");
-	DebugMan.addDebugChannel(kDebugDisplay, "Display", "Display debug level");
-	DebugMan.addDebugChannel(kDebugMouse, "Mouse", "Mouse debug level");
-	DebugMan.addDebugChannel(kDebugParser, "Parser", "Parser debug level");
-	DebugMan.addDebugChannel(kDebugFile, "File", "File IO debug level");
-	DebugMan.addDebugChannel(kDebugRoute, "Route", "Route debug level");
-	DebugMan.addDebugChannel(kDebugInventory, "Inventory", "Inventory debug level");
-	DebugMan.addDebugChannel(kDebugObject, "Object", "Object debug level");
-	DebugMan.addDebugChannel(kDebugMusic, "Music", "Music debug level");
+	DebugMan.addDebugChannel(kDebugActionRecord, "ActionRecord", "Action Record debug level");
+	DebugMan.addDebugChannel(kDebugScene, "Scene", "Scene debug level");
 
 	_console = new NancyConsole(this);
 	_rnd = new Common::RandomSource("Nancy");
diff --git a/engines/nancy/nancy.h b/engines/nancy/nancy.h
index b10ea8201b..2d067e2b46 100644
--- a/engines/nancy/nancy.h
+++ b/engines/nancy/nancy.h
@@ -49,16 +49,9 @@ namespace Nancy {
 static const int kSavegameVersion = 1;
 
 enum NancyDebugChannels {
-	kDebugSchedule  = 1 <<  0,
-	kDebugEngine    = 1 <<  1,
-	kDebugDisplay   = 1 <<  2,
-	kDebugMouse     = 1 <<  3,
-	kDebugParser    = 1 <<  4,
-	kDebugFile      = 1 <<  5,
-	kDebugRoute     = 1 <<  6,
-	kDebugInventory = 1 <<  7,
-	kDebugObject    = 1 <<  8,
-	kDebugMusic     = 1 <<  9
+	kDebugEngine    	= 1 << 0,
+	kDebugActionRecord  = 1 << 1,
+	kDebugScene			= 1 << 2
 };
 
 struct NancyGameDescription;
diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index b2af32df96..80df69949d 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -194,6 +194,13 @@ void Scene::load() {
 
     readSceneSummary(*sceneSummaryChunk);
 
+    debugC(0, kDebugScene, "Loading new scene %i: description \"%s\", frame %i, vertical scroll %i, doNotStartSound == %s",
+                _sceneState.nextScene.sceneID,
+                _sceneState.summary.description.c_str(),
+                _sceneState.nextScene.frameID,
+                _sceneState.nextScene.verticalOffset,
+                _sceneState._doNotStartSound == true ? "true" : "false");
+
     // Search for Action Records, maximum for a scene is 30
     Common::SeekableReadStream *actionRecordChunk = nullptr;
 
diff --git a/engines/nancy/state/scene.h b/engines/nancy/state/scene.h
index ddfc4e86f3..fb24ea963f 100644
--- a/engines/nancy/state/scene.h
+++ b/engines/nancy/state/scene.h
@@ -189,7 +189,7 @@ protected:
     };
 
     struct Timers {
-        enum TimeOfDay { kDay, kNight, kDuskDawn };
+        enum TimeOfDay { kDay = 0, kNight = 1, kDuskDawn = 2 };
 
         Time tickCount;
         Time pushedPlayTime;


Commit: 51ba19dc70caccfba25d4f1efb93d729944fffb3
    https://github.com/scummvm/scummvm/commit/51ba19dc70caccfba25d4f1efb93d729944fffb3
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Secondary movie fixes

Secondary movies now actually reach the kActionTrigger state, and now also supports reverse playback. The PlaySecondaryMovie class was moved to its own header file, the SecondaryVideoDescription struct was moved to commontypes.h, and new methods for disabling mouse input were added to NancyEngine and Scene.

Changed paths:
  A engines/nancy/action/secondarymovie.cpp
  A engines/nancy/action/secondarymovie.h
    engines/nancy/action/arfactory_v1.cpp
    engines/nancy/action/secondaryvideo.cpp
    engines/nancy/action/secondaryvideo.h
    engines/nancy/commontypes.cpp
    engines/nancy/commontypes.h
    engines/nancy/input.cpp
    engines/nancy/input.h
    engines/nancy/module.mk
    engines/nancy/nancy.cpp
    engines/nancy/nancy.h


diff --git a/engines/nancy/action/arfactory_v1.cpp b/engines/nancy/action/arfactory_v1.cpp
index 711f6fd089..1eef792bdd 100644
--- a/engines/nancy/action/arfactory_v1.cpp
+++ b/engines/nancy/action/arfactory_v1.cpp
@@ -25,6 +25,7 @@
 #include "engines/nancy/action/actionrecord.h"
 #include "engines/nancy/action/primaryvideo.h"
 #include "engines/nancy/action/secondaryvideo.h"
+#include "engines/nancy/action/secondarymovie.h"
 #include "engines/nancy/action/staticbitmapanim.h"
 #include "engines/nancy/action/orderingpuzzle.h"
 #include "engines/nancy/action/rotatinglockpuzzle.h"
diff --git a/engines/nancy/action/secondarymovie.cpp b/engines/nancy/action/secondarymovie.cpp
new file mode 100644
index 0000000000..420eddee27
--- /dev/null
+++ b/engines/nancy/action/secondarymovie.cpp
@@ -0,0 +1,183 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/nancy/action/secondarymovie.h"
+
+#include "engines/nancy/graphics.h"
+#include "engines/nancy/nancy.h"
+#include "engines/nancy/state/scene.h"
+
+namespace Nancy {
+namespace Action {
+
+PlaySecondaryMovie::~PlaySecondaryMovie()  {
+    _decoder.close(); 
+    if (hideMouse == kTrue && unknown == 5) {
+        _engine->setMouseEnabled(true);
+    }
+}
+
+uint16 PlaySecondaryMovie::readData(Common::SeekableReadStream &stream) {  
+    char name[10];
+    stream.read(name, 10);
+    videoName = name;
+
+    stream.skip(0x12);
+    unknown = stream.readUint16LE();
+	hideMouse = (NancyFlag)stream.readUint16LE();
+    isReverse = (NancyFlag)stream.readUint16LE();
+    firstFrame = (NancyFlag)stream.readUint16LE();
+    lastFrame = (NancyFlag)stream.readUint16LE();
+    for (uint i = 0; i < 15; ++i) {
+        frameFlags[i].frameID = stream.readSint16LE();
+        frameFlags[i].flagDesc.label = stream.readSint16LE();
+        frameFlags[i].flagDesc.flag = (NancyFlag)stream.readUint16LE();
+    }
+
+    triggerFlags.readData(stream);
+    sound.read(stream, SoundDescription::kNormal);
+    sceneChange.readData(stream);
+
+    uint16 numVideoDescs = stream.readUint16LE();
+    for (uint i = 0; i < numVideoDescs; ++i) {
+        videoDescs.push_back(SecondaryVideoDescription());
+        videoDescs[i].readData(stream);
+    }
+
+    return 0xD4 + numVideoDescs * 0x42; // TODO
+}
+
+void PlaySecondaryMovie::init() {
+    if(_decoder.isVideoLoaded()) {
+        _decoder.close();
+    }
+    _decoder.loadFile(videoName + ".avf");
+    _drawSurface.create(_decoder.getWidth(), _decoder.getHeight(), GraphicsManager::pixelFormat);
+    _screenPosition = _drawSurface.getBounds();
+
+    RenderObject::init();
+}
+
+void PlaySecondaryMovie::updateGraphics() {
+    if (!_decoder.isVideoLoaded()) {
+        return;
+    }
+
+    if (!_decoder.isPlaying() && _isVisible && !isFinished) {
+        _decoder.start();
+		if (isReverse == kTrue) {
+			_decoder.setRate(-_decoder.getRate());
+			_decoder.seekToFrame(lastFrame);
+		} else {
+			_decoder.seekToFrame(firstFrame);
+		}
+    }
+
+    if (_decoder.needsUpdate()) {
+        uint descID = 0;
+        for (uint i = 0; i < videoDescs.size(); ++i) {
+            if (videoDescs[i].frameID == _curViewportFrame) {
+                descID = i;
+            }
+        }
+        _drawSurface.blitFrom(*_decoder.decodeNextFrame(), videoDescs[descID].srcRect, Common::Point());
+        _needsRedraw = true;
+        
+        for (auto f : frameFlags) {
+            if (_decoder.getCurFrame() == f.frameID) {
+                _engine->scene->setEventFlag(f.flagDesc);
+            }
+        }
+    }
+
+	if ((_decoder.getCurFrame() == lastFrame && isReverse == kFalse) ||
+	    (_decoder.getCurFrame() == firstFrame && isReverse == kTrue)) {
+		if (!_engine->sound->isSoundPlaying(sound.channelID)) {
+			_engine->sound->stopSound(sound.channelID);
+			_decoder.stop();
+            isFinished = true;
+			state = kActionTrigger;
+		}
+	}
+
+    RenderObject::updateGraphics();
+}
+
+void PlaySecondaryMovie::onPause(bool pause) {
+    _decoder.pauseVideo(pause);
+}
+
+void PlaySecondaryMovie::execute(NancyEngine *engine) {
+    switch (state) {
+        case kBegin:
+            init();
+            registerGraphics();
+            engine->sound->loadSound(sound);
+            engine->sound->playSound(sound.channelID);
+            if (hideMouse == kTrue) {
+                engine->setMouseEnabled(false);
+            }
+            state = kRun;
+            // fall through
+        case kRun: {
+            
+            int newFrame = _engine->scene->getSceneInfo().frameID;
+
+            if (newFrame != _curViewportFrame) {
+                _curViewportFrame = newFrame;
+                int activeFrame = -1;
+                for (uint i = 0; i < videoDescs.size(); ++i) {
+                    if (newFrame == videoDescs[i].frameID) {
+                        activeFrame = i;
+                        break;
+                    }
+                }
+
+                if (activeFrame != -1) {
+                    _screenPosition = videoDescs[activeFrame].destRect;
+                    setVisible(true);
+                } else {
+                    setVisible(false);
+                }
+            }
+
+            break;
+        }
+        case kActionTrigger:
+            triggerFlags.execute(engine);
+            if (unknown == 5) {
+                engine->scene->changeScene(sceneChange);
+            } else {
+                // Not changing the scene so enable the mouse now
+                if (hideMouse == kTrue) {
+                    engine->setMouseEnabled(true);
+                }
+            }
+            
+
+            finishExecution();
+            break;
+    }
+}
+
+} // End of namespace Action
+} // End of namespace Nancy
diff --git a/engines/nancy/action/secondarymovie.h b/engines/nancy/action/secondarymovie.h
new file mode 100644
index 0000000000..ded20ea50b
--- /dev/null
+++ b/engines/nancy/action/secondarymovie.h
@@ -0,0 +1,88 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef NANCY_ACTION_SECONDARYMOVIE_H
+#define NANCY_ACTION_SECONDARYMOVIE_H
+
+#include "engines/nancy/action/actionrecord.h"
+#include "engines/nancy/renderobject.h"
+
+#include "engines/nancy/video.h"
+#include "engines/nancy/commontypes.h"
+
+#include "common/str.h"
+#include "common/array.h"
+
+namespace Nancy {
+namespace Action {
+
+class PlaySecondaryMovie : public ActionRecord, public RenderObject {
+public:
+    struct FlagAtFrame {
+        int16 frameID;
+        EventFlagDescription flagDesc;
+    };
+
+    PlaySecondaryMovie(RenderObject &redrawFrom) :
+        RenderObject(redrawFrom),
+        _curViewportFrame(-1),
+        isFinished(false) {}
+    virtual ~PlaySecondaryMovie();
+
+    virtual void init() override;
+    virtual void updateGraphics() override;
+    virtual void onPause(bool pause) override;
+
+    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void execute(NancyEngine *engine) override;
+
+    Common::String videoName; // 0x00
+
+    uint16 unknown; // 0x1C
+    NancyFlag hideMouse; // 0x1E
+    NancyFlag isReverse; // 0x20
+    uint16 firstFrame; // 0x22
+    uint16 lastFrame; // 0x24
+    FlagAtFrame frameFlags[15]; // 0x26
+    MultiEventFlagDescription triggerFlags; // 0x80
+
+    SoundDescription sound; // 0xA8
+
+    SceneChangeDescription sceneChange; // 0xCA
+    Common::Array<SecondaryVideoDescription> videoDescs; // 0xD4
+
+protected:
+    virtual Common::String getRecordTypeName() const override { return "PlaySecondaryMovie"; }
+
+    virtual uint16 getZOrder() const override { return 8; }
+    virtual BlitType getBlitType() const override { return kNoTrans; }
+    virtual bool isViewportRelative() const override { return true; }
+
+    AVFDecoder _decoder;
+    int _curViewportFrame;
+    bool isFinished;
+};
+    
+} // End of namespace Action
+} // End of namespace Nancy
+
+#endif // NANCY_ACTION_SECONDARYMOVIE_H
diff --git a/engines/nancy/action/secondaryvideo.cpp b/engines/nancy/action/secondaryvideo.cpp
index c21502f061..36a3ec58f2 100644
--- a/engines/nancy/action/secondaryvideo.cpp
+++ b/engines/nancy/action/secondaryvideo.cpp
@@ -41,13 +41,6 @@
 namespace Nancy {
 namespace Action {
 
-void SecondaryVideoDesc::readData(Common::SeekableReadStream &stream) {
-    frameID = stream.readUint16LE();
-    readRect(stream, srcRect);
-    readRect(stream, destRect);
-    stream.skip(0x20);
-}
-
 void PlaySecondaryVideo::init() {
     if(_decoder.isVideoLoaded()) {
         _decoder.close();
@@ -153,7 +146,7 @@ uint16 PlaySecondaryVideo::readData(Common::SeekableReadStream &stream) {
 
     uint16 numVideoDescs = stream.readUint16LE();
     for (uint i = 0; i < numVideoDescs; ++i) {
-        videoDescs.push_back(SecondaryVideoDesc());
+        videoDescs.push_back(SecondaryVideoDescription());
         videoDescs[i].readData(stream);
     }
 
@@ -206,117 +199,5 @@ void PlaySecondaryVideo::execute(NancyEngine *engine) {
     }
 }
 
-uint16 PlaySecondaryMovie::readData(Common::SeekableReadStream &stream) {  
-    char name[10];
-    stream.read(name, 10);
-    videoName = name;
-
-    stream.skip(0x1C);
-    for (uint i = 0; i < 15; ++i) {
-        frameFlags[i].frameID = stream.readSint16LE();
-        frameFlags[i].flagDesc.label = stream.readSint16LE();
-        frameFlags[i].flagDesc.flag = (NancyFlag)stream.readUint16LE();
-    }
-
-    triggerFlags.readData(stream);
-    sound.read(stream, SoundDescription::kNormal);
-    sceneChange.readData(stream);
-
-    uint16 numVideoDescs = stream.readUint16LE();
-    for (uint i = 0; i < numVideoDescs; ++i) {
-        videoDescs.push_back(SecondaryVideoDesc());
-        videoDescs[i].readData(stream);
-    }
-
-    return 0xD4 + numVideoDescs * 0x42; // TODO
-}
-
-void PlaySecondaryMovie::init() {
-    if(_decoder.isVideoLoaded()) {
-        _decoder.close();
-    }
-    _decoder.loadFile(videoName + ".avf");
-    _drawSurface.create(_decoder.getWidth(), _decoder.getHeight(), GraphicsManager::pixelFormat);
-    _screenPosition = _drawSurface.getBounds();
-
-    RenderObject::init();
-}
-
-void PlaySecondaryMovie::updateGraphics() {
-    if (!_decoder.isVideoLoaded()) {
-        return;
-    }
-
-    if (!_decoder.isPlaying() && _isVisible) {
-        _decoder.start();
-    }
-
-    if (_decoder.needsUpdate()) {
-        uint descID = 0;
-        for (uint i = 0; i < videoDescs.size(); ++i) {
-            if (videoDescs[i].frameID == _curViewportFrame) {
-                descID = i;
-            }
-        }
-        _drawSurface.blitFrom(*_decoder.decodeNextFrame(), videoDescs[descID].srcRect, Common::Point());
-        _needsRedraw = true;
-    } else {
-        // Set flag if not drawing new frame
-        for (auto f : frameFlags) {
-            if (_decoder.getCurFrame() == f.frameID) {
-                _engine->scene->setEventFlag(f.flagDesc);
-            }
-        }
-    }
-
-    RenderObject::updateGraphics();
-}
-
-void PlaySecondaryMovie::onPause(bool pause) {
-    _decoder.pauseVideo(pause);
-}
-
-void PlaySecondaryMovie::execute(NancyEngine *engine) {
-    switch (state) {
-        case kBegin:
-            init();
-            registerGraphics();
-            engine->sound->loadSound(sound);
-            state = kRun;
-            // fall through
-        case kRun: {
-            engine->cursorManager->showCursor(false);
-            
-            int newFrame = _engine->scene->getSceneInfo().frameID;
-
-            if (newFrame != _curViewportFrame) {
-                _curViewportFrame = newFrame;
-                int activeFrame = -1;
-                for (uint i = 0; i < videoDescs.size(); ++i) {
-                    if (newFrame == videoDescs[i].frameID) {
-                        activeFrame = i;
-                        break;
-                    }
-                }
-
-                if (activeFrame != -1) {
-                    _screenPosition = videoDescs[activeFrame].destRect;
-                    engine->sound->playSound(sound.channelID);
-                    setVisible(true);
-                } else {
-                    setVisible(false);
-                }
-            }
-
-            break;
-        }
-        case kActionTrigger:
-            triggerFlags.execute(engine);
-            engine->scene->changeScene(sceneChange);
-            finishExecution();
-            break;
-    }
-}
-
 } // End of namespace Action
 } // End of namespace Nancy
diff --git a/engines/nancy/action/secondaryvideo.h b/engines/nancy/action/secondaryvideo.h
index b8c2f070ac..f20f250419 100644
--- a/engines/nancy/action/secondaryvideo.h
+++ b/engines/nancy/action/secondaryvideo.h
@@ -35,15 +35,6 @@
 namespace Nancy {
 namespace Action {
 
-struct SecondaryVideoDesc {
-    int16 frameID;
-    Common::Rect srcRect;
-    Common::Rect destRect;
-    // 2 unknown/empty rects
-
-    void readData(Common::SeekableReadStream &stream);
-};
-
 // ActionRecord that shows NPC animations outside of dialogue. Supports
 // different animations depending on whether the NPC is hovered by the mouse
 class PlaySecondaryVideo : public ActionRecord, public RenderObject {
@@ -71,7 +62,7 @@ public:
     uint16 onHoverEndLastFrame = 0; // 0x28
     SceneChangeDescription sceneChange; // 0x2A
     // unknown byte
-    Common::Array<SecondaryVideoDesc> videoDescs; // 0x35
+    Common::Array<SecondaryVideoDescription> videoDescs; // 0x35
 
 protected:
     virtual Common::String getRecordTypeName() const override { return Common::String("PlaySecondaryVideoChan" + channel); }
@@ -89,44 +80,6 @@ protected:
     char channel;
 };
 
-class PlaySecondaryMovie : public ActionRecord, public RenderObject {
-public:
-    struct FlagAtFrame {
-        int16 frameID;
-        EventFlagDescription flagDesc;
-    };
-
-    PlaySecondaryMovie(RenderObject &redrawFrom) : RenderObject(redrawFrom) {}
-    virtual ~PlaySecondaryMovie() { _decoder.close(); }
-
-    virtual void init() override;
-    virtual void updateGraphics() override;
-    virtual void onPause(bool pause) override;
-
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
-    virtual void execute(NancyEngine *engine) override;
-
-    Common::String videoName; // 0x00
-
-    FlagAtFrame frameFlags[15]; // 0x26
-    MultiEventFlagDescription triggerFlags; // 0x80
-
-    SoundDescription sound; // 0xA8
-
-    SceneChangeDescription sceneChange; // 0xCA
-    Common::Array<SecondaryVideoDesc> videoDescs; // 0xD4
-
-protected:
-    virtual Common::String getRecordTypeName() const override { return "PlaySecondaryMovie"; }
-
-    virtual uint16 getZOrder() const override { return 8; }
-    virtual BlitType getBlitType() const override { return kNoTrans; }
-    virtual bool isViewportRelative() const override { return true; }
-
-    AVFDecoder _decoder;
-    int _curViewportFrame = -1;
-};
-
 } // End of namespace Action
 } // End of namespace Nancy
 
diff --git a/engines/nancy/commontypes.cpp b/engines/nancy/commontypes.cpp
index 97731c3132..4bf8d0d00b 100644
--- a/engines/nancy/commontypes.cpp
+++ b/engines/nancy/commontypes.cpp
@@ -61,6 +61,13 @@ void MultiEventFlagDescription::execute(NancyEngine *engine) {
     }
 }
 
+void SecondaryVideoDescription::readData(Common::SeekableReadStream &stream) {
+    frameID = stream.readUint16LE();
+    readRect(stream, srcRect);
+    readRect(stream, destRect);
+    stream.skip(0x20);
+}
+
 void SoundDescription::read(Common::SeekableReadStream &stream, Type type) {
 	char buf[10];
 
diff --git a/engines/nancy/commontypes.h b/engines/nancy/commontypes.h
index f6f8052232..fb445ce6fc 100644
--- a/engines/nancy/commontypes.h
+++ b/engines/nancy/commontypes.h
@@ -79,6 +79,15 @@ struct MultiEventFlagDescription {
     void execute(Nancy::NancyEngine *engine);
 };
 
+struct SecondaryVideoDescription {
+    int16 frameID;
+    Common::Rect srcRect;
+    Common::Rect destRect;
+    // 2 unknown/empty rects
+
+    void readData(Common::SeekableReadStream &stream);
+};
+
 // Descrbes a single sound. Combines four different structs found in the data in one
 struct SoundDescription {
     enum Type { kNormal, kMenu, kDIGI, kScene };
diff --git a/engines/nancy/input.cpp b/engines/nancy/input.cpp
index 2157b62027..bac93f5a92 100644
--- a/engines/nancy/input.cpp
+++ b/engines/nancy/input.cpp
@@ -120,8 +120,12 @@ void InputManager::processEvents() {
 
 NancyInput InputManager::getInput() const {
     NancyInput ret;
-    ret.mousePos = _engine->getEventManager()->getMousePos();
     ret.input = _inputs;
+    if (_mouseEnabled) {
+        ret.mousePos = _engine->getEventManager()->getMousePos();
+    } else {
+        ret.eatMouseInput();
+    }
     ret.otherKbdInput = _otherKbdInput;
     return ret;
 }
diff --git a/engines/nancy/input.h b/engines/nancy/input.h
index dce3cc5dce..d8df3d0089 100644
--- a/engines/nancy/input.h
+++ b/engines/nancy/input.h
@@ -87,12 +87,14 @@ enum NancyAction {
 public:
     InputManager(NancyEngine *engine) :
         _engine(engine),
-        _inputs(0) {}
+        _inputs(0),
+        _mouseEnabled(true) {}
 
     void processEvents();
 
     NancyInput getInput() const;
     void forceCleanInput();
+    void setMouseInputEnabled(bool enabled) { _mouseEnabled = enabled; }
 
     static void initKeymaps(Common::KeymapArray &keymaps);
     
@@ -100,6 +102,7 @@ private:
     NancyEngine *_engine;
     uint16 _inputs;
     Common::Array<Common::KeyState> _otherKbdInput;
+    bool _mouseEnabled;
 };
 
 } // End of namespace Nancy
diff --git a/engines/nancy/module.mk b/engines/nancy/module.mk
index 888024a3f3..ba9b45d61f 100644
--- a/engines/nancy/module.mk
+++ b/engines/nancy/module.mk
@@ -9,6 +9,7 @@ MODULE_OBJS = \
   action/primaryvideo.o \
   action/recordtypes.o \
   action/rotatinglockpuzzle.o \
+  action/secondarymovie.o \
   action/secondaryvideo.o \
   action/sliderpuzzle.o \
   action/staticbitmapanim.o \
diff --git a/engines/nancy/nancy.cpp b/engines/nancy/nancy.cpp
index a7abe09dfa..99d418ebd2 100644
--- a/engines/nancy/nancy.cpp
+++ b/engines/nancy/nancy.cpp
@@ -250,6 +250,9 @@ void NancyEngine::stopAndUnloadSpecificSounds() {
 		sound->stopSound(i);
 	}
 }
+void NancyEngine::setMouseEnabled(bool enabled) {
+	cursorManager->showCursor(enabled); input->setMouseInputEnabled(enabled);
+}
 
 void NancyEngine::pauseEngineIntern(bool pause) {
 	if (pause) {
diff --git a/engines/nancy/nancy.h b/engines/nancy/nancy.h
index 2d067e2b46..1311230fba 100644
--- a/engines/nancy/nancy.h
+++ b/engines/nancy/nancy.h
@@ -126,6 +126,8 @@ public:
 	GameState getGameState() const { return _gameFlow.minGameState; }
 	GameState getPreviousGameState() const { return _gameFlow.previousGameState; }
 
+	void setMouseEnabled(bool enabled);
+
 	virtual void pauseEngineIntern(bool pause) override;
 
 	// Managers


Commit: f5ec222c95516c8eddca846c9d55d1557d297adb
    https://github.com/scummvm/scummvm/commit/f5ec222c95516c8eddca846c9d55d1557d297adb
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Fix sound channel clashes

Added functions that directly take a SoundDescription object and only execute if its name isn't NO SOUND. This fixes the elevator in nancy1 taking too long to start.

Changed paths:
    engines/nancy/action/leverpuzzle.cpp
    engines/nancy/action/orderingpuzzle.cpp
    engines/nancy/action/passwordpuzzle.cpp
    engines/nancy/action/primaryvideo.cpp
    engines/nancy/action/recordtypes.cpp
    engines/nancy/action/rotatinglockpuzzle.cpp
    engines/nancy/action/secondarymovie.cpp
    engines/nancy/action/sliderpuzzle.cpp
    engines/nancy/action/staticbitmapanim.cpp
    engines/nancy/action/telephone.cpp
    engines/nancy/sound.cpp
    engines/nancy/sound.h
    engines/nancy/state/map.cpp
    engines/nancy/state/scene.cpp


diff --git a/engines/nancy/action/leverpuzzle.cpp b/engines/nancy/action/leverpuzzle.cpp
index 53468456ac..129f234b46 100644
--- a/engines/nancy/action/leverpuzzle.cpp
+++ b/engines/nancy/action/leverpuzzle.cpp
@@ -126,12 +126,12 @@ void LeverPuzzle::execute(Nancy::NancyEngine *engine) {
                     }
 
                     engine->sound->loadSound(solveSound);
-                    _engine->sound->playSound(solveSound.channelID);
+                    _engine->sound->playSound(solveSound);
                     solveState = kWaitForSound;
                     break;
                 case kWaitForSound:
-                    if (!_engine->sound->isSoundPlaying(solveSound.channelID)) {
-                        _engine->sound->stopSound(solveSound.channelID);
+                    if (!_engine->sound->isSoundPlaying(solveSound)) {
+                        _engine->sound->stopSound(solveSound);
                         state = kActionTrigger;
                     }
 
@@ -140,8 +140,8 @@ void LeverPuzzle::execute(Nancy::NancyEngine *engine) {
 
             break;
         case kActionTrigger:
-            _engine->sound->stopSound(moveSound.channelID);
-            _engine->sound->stopSound(noMoveSound.channelID);
+            _engine->sound->stopSound(moveSound);
+            _engine->sound->stopSound(noMoveSound);
             
             if (solveState == kNotSolved) {
                 _engine->scene->changeScene(exitScene);
@@ -192,7 +192,7 @@ void LeverPuzzle::handleInput(NancyInput &input) {
                 }
 
                 if (isMoving) {
-                    _engine->sound->playSound(moveSound.channelID);
+                    _engine->sound->playSound(moveSound);
 
                     if (leverDirection[i]) {
                         // Moving down
@@ -214,7 +214,7 @@ void LeverPuzzle::handleInput(NancyInput &input) {
 
                     drawLever(i);
                 } else {
-                    _engine->sound->playSound(noMoveSound.channelID);
+                    _engine->sound->playSound(noMoveSound);
                     return;
                 }
             }
diff --git a/engines/nancy/action/orderingpuzzle.cpp b/engines/nancy/action/orderingpuzzle.cpp
index 5f3b126d69..7ce695ee7b 100644
--- a/engines/nancy/action/orderingpuzzle.cpp
+++ b/engines/nancy/action/orderingpuzzle.cpp
@@ -136,11 +136,11 @@ void OrderingPuzzle::execute(Nancy::NancyEngine *engine) {
                         break;
                     }
 
-                    _engine->sound->playSound(solveSound.channelID);
+                    _engine->sound->playSound(solveSound);
                     solveState = kWaitForSound;
                     break;
                 case kWaitForSound:
-                    if (!_engine->sound->isSoundPlaying(solveSound.channelID)) {
+                    if (!_engine->sound->isSoundPlaying(solveSound)) {
                         state = kActionTrigger;
                     }
 
@@ -148,8 +148,8 @@ void OrderingPuzzle::execute(Nancy::NancyEngine *engine) {
             }
             break;
         case kActionTrigger:
-            _engine->sound->stopSound(clickSound.channelID);
-            _engine->sound->stopSound(solveSound.channelID);
+            _engine->sound->stopSound(clickSound);
+            _engine->sound->stopSound(solveSound);
 
             if (solveState == kNotSolved) {
                 _engine->scene->changeScene(exitScene);
@@ -182,7 +182,7 @@ void OrderingPuzzle::handleInput(NancyInput &input) {
             _engine->cursorManager->setCursorType(CursorManager::kHotspot);
 
             if (input.input & NancyInput::kLeftMouseButtonUp) {
-                _engine->sound->playSound(clickSound.channelID);
+                _engine->sound->playSound(clickSound);
                 
                 for (uint j = 0; j < clickedSequence.size(); ++j) {
                     if (clickedSequence[j] == i && drawnElements[i] == true) {
diff --git a/engines/nancy/action/passwordpuzzle.cpp b/engines/nancy/action/passwordpuzzle.cpp
index 14b911c7f4..611153e835 100644
--- a/engines/nancy/action/passwordpuzzle.cpp
+++ b/engines/nancy/action/passwordpuzzle.cpp
@@ -100,12 +100,12 @@ void PasswordPuzzle::execute(Nancy::NancyEngine *engine) {
                                 passwordFieldIsActive = true;
                             } else {
                                 engine->sound->loadSound(solveSound);
-                                engine->sound->playSound(solveSound.channelID);
+                                engine->sound->playSound(solveSound);
                                 solveState = kSolved;
                             }
                         } else {
                             engine->sound->loadSound(failSound);
-                            engine->sound->playSound(failSound.channelID);
+                            engine->sound->playSound(failSound);
                             solveState = kFailed;
                         }
                         
@@ -125,15 +125,15 @@ void PasswordPuzzle::execute(Nancy::NancyEngine *engine) {
                     break;
                 }
                 case kFailed:
-                    if (!engine->sound->isSoundPlaying(failSound.channelID)) {
-                        engine->sound->stopSound(failSound.channelID);
+                    if (!engine->sound->isSoundPlaying(failSound)) {
+                        engine->sound->stopSound(failSound);
                         state = kActionTrigger;
                     }
 
                     break;
                 case kSolved:
-                    if (!engine->sound->isSoundPlaying(solveSound.channelID)) {
-                        engine->sound->stopSound(solveSound.channelID);
+                    if (!engine->sound->isSoundPlaying(solveSound)) {
+                        engine->sound->stopSound(solveSound);
                         state = kActionTrigger;
                     }
 
diff --git a/engines/nancy/action/primaryvideo.cpp b/engines/nancy/action/primaryvideo.cpp
index be8451dac4..ca4fd86524 100644
--- a/engines/nancy/action/primaryvideo.cpp
+++ b/engines/nancy/action/primaryvideo.cpp
@@ -222,7 +222,7 @@ void PlayPrimaryVideoChan0::execute(NancyEngine *engine) {
             init();
             registerGraphics();
             engine->sound->loadSound(sound);
-            engine->sound->playSound(sound.channelID);
+            engine->sound->playSound(sound);
             state = kRun;
 		    activePrimaryVideo = this;
             // fall through
@@ -249,8 +249,8 @@ void PlayPrimaryVideoChan0::execute(NancyEngine *engine) {
                 }
             }
 
-            if (!engine->sound->isSoundPlaying(sound.channelID)) {
-                engine->sound->stopSound(sound.channelID);
+            if (!engine->sound->isSoundPlaying(sound)) {
+                engine->sound->stopSound(sound);
                 if (responses.size() == 0) {
                     // NPC has finished talking with no responses available, auto-advance to next scene
                     state = kActionTrigger;
@@ -268,7 +268,7 @@ void PlayPrimaryVideoChan0::execute(NancyEngine *engine) {
                         responseGenericSound.name = responses[pickedResponse].soundName;
                         // TODO this is probably not correct
                         engine->sound->loadSound(responseGenericSound);
-                        engine->sound->playSound(responseGenericSound.channelID);
+                        engine->sound->playSound(responseGenericSound);
                         state = kActionTrigger;
                     }
                 }
@@ -287,8 +287,8 @@ void PlayPrimaryVideoChan0::execute(NancyEngine *engine) {
                 engine->scene->setEventFlag(responses[pickedResponse].flagDesc);
             }
 
-            if (!engine->sound->isSoundPlaying(responseGenericSound.channelID)) {
-                engine->sound->stopSound(responseGenericSound.channelID);
+            if (!engine->sound->isSoundPlaying(responseGenericSound)) {
+                engine->sound->stopSound(responseGenericSound);
                 
                 if (pickedResponse != -1) {
                     engine->scene->changeScene(responses[pickedResponse].sceneChange);
diff --git a/engines/nancy/action/recordtypes.cpp b/engines/nancy/action/recordtypes.cpp
index ddcbe0a902..8f12d19765 100644
--- a/engines/nancy/action/recordtypes.cpp
+++ b/engines/nancy/action/recordtypes.cpp
@@ -432,11 +432,11 @@ void PlayDigiSoundAndDie::execute(NancyEngine *engine) {
     switch (state) {
         case kBegin:
             engine->sound->loadSound(sound);
-            engine->sound->playSound(sound.channelID);
+            engine->sound->playSound(sound);
             state = kRun;
             break;
         case kRun:
-            if (!engine->sound->isSoundPlaying(sound.channelID)) {
+            if (!engine->sound->isSoundPlaying(sound)) {
                 state = kActionTrigger;
             }
             break;
@@ -446,7 +446,7 @@ void PlayDigiSoundAndDie::execute(NancyEngine *engine) {
             }
             
             engine->scene->setEventFlag(flagOnTrigger);
-            engine->sound->stopSound(sound.channelID);
+            engine->sound->stopSound(sound);
 
             finishExecution();
             break;
@@ -495,7 +495,7 @@ void PlaySoundMultiHS::execute(Nancy::NancyEngine *engine) {
         }
         case kActionTrigger:
             engine->sound->loadSound(sound);
-            engine->sound->playSound(sound.channelID);
+            engine->sound->playSound(sound);
             engine->scene->changeScene(sceneChange);
             engine->scene->setEventFlag(flag);
             finishExecution();
@@ -522,12 +522,12 @@ void HintSystem::execute(Nancy::NancyEngine *engine) {
             engine->scene->getTextbox().addTextLine(text);
 
             engine->sound->loadSound(genericSound);
-            engine->sound->playSound(genericSound.channelID);
+            engine->sound->playSound(genericSound);
             state = kRun;
             break;
         case kRun:
-            if (!engine->sound->isSoundPlaying(genericSound.channelID)) {
-                engine->sound->stopSound(genericSound.channelID);
+            if (!engine->sound->isSoundPlaying(genericSound)) {
+                engine->sound->stopSound(genericSound);
                 state = kActionTrigger;
             } else {
                 break;
diff --git a/engines/nancy/action/rotatinglockpuzzle.cpp b/engines/nancy/action/rotatinglockpuzzle.cpp
index e290d89f0e..06cc7cfd69 100644
--- a/engines/nancy/action/rotatinglockpuzzle.cpp
+++ b/engines/nancy/action/rotatinglockpuzzle.cpp
@@ -139,11 +139,11 @@ void RotatingLockPuzzle::execute(Nancy::NancyEngine *engine) {
                         break;
                     }
 
-                    _engine->sound->playSound(solveSound.channelID);
+                    _engine->sound->playSound(solveSound);
                     solveState = kWaitForSound;
                     break;
                 case kWaitForSound:
-                    if (!_engine->sound->isSoundPlaying(solveSound.channelID)) {
+                    if (!_engine->sound->isSoundPlaying(solveSound)) {
                         state = kActionTrigger;
                     }
 
@@ -151,8 +151,8 @@ void RotatingLockPuzzle::execute(Nancy::NancyEngine *engine) {
             }
             break;
         case kActionTrigger:
-            _engine->sound->stopSound(clickSound.channelID);
-            _engine->sound->stopSound(solveSound.channelID);
+            _engine->sound->stopSound(clickSound);
+            _engine->sound->stopSound(solveSound);
 
             if (solveState == kNotSolved) {
                 _engine->scene->changeScene(exitScene);
@@ -184,7 +184,7 @@ void RotatingLockPuzzle::handleInput(NancyInput &input) {
             _engine->cursorManager->setCursorType(CursorManager::kHotspot);
 
             if (input.input & NancyInput::kLeftMouseButtonUp) {
-                _engine->sound->playSound(clickSound.channelID);
+                _engine->sound->playSound(clickSound);
                 
                 currentSequence[i] = ++currentSequence[i] > 9 ? 0 : currentSequence[i];
                 drawDial(i);
@@ -198,7 +198,7 @@ void RotatingLockPuzzle::handleInput(NancyInput &input) {
             _engine->cursorManager->setCursorType(CursorManager::kHotspot);
 
             if (input.input & NancyInput::kLeftMouseButtonUp) {
-                _engine->sound->playSound(clickSound.channelID);
+                _engine->sound->playSound(clickSound);
 
                 int8 n = currentSequence[i];
                 n = --n < 0 ? 9 : n;
diff --git a/engines/nancy/action/secondarymovie.cpp b/engines/nancy/action/secondarymovie.cpp
index 420eddee27..66c502eb13 100644
--- a/engines/nancy/action/secondarymovie.cpp
+++ b/engines/nancy/action/secondarymovie.cpp
@@ -111,8 +111,8 @@ void PlaySecondaryMovie::updateGraphics() {
 
 	if ((_decoder.getCurFrame() == lastFrame && isReverse == kFalse) ||
 	    (_decoder.getCurFrame() == firstFrame && isReverse == kTrue)) {
-		if (!_engine->sound->isSoundPlaying(sound.channelID)) {
-			_engine->sound->stopSound(sound.channelID);
+		if (!_engine->sound->isSoundPlaying(sound)) {
+			_engine->sound->stopSound(sound);
 			_decoder.stop();
             isFinished = true;
 			state = kActionTrigger;
@@ -132,7 +132,7 @@ void PlaySecondaryMovie::execute(NancyEngine *engine) {
             init();
             registerGraphics();
             engine->sound->loadSound(sound);
-            engine->sound->playSound(sound.channelID);
+            engine->sound->playSound(sound);
             if (hideMouse == kTrue) {
                 engine->setMouseEnabled(false);
             }
diff --git a/engines/nancy/action/sliderpuzzle.cpp b/engines/nancy/action/sliderpuzzle.cpp
index 49ce4c6e3e..c78eb69611 100644
--- a/engines/nancy/action/sliderpuzzle.cpp
+++ b/engines/nancy/action/sliderpuzzle.cpp
@@ -149,12 +149,12 @@ void SliderPuzzle::execute(Nancy::NancyEngine *engine) {
                     }
 
                     engine->sound->loadSound(solveSound);
-                    engine->sound->playSound(solveSound.channelID);
+                    engine->sound->playSound(solveSound);
                     solveState = kWaitForSound;
                     break;
                 case kWaitForSound:
-                    if (!engine->sound->isSoundPlaying(solveSound.channelID)) {
-                        engine->sound->stopSound(solveSound.channelID);
+                    if (!engine->sound->isSoundPlaying(solveSound)) {
+                        engine->sound->stopSound(solveSound);
                         state = kActionTrigger;
                     }
 
@@ -175,7 +175,7 @@ void SliderPuzzle::execute(Nancy::NancyEngine *engine) {
                     break;
             }
 
-            engine->sound->stopSound(clickSound.channelID);
+            engine->sound->stopSound(clickSound);
             finishExecution();
     }
 }
@@ -244,7 +244,7 @@ void SliderPuzzle::handleInput(NancyInput &input) {
         _engine->cursorManager->setCursorType(CursorManager::kHotspot);
 
         if (input.input & NancyInput::kLeftMouseButtonUp) {
-            _engine->sound->playSound(clickSound.channelID);
+            _engine->sound->playSound(clickSound);
             switch (direction) {
                 case kUp: {
                     uint curTileID = playerTileOrder[currentTileY][currentTileX];
diff --git a/engines/nancy/action/staticbitmapanim.cpp b/engines/nancy/action/staticbitmapanim.cpp
index 15e2760f49..d78da910a7 100644
--- a/engines/nancy/action/staticbitmapanim.cpp
+++ b/engines/nancy/action/staticbitmapanim.cpp
@@ -98,7 +98,7 @@ void PlayStaticBitmapAnimation::execute(NancyEngine *engine) {
             init();
             registerGraphics();
             _engine->sound->loadSound(sound);
-            _engine->sound->playSound(sound.channelID);
+            _engine->sound->playSound(sound);
             state = kRun;
             // fall through
         case kRun: {
@@ -108,7 +108,7 @@ void PlayStaticBitmapAnimation::execute(NancyEngine *engine) {
                 if (engine->scene->getEventFlag(interruptCondition) ||
                     (   (((currentFrame == loopLastFrame) && (isReverse == kFalse) && (isLooping == kFalse)) ||
                         ((currentFrame == loopFirstFrame) && (isReverse == kTrue) && (isLooping == kFalse))) &&
-                            !engine->sound->isSoundPlaying(sound.channelID))   ) {
+                            !engine->sound->isSoundPlaying(sound))   ) {
                     
                     state = kActionTrigger;
 
@@ -116,8 +116,8 @@ void PlayStaticBitmapAnimation::execute(NancyEngine *engine) {
                     // nancy1's safe lock light not turning off.
                     setVisible(false);
         
-                    if (!engine->sound->isSoundPlaying(sound.channelID)) {
-                        engine->sound->stopSound(sound.channelID);
+                    if (!engine->sound->isSoundPlaying(sound)) {
+                        engine->sound->stopSound(sound);
                     }
                 } else {
                     // Check if we've moved the viewport
diff --git a/engines/nancy/action/telephone.cpp b/engines/nancy/action/telephone.cpp
index 960e306808..db176baffe 100644
--- a/engines/nancy/action/telephone.cpp
+++ b/engines/nancy/action/telephone.cpp
@@ -123,7 +123,7 @@ void Telephone::execute(NancyEngine *engine) {
             init();
             registerGraphics();
             _engine->sound->loadSound(dialToneSound);
-            _engine->sound->playSound(dialToneSound.channelID);
+            _engine->sound->playSound(dialToneSound);
             _engine->scene->getTextbox().clear();
             _engine->scene->getTextbox().addTextLine(addressBookString);
             state = kRun;
@@ -136,21 +136,21 @@ void Telephone::execute(NancyEngine *engine) {
                         _engine->scene->getTextbox().clear();
                         _engine->scene->getTextbox().addTextLine("ringing...<n><e>"); // Hardcoded in the original engine
                         _engine->sound->loadSound(ringSound);
-                        _engine->sound->playSound(ringSound.channelID);
+                        _engine->sound->playSound(ringSound);
                         callState = kRinging;
                     }
                     break;
                 case kButtonPress:
-                    if (!_engine->sound->isSoundPlaying(genericButtonSound.channelID)) {
-                        _engine->sound->stopSound(genericButtonSound.channelID);
+                    if (!_engine->sound->isSoundPlaying(genericButtonSound)) {
+                        _engine->sound->stopSound(genericButtonSound);
                         undrawButton(selected);
                         callState = kWaiting;
                     }
 
                     break;
                 case kRinging:
-                    if (!_engine->sound->isSoundPlaying(ringSound.channelID)) {
-                        _engine->sound->stopSound(ringSound.channelID);
+                    if (!_engine->sound->isSoundPlaying(ringSound)) {
+                        _engine->sound->stopSound(ringSound);
 
                         uint numberLength = calledNumber[0] == 1 ? 11 : 7;
                         for (uint i = 0; i < calls.size(); ++i) {
@@ -172,7 +172,7 @@ void Telephone::execute(NancyEngine *engine) {
 
                             genericDialogueSound.name = calls[i].soundName;
                             _engine->sound->loadSound(genericDialogueSound);
-                            _engine->sound->playSound(genericDialogueSound.channelID);
+                            _engine->sound->playSound(genericDialogueSound);
                             selected = i;
                             callState = kCall;
 
@@ -183,31 +183,31 @@ void Telephone::execute(NancyEngine *engine) {
                         _engine->scene->getTextbox().addTextLine(dialAgainString);
 
                         _engine->sound->loadSound(dialAgainSound);
-                        _engine->sound->playSound(dialAgainSound.channelID);
+                        _engine->sound->playSound(dialAgainSound);
                         callState = kBadNumber;
                         return;
                     }
 
                     break;
                 case kBadNumber:
-                    if (!_engine->sound->isSoundPlaying(dialAgainSound.channelID)) {
-                        _engine->sound->stopSound(dialAgainSound.channelID);
+                    if (!_engine->sound->isSoundPlaying(dialAgainSound)) {
+                        _engine->sound->stopSound(dialAgainSound);
 
                         state = kActionTrigger;
                     }
 
                     break;
                 case kCall:
-                    if (!_engine->sound->isSoundPlaying(genericDialogueSound.channelID)) {
-                        _engine->sound->stopSound(genericDialogueSound.channelID);
+                    if (!_engine->sound->isSoundPlaying(genericDialogueSound)) {
+                        _engine->sound->stopSound(genericDialogueSound);
 
                         state = kActionTrigger;
                     }
 
                     break;
                 case kHangUp:
-                    if (!_engine->sound->isSoundPlaying(hangUpSound.channelID)) {
-                        _engine->sound->stopSound(hangUpSound.channelID);
+                    if (!_engine->sound->isSoundPlaying(hangUpSound)) {
+                        _engine->sound->stopSound(hangUpSound);
 
                         state = kActionTrigger;
                     }
@@ -268,7 +268,7 @@ void Telephone::handleInput(NancyInput &input) {
 
         if (input.input & NancyInput::kLeftMouseButtonUp) {
             _engine->sound->loadSound(hangUpSound);
-            _engine->sound->playSound(hangUpSound.channelID);
+            _engine->sound->playSound(hangUpSound);
 
             callState = kHangUp;
         }
@@ -277,14 +277,14 @@ void Telephone::handleInput(NancyInput &input) {
 
     if (buttonNr != -1) {
         if (input.input & NancyInput::kLeftMouseButtonUp) {
-            if (_engine->sound->isSoundPlaying(dialToneSound.channelID)) {
-                _engine->sound->stopSound(dialToneSound.channelID);
+            if (_engine->sound->isSoundPlaying(dialToneSound)) {
+                _engine->sound->stopSound(dialToneSound);
             }
 
             calledNumber.push_back(buttonNr);
             genericButtonSound.name = buttonSoundNames[buttonNr];
             _engine->sound->loadSound(genericButtonSound);
-            _engine->sound->playSound(genericButtonSound.channelID);
+            _engine->sound->playSound(genericButtonSound);
 
             drawButton(buttonNr);
 
diff --git a/engines/nancy/sound.cpp b/engines/nancy/sound.cpp
index 1d0b879cf5..b1af23e158 100644
--- a/engines/nancy/sound.cpp
+++ b/engines/nancy/sound.cpp
@@ -188,6 +188,10 @@ SoundManager::~SoundManager() {
 }
 
 void SoundManager::loadSound(const SoundDescription &description) {
+	if (description.name == "NO SOUND") {
+		return;
+	}
+
 	if (_mixer->isSoundHandleActive(_channels[description.channelID].handle)) {
 		_mixer->stopHandle(_channels[description.channelID].handle);
 	}
@@ -199,9 +203,6 @@ void SoundManager::loadSound(const SoundDescription &description) {
 	_channels[description.channelID].numLoops = description.numLoops;
 	_channels[description.channelID].volume = description.volume;
 
-	if (description.name == "NO SOUND") {
-		return;
-	}
 
 	Common::SeekableReadStream *file = SearchMan.createReadStreamForMember(description.name + ".his");
 	if (file) {
@@ -210,7 +211,7 @@ void SoundManager::loadSound(const SoundDescription &description) {
 }
 
 void SoundManager::playSound(uint16 channelID) {
-	if (channelID > 32 || _channels[channelID].stream == 0 || _channels->name == "NO SOUND")
+	if (channelID > 32 || _channels[channelID].stream == 0)
 		return;
 
 	_channels[channelID].stream->seek(0);
@@ -223,6 +224,12 @@ void SoundManager::playSound(uint16 channelID) {
 						0, DisposeAfterUse::NO);
 }
 
+void SoundManager::playSound(const SoundDescription &description) {
+	if (description.name != "NO SOUND") {
+		playSound(description.channelID);
+	}
+}
+
 void SoundManager::pauseSound(uint16 channelID, bool pause) {
 	if (channelID > 32)
 		return;
@@ -232,6 +239,27 @@ void SoundManager::pauseSound(uint16 channelID, bool pause) {
 	}
 }
 
+void SoundManager::pauseSound(const SoundDescription &description, bool pause) {
+	if (description.name != "NO SOUND") {
+		pauseSound(description.channelID, pause);
+	}
+}
+
+bool SoundManager::isSoundPlaying(uint16 channelID) {
+	if (channelID > 32)
+		return false;
+	
+	return _mixer->isSoundHandleActive(_channels[channelID].handle);
+}
+
+bool SoundManager::isSoundPlaying(const SoundDescription &description) {
+	if (description.name == "NO SOUND") {
+		return false;
+	} else {
+		return isSoundPlaying(description.channelID);
+	}
+}
+
 void SoundManager::stopSound(uint16 channelID) {
 	if (channelID > 32)
 		return;
@@ -244,11 +272,10 @@ void SoundManager::stopSound(uint16 channelID) {
 	_channels[channelID].stream = nullptr;
 }
 
-bool SoundManager::isSoundPlaying(uint16 channelID) {
-	if (channelID > 32)
-		return false;
-	
-	return _mixer->isSoundHandleActive(_channels[channelID].handle);
+void SoundManager::stopSound(const SoundDescription &description) {
+	if (description.name != "NO SOUND") {
+		stopSound(description.channelID);
+	}
 }
 
 // Returns whether the exception was skipped
diff --git a/engines/nancy/sound.h b/engines/nancy/sound.h
index e54cfce4ce..9eb5eece9d 100644
--- a/engines/nancy/sound.h
+++ b/engines/nancy/sound.h
@@ -51,11 +51,13 @@ public:
     void loadSound(const SoundDescription &description);
 
     void playSound(uint16 channelID);
+    void playSound(const SoundDescription &description);
     void pauseSound(uint16 channelID, bool pause);
+    void pauseSound(const SoundDescription &description, bool pause);
     bool isSoundPlaying(uint16 channelID);
-
-    // Stop playing a sound and unload it from the channel
+    bool isSoundPlaying(const SoundDescription &description);
     void stopSound(uint16 channelID);
+    void stopSound(const SoundDescription &description);
     void stopAllSounds();
 
     static Audio::SeekableAudioStream *makeHISStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse);
diff --git a/engines/nancy/state/map.cpp b/engines/nancy/state/map.cpp
index ed092cdb71..505f763adf 100644
--- a/engines/nancy/state/map.cpp
+++ b/engines/nancy/state/map.cpp
@@ -76,7 +76,7 @@ void Map::init() {
     SoundDescription sound;
     sound.read(*chunk, SoundDescription::kMenu);
     _engine->sound->loadSound(sound);
-    _engine->sound->playSound(sound.channelID);
+    _engine->sound->playSound(sound);
 
     for (uint i = 0; i < 4; ++i) {
         chunk->seek(0x162 + i * 16, SEEK_SET);
@@ -145,7 +145,7 @@ void Map::stopSound() {
     SoundDescription sound;
     chunk->seek(0x18 + _mapID * 0x20, SEEK_SET);
     sound.read(*chunk, SoundDescription::kMenu);
-    _engine->sound->stopSound(sound.channelID);
+    _engine->sound->stopSound(sound);
 }
 
 void Map::registerGraphics() {
diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index 80df69949d..4341f8c22c 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -57,7 +57,7 @@ void Scene::process() {
         if (!_sceneState._doNotStartSound) {
             _engine->stopAndUnloadSpecificSounds();
             _engine->sound->loadSound(_sceneState.summary.sound);
-            _engine->sound->playSound(_sceneState.summary.sound.channelID);
+            _engine->sound->playSound(_sceneState.summary.sound);
         }
         // fall through
     case kRun:


Commit: 9626437921679d06b3ed5e90dca3ffbdf5671914
    https://github.com/scummvm/scummvm/commit/9626437921679d06b3ed5e90dca3ffbdf5671914
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Implement cheat dialogs

The original engine has two secret debug/cheat dialogs: one for general data (timers, current scene, inventory), and one for event flags. I decided to implement them as I find them much more useful than just using debug commands, particularly since they contain descriptions of every event flag and inventory item (this data is pulled directly from the executable's embedded resources, albeit in a slightly hacky way). Also, I've decided not to implement the slider puzzle tab present in nancy1 since I have no use for it.

Changed paths:
  A engines/nancy/cheat.cpp
  A engines/nancy/cheat.h
    engines/nancy/input.cpp
    engines/nancy/input.h
    engines/nancy/module.mk
    engines/nancy/nancy.cpp
    engines/nancy/nancy.h
    engines/nancy/state/scene.h
    engines/nancy/time.h


diff --git a/engines/nancy/cheat.cpp b/engines/nancy/cheat.cpp
new file mode 100644
index 0000000000..26a1831681
--- /dev/null
+++ b/engines/nancy/cheat.cpp
@@ -0,0 +1,346 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/nancy/cheat.h"
+
+#include "engines/nancy/nancy.h"
+#include "engines/nancy/iff.h"
+#include "engines/nancy/state/scene.h"
+
+#include "common/winexe.h"
+#include "common/stream.h"
+#include "common/translation.h"
+
+#include "gui/widgets/tab.h"
+#include "gui/widgets/edittext.h"
+
+namespace Nancy {
+
+CheatDialog::CheatDialog(NancyEngine *engine) :
+        GUI::Dialog(20, 20, 600, 440),
+        _engine(engine) {
+    _backgroundType = GUI::ThemeEngine::kDialogBackgroundSpecial;
+    Common::WinResources *res = Common::WinResources::createFromEXE("game.exe");
+    Common::Array<Common::WinResourceID> dialogIDs = res->getIDList(Common::kWinDialog);
+    State::SceneInfo scene = _engine->scene->getSceneInfo();
+    Time playerTime = _engine->scene->_timers.playerTime;
+    Time timerTime = _engine->scene->_timers.timerTime;
+    bool timerIsActive = _engine->scene->_timers.timerIsActive;
+    if (!timerIsActive) {
+        timerTime = 0;
+    }
+    char buf[4];
+
+    GUI::TabWidget *_tabs = new GUI::TabWidget(this, 0, 0, 600, 370);
+    new GUI::ButtonWidget(this, 420, 410, 60, 20, _("Cancel"), Common::U32String(), GUI::kCloseCmd);
+    new GUI::ButtonWidget(this, 520, 410, 60, 20, _("Ok"), Common::U32String(), GUI::kOKCmd);
+
+    _tabs->addTab(_("General"), _("Cheat.General"));
+    
+    new GUI::StaticTextWidget(_tabs, 30, 20, 150, 20, _("Scene Data"), Graphics::kTextAlignLeft);
+    _restartScene = new GUI::CheckboxWidget(_tabs, 35, 50, 150, 20, _("Restart the Scene"), _(""));
+    _scene = new GUI::EditTextWidget(_tabs, 35, 75, 45, 20, _(itoa(scene.sceneID, buf, 10)), _(""), kInputSceneNr, kInputSceneNr);
+    new GUI::StaticTextWidget(_tabs, 85, 75, 150, 20, _("Scene Number"), Graphics::kTextAlignLeft);
+    _frame = new GUI::EditTextWidget(_tabs, 35, 100, 45, 20, _(itoa(scene.frameID, buf, 10)), _(""), kInputFrameNr, kInputFrameNr);
+    new GUI::StaticTextWidget(_tabs, 85, 100, 150, 20, _("Frame Number"), Graphics::kTextAlignLeft);
+    _offset = new GUI::EditTextWidget(_tabs, 35, 125, 45, 20, _(itoa(scene.verticalOffset, buf, 10)), _(""), kInputScroll, kInputScroll);
+    new GUI::StaticTextWidget(_tabs, 85, 125, 150, 20, _("Background Top (Y)"), Graphics::kTextAlignLeft);
+
+    new GUI::StaticTextWidget(_tabs, 30, 160, 150, 20, _("Hints Remaining"), Graphics::kTextAlignLeft);
+    new GUI::StaticTextWidget(_tabs, 35, 185, 45, 20, _("Easy"), Graphics::kTextAlignLeft);
+    _hintsRemainingEasy = new GUI::EditTextWidget(_tabs, 35, 205, 45, 20, _(itoa(_engine->scene->_hintsRemaining[0], buf, 10)), _(""), kInputHintsEasy, kInputHintsEasy);
+    new GUI::StaticTextWidget(_tabs, 85, 185, 45, 20, _("Medium"), Graphics::kTextAlignLeft);
+    _hintsRemainingMedium = new GUI::EditTextWidget(_tabs, 85, 205, 45, 20, _(itoa(_engine->scene->_hintsRemaining[1], buf, 10)), _(""), kInputHintsMedium, kInputHintsMedium);
+    new GUI::StaticTextWidget(_tabs, 135, 185, 45, 20, _("Hard"), Graphics::kTextAlignLeft);
+    _hintsRemainingHard = new GUI::EditTextWidget(_tabs, 135, 205, 45, 20, _(itoa(_engine->scene->_hintsRemaining[2], buf, 10)), _(""), kInputHintsHard, kInputHintsHard);
+    
+    new GUI::StaticTextWidget(_tabs, 250, 20, 150, 20, _("Player Data"), Graphics::kTextAlignLeft);
+    new GUI::StaticTextWidget(_tabs, 255, 50, 150, 20, _("Player Time:"), Graphics::kTextAlignLeft);
+    _playerTimeDays = new GUI::EditTextWidget(_tabs, 255, 75, 35, 20, _(itoa(playerTime.getDays(), buf, 10)), _(""), kInputPlayerTime, kInputPlayerTime);
+    new GUI::StaticTextWidget(_tabs, 295, 75, 40, 20, _("Days"), Graphics::kTextAlignLeft);
+    _playerTimeHours =new GUI::EditTextWidget(_tabs, 335, 75, 35, 20, _(itoa(playerTime.getHours(), buf, 10)), _(""), kInputPlayerTime, kInputPlayerTime);
+    new GUI::StaticTextWidget(_tabs, 375, 75, 40, 20, _("Hours"), Graphics::kTextAlignLeft);
+    _playerTimeMinutes =new GUI::EditTextWidget(_tabs, 415, 75, 35, 20, _(itoa(playerTime.getMinutes(), buf, 10)), _(""), kInputPlayerTime, kInputPlayerTime);
+    new GUI::StaticTextWidget(_tabs, 455, 75, 50, 20, _("Minutes"), Graphics::kTextAlignLeft);
+    _difficulty = new GUI::EditTextWidget(_tabs, 255, 105, 35, 20, _(itoa(_engine->scene->_difficulty, buf, 10)), _(""), kInputDifficulty, kInputDifficulty);
+    new GUI::StaticTextWidget(_tabs, 295, 105, 150, 20, _("Player Difficulty Level"), Graphics::kTextAlignLeft);
+
+    new GUI::StaticTextWidget(_tabs, 250, 140, 150, 20, _("Player Data"), Graphics::kTextAlignLeft);
+    _timerOn = new GUI::CheckboxWidget(_tabs, 255, 170, 150, 20, _("Timer On"), _(""));
+    _timerOn->setState(timerIsActive);
+    _timerHours = new GUI::EditTextWidget(_tabs, 255, 195, 35, 20, _(itoa(timerTime.getTotalHours(), buf, 10)), _(""), kInputTimer, kInputTimer);
+    new GUI::StaticTextWidget(_tabs, 295, 195, 40, 20, _("Hours"), Graphics::kTextAlignLeft);
+    _timerMinutes = new GUI::EditTextWidget(_tabs, 335, 195, 35, 20, _(itoa(timerTime.getMinutes(), buf, 10)), _(""), kInputTimer, kInputTimer);
+    new GUI::StaticTextWidget(_tabs, 375, 195, 50, 20, _("Minutes"), Graphics::kTextAlignLeft);
+    _timerSeconds = new GUI::EditTextWidget(_tabs, 425, 195, 35, 20, _(itoa(timerTime.getSeconds(), buf, 10)), _(""), kInputTimer, kInputTimer);
+    new GUI::StaticTextWidget(_tabs, 465, 195, 50, 20, _("Seconds"), Graphics::kTextAlignLeft);
+
+    _tabs->addTab(_("Inventory"), _("Cheat.Inventory"));
+
+    for (uint i = 0; i < dialogIDs.size(); ++i) {
+        Common::SeekableReadStream *resStream = res->getResource(Common::kWinDialog, dialogIDs[i].getID());
+        
+        Common::String idString;
+        resStream->skip(0x16);
+        while (true) {
+            char add = resStream->readByte();
+            if (add != 0) {
+                idString += add;
+                resStream->skip(1);
+            } else {
+                resStream->skip(1);
+                break;
+            }
+        }
+
+        if (!idString.hasPrefix("Inventory")) {
+            continue;
+        }
+
+        idString.trim();
+        uint numItems = 0;
+        
+        while (resStream->pos() < resStream->size()) {
+            if (resStream->readUint16LE() == 0xFFFF && resStream->readSint16LE() == 0x80) {
+                // Found a resource, read its string id
+                Common::String itemLabel;
+
+                while (true) {
+                    char add = resStream->readByte();
+                    if (add != 0) {
+                        itemLabel += add;
+                        resStream->skip(1);
+                    } else {
+                        resStream->skip(1);
+                        break;
+                    }
+                }
+                GUI::CheckboxWidget *box = new GUI::CheckboxWidget(_tabs, 250 * (numItems / 10) + 20, (350 / 10) * (numItems % 10) + 15, 250, 250/10, _(itemLabel), Common::U32String());
+                box->setState(_engine->scene->hasItem(numItems) == kTrue);
+                _inventory.push_back(box);
+
+                ++numItems;
+            }
+        }
+
+        break;
+    }
+
+    _tabs->setActiveTab(0);
+}
+
+void CheatDialog::handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) {
+    switch (cmd) {
+        case GUI::kOKCmd: {
+            if (_restartScene->getState()) {
+                uint sceneID = atoi(Common::String(_scene->getEditString()).c_str());
+                IFF iff(_engine, Common::String::format("S%u", sceneID));
+                if (iff.load()) {
+                    _engine->scene->changeScene(
+                        atoi(Common::String(_scene->getEditString()).c_str()),
+                        atoi(Common::String(_frame->getEditString()).c_str()),
+                        atoi(Common::String(_offset->getEditString()).c_str()),
+                        true);
+                } else {
+                    new GUI::StaticTextWidget(this, 20, 410, 150, 20, _("Invalid Scene ID!"), Graphics::kTextAlignLeft);
+                    return;
+                }
+            }
+
+            if (_timerOn->getState()) {
+                _engine->scene->_timers.timerIsActive = true;
+                Time &timer = _engine->scene->_timers.timerTime;
+                timer = 0;
+                timer += 1000 * atoi(Common::String(_timerSeconds->getEditString()).c_str());
+                timer += 60000 * atoi(Common::String(_timerMinutes->getEditString()).c_str());
+                timer += 3600000 * atoi(Common::String(_timerHours->getEditString()).c_str());
+            } else {
+                _engine->scene->stopTimer();
+            }
+
+            Time &playerTime = _engine->scene->_timers.timerTime;
+            playerTime = 0;
+            playerTime += 60000 * atoi(Common::String(_playerTimeMinutes->getEditString()).c_str());
+            playerTime += 3600000 * atoi(Common::String(_playerTimeHours->getEditString()).c_str());
+            playerTime += 86400000 * atoi(Common::String(_playerTimeMinutes->getEditString()).c_str());
+
+            _engine->scene->_difficulty = atoi(Common::String(_difficulty->getEditString()).c_str());
+            _engine->scene->_hintsRemaining[0] = atoi(Common::String(_hintsRemainingEasy->getEditString()).c_str());
+            _engine->scene->_hintsRemaining[1] = atoi(Common::String(_hintsRemainingMedium->getEditString()).c_str());
+            _engine->scene->_hintsRemaining[2] = atoi(Common::String(_hintsRemainingHard->getEditString()).c_str());
+
+            for (uint i = 0; i < _inventory.size(); ++i) {
+                if (_engine->scene->hasItem(i) == kTrue && !_inventory[i]->getState()) {
+                    _engine->scene->removeItemFromInventory(i, false);
+                } else if (_engine->scene->hasItem(i) == kFalse && _inventory[i]->getState()) {
+                    _engine->scene->addItemToInventory(i);
+                }
+            }
+            cmd = GUI::kCloseCmd;
+
+            break;
+        }
+        case kInputSceneNr:
+            sanitizeInput(_scene);
+            break;
+        case kInputFrameNr: 
+            sanitizeInput(_frame);
+            break;
+        case kInputScroll:
+            sanitizeInput(_offset);
+            break;
+        case kInputDifficulty:
+            sanitizeInput(_scene, 2);
+            break;
+        case kInputHintsEasy:
+            sanitizeInput(_hintsRemainingEasy);
+            break;
+        case kInputHintsMedium:
+            sanitizeInput(_hintsRemainingMedium);
+            break;
+        case kInputHintsHard:
+            sanitizeInput(_hintsRemainingHard);
+            break;
+        case kInputPlayerTime:
+            sanitizeInput(_playerTimeMinutes, 59);
+            sanitizeInput(_playerTimeHours, 23);
+            sanitizeInput(_playerTimeDays);
+            break;
+        case kInputTimer:
+            sanitizeInput(_timerSeconds, 59);
+            sanitizeInput(_timerMinutes, 59);
+            sanitizeInput(_timerHours, 23);
+            break;
+        default:
+            break;
+    }
+    
+    Dialog::handleCommand(sender, cmd, data);
+}
+
+void CheatDialog::sanitizeInput(GUI::EditTextWidget *textWidget, int maxValue) {
+    const Common::U32String &str = textWidget->getEditString();
+    for (uint i = 0; i < str.size(); ++i) {
+        if (!Common::isDigit(str[i])) {
+            textWidget->setEditString(str.substr(0, i));
+            break;
+        }
+    }
+
+    if (maxValue > -1) {
+        int number = atoi(Common::String(str).c_str());
+        if (number > maxValue) {
+            char *buf = new char[str.size() + 1];
+            textWidget->setEditString(_(itoa(maxValue, buf, 10)));
+            delete[] buf;
+        }
+    }
+
+    textWidget->setCaretPos(str.size());
+}
+
+EventFlagDialog::EventFlagDialog(NancyEngine *engine) :
+        GUI::Dialog(20, 20, 600, 440),
+        _engine(engine) {
+    _backgroundType = GUI::ThemeEngine::kDialogBackgroundSpecial;
+    Common::WinResources *res = Common::WinResources::createFromEXE("game.exe");
+    Common::Array<Common::WinResourceID> dialogIDs = res->getIDList(Common::kWinDialog);
+
+    GUI::TabWidget *_tabs = new GUI::TabWidget(this, 0, 0, 600, 370);
+    new GUI::ButtonWidget(this, 520, 410, 60, 20, _("Close"), Common::U32String(), GUI::kCloseCmd);
+
+    for (uint i = 0; i < dialogIDs.size(); ++i) {
+        Common::SeekableReadStream *resStream = res->getResource(Common::kWinDialog, dialogIDs[i].getID());
+        
+        Common::String idString;
+        resStream->skip(0x16);
+        while (true) {
+            char add = resStream->readByte();
+            if (add != 0) {
+                idString += add;
+                resStream->skip(1);
+            } else {
+                resStream->skip(1);
+                break;
+            }
+        }
+
+        if (!idString.hasPrefix("Event")) {
+            continue;
+        }
+        
+        idString.trim();
+        _tabs->addTab(idString, Common::String("tab " + idString));
+        uint numFlags = 0;
+
+        while (resStream->pos() < resStream->size()) {
+            if (resStream->readUint16LE() == 0xFFFF && resStream->readSint16LE() == 0x80) {
+                // Found a resource, read its string id
+                Common::String flagLabel;
+
+                while (true) {
+                    char add = resStream->readByte();
+                    if (add != 0) {
+                        flagLabel += add;
+                        resStream->skip(1);
+                    } else {
+                        resStream->skip(1);
+                        break;
+                    }
+                }
+
+                // String begins with number so we assume it's an event flag radio button
+                if (Common::isDigit(flagLabel.firstChar())) {
+                    Common::String num;
+                    uint j = 0;
+                    while (true) {
+                        if (Common::isDigit(flagLabel[j])) {
+                            num += flagLabel[j];
+                        } else {
+                            break;
+                        }
+                        ++j;
+                    }
+
+                    uint32 command = atoi(num.c_str()) << 16 | 'ev';
+
+                    GUI::CheckboxWidget *box = new GUI::CheckboxWidget(_tabs, 300 * (numFlags / 12) + 20, (350 / 12) * (numFlags % 12) + 15, 300, 350/12, _(flagLabel), Common::U32String(), command);
+                    box->setState(_engine->scene->getEventFlag(command >> 16));
+
+                    ++numFlags;
+                }
+            }
+        }
+    }
+
+    _tabs->setActiveTab(0);
+}
+
+void EventFlagDialog::handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) {
+    Dialog::handleCommand(sender, cmd, data);
+    if (cmd & 'ev') {
+        cmd >>= 16;
+        _engine->scene->setEventFlag(cmd, data == 0 ? kFalse : kTrue);
+    }
+}
+
+} // End of namespace Nancy
diff --git a/engines/nancy/cheat.h b/engines/nancy/cheat.h
new file mode 100644
index 0000000000..ab29eef3d8
--- /dev/null
+++ b/engines/nancy/cheat.h
@@ -0,0 +1,100 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+
+#ifndef NANCY_CHEAT_H
+#define NANCY_CHEAT_H
+
+#include "engines/nancy/commontypes.h"
+
+#include "gui/dialog.h"
+#include "gui/widget.h"
+
+#include "common/str.h"
+#include "common/array.h"
+
+namespace GUI {
+class TabWidget;
+class EditTextWidget;
+}
+
+namespace Nancy {
+
+class NancyEngine;
+
+class CheatDialog : public GUI::Dialog {
+public:
+    CheatDialog(NancyEngine *engine);
+
+protected:
+    enum Commands {
+        kInputSceneNr = 'isnr',
+        kInputFrameNr = 'ifnr',
+        kInputScroll = 'iscr',
+        kInputHintsEasy = 'ihea',
+        kInputHintsMedium = 'ihme',
+        kInputHintsHard = 'ihha',
+        kInputPlayerTime = 'plti',
+        kInputDifficulty = 'diff',
+        kInputTimer = 'time'
+    };
+
+    void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) override;
+    static void sanitizeInput(GUI::EditTextWidget *textWidget, int maxValue = -1);
+    
+    NancyEngine *_engine;
+
+    GUI::CheckboxWidget *_restartScene;
+    GUI::EditTextWidget *_scene;
+    GUI::EditTextWidget *_frame;
+    GUI::EditTextWidget *_offset;
+
+    GUI::EditTextWidget *_hintsRemainingEasy;
+    GUI::EditTextWidget *_hintsRemainingMedium;
+    GUI::EditTextWidget *_hintsRemainingHard;
+
+    GUI::EditTextWidget *_playerTimeDays;
+    GUI::EditTextWidget *_playerTimeHours;
+    GUI::EditTextWidget *_playerTimeMinutes;
+    GUI::EditTextWidget *_difficulty;
+
+    GUI::CheckboxWidget *_timerOn;
+    GUI::EditTextWidget *_timerHours;
+    GUI::EditTextWidget *_timerMinutes;
+    GUI::EditTextWidget *_timerSeconds;
+
+    Common::Array<GUI::CheckboxWidget *> _inventory;
+};
+
+class EventFlagDialog : public GUI::Dialog {
+public:
+    EventFlagDialog(NancyEngine *engine);
+
+protected:
+    void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) override;
+    
+    NancyEngine *_engine;
+};
+
+} // End of namespace Nancy
+
+#endif // NANCY_CHEAT_H
diff --git a/engines/nancy/input.cpp b/engines/nancy/input.cpp
index bac93f5a92..4922a20127 100644
--- a/engines/nancy/input.cpp
+++ b/engines/nancy/input.cpp
@@ -76,8 +76,13 @@ void InputManager::processEvents() {
                     case kNancyActionMoveFast:
                         _inputs |= NancyInput::kMoveFastModifier;
                         break;
+                    case kNancyActionRequestCheatMenu:
+                        _engine->callCheatMenu(false);
+                        break;
+                    case kNancyActionRequestEventMenu:
+                        _engine->callCheatMenu(true);
+                        break;
                     default:
-                        // TODO handle debug key combos
                         break;
                 }
 
@@ -230,6 +235,16 @@ void InputManager::initKeymaps(Common::KeymapArray &keymaps) {
     act->addDefaultInputMapping("C+S+TAB+m");
 	debugKeymap->addAction(act);
 
+    act = new Action("CHEAT", U32String("Open general cheat menu"));
+	act->setCustomEngineActionEvent(kNancyActionRequestCheatMenu);
+    act->addDefaultInputMapping("C+S+TAB+c");
+	debugKeymap->addAction(act);
+
+    act = new Action("EVENT", U32String("Open event flags cheat menu"));
+	act->setCustomEngineActionEvent(kNancyActionRequestEventMenu);
+    act->addDefaultInputMapping("C+S+TAB+v");
+	debugKeymap->addAction(act);
+
 	keymaps.push_back(mainKeymap);
     keymaps.push_back(debugKeymap);
 }
diff --git a/engines/nancy/input.h b/engines/nancy/input.h
index d8df3d0089..6ba64bbd22 100644
--- a/engines/nancy/input.h
+++ b/engines/nancy/input.h
@@ -81,7 +81,9 @@ enum NancyAction {
     kNancyActionRequestSaveLoad,
     kNancyActionRequestSetupMenu,
     kNancyActionRequestCredits,
-    kNancyActionRequestMap
+    kNancyActionRequestMap,
+    kNancyActionRequestCheatMenu,
+    kNancyActionRequestEventMenu
 };
 
 public:
diff --git a/engines/nancy/module.mk b/engines/nancy/module.mk
index ba9b45d61f..7b4b8c333a 100644
--- a/engines/nancy/module.mk
+++ b/engines/nancy/module.mk
@@ -22,6 +22,7 @@ MODULE_OBJS = \
   state/logo.o \
   state/map.o \
   state/scene.o \
+  cheat.o \
   commontypes.o \
   console.o \
   cursor.o \
diff --git a/engines/nancy/nancy.cpp b/engines/nancy/nancy.cpp
index 99d418ebd2..2b31f74ede 100644
--- a/engines/nancy/nancy.cpp
+++ b/engines/nancy/nancy.cpp
@@ -32,6 +32,7 @@
 #include "engines/nancy/state/map.h"
 #include "engines/nancy/graphics.h"
 #include "engines/nancy/cursor.h"
+#include "engines/nancy/cheat.h"
 
 #include "common/system.h"
 #include "common/random.h"
@@ -126,7 +127,7 @@ Common::Error NancyEngine::run() {
 	SearchMan.addSubDirectoryMatching(gameDataDir, "cdsound");
 	SearchMan.addSubDirectoryMatching(gameDataDir, "hdvideo");
 	SearchMan.addSubDirectoryMatching(gameDataDir, "cdvideo");
-
+	
 	Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember("data1.cab");
 		if (!stream)
 			error("Failed to open data1.cab");
@@ -134,7 +135,7 @@ Common::Error NancyEngine::run() {
 		Common::Archive *cab = Common::makeInstallShieldArchive(stream);
 	if (cab)
 		SearchMan.add("data1.hdr", cab);
-
+	
 	_res = new ResourceManager(this);
 	_res->initialize();
 
@@ -165,6 +166,20 @@ Common::Error NancyEngine::run() {
 		case kMap:
 			map->process();
 			break;
+		case kCheat: {
+			if (_cheatTypeIsEventFlag) {
+				EventFlagDialog *dialog = new EventFlagDialog(this);
+				dialog->runModal();
+				delete dialog;
+			} else {
+				CheatDialog *dialog = new CheatDialog(this);
+				dialog->runModal();
+				delete dialog;
+			}
+			setGameState(getPreviousGameState());
+			input->forceCleanInput();
+			break;
+		}
 		case kIdle:
 		default:
 			break;
diff --git a/engines/nancy/nancy.h b/engines/nancy/nancy.h
index 1311230fba..e5fb6c0b82 100644
--- a/engines/nancy/nancy.h
+++ b/engines/nancy/nancy.h
@@ -62,6 +62,7 @@ class InputManager;
 class SoundManager;
 class GraphicsManager;
 class CursorManager;
+class CheatDialog;
 
 namespace State {
 class Logo;
@@ -125,6 +126,7 @@ public:
 	void setGameState(GameState state, bool keepGraphics = false);
 	GameState getGameState() const { return _gameFlow.minGameState; }
 	GameState getPreviousGameState() const { return _gameFlow.previousGameState; }
+	void callCheatMenu(bool eventFlags) { setGameState(kCheat), _cheatTypeIsEventFlag = eventFlags; }
 
 	void setMouseEnabled(bool enabled);
 
@@ -173,7 +175,6 @@ protected:
 		uint16 height;
 	};
 
-
 	struct GameFlow {
 		GameState minGameState;
 		GameState previousGameState;
@@ -187,6 +188,8 @@ protected:
 	ImageList _objects;
 	int32 _fontSize;
 
+	bool _cheatTypeIsEventFlag;
+
 	void preloadCals(const IFF &boot);
 	void readImageList(const IFF &boot, const Common::String &prefix, ImageList &list);
 	Common::String readFilename(Common::ReadStream *stream) const;
@@ -194,7 +197,6 @@ protected:
 	virtual uint getFilenameLen() const = 0;
 	virtual void readBootSummary(const IFF &boot) = 0;
 
-
 private:
 	static NancyEngine *s_Engine;
 
diff --git a/engines/nancy/state/scene.h b/engines/nancy/state/scene.h
index fb24ea963f..c1618804af 100644
--- a/engines/nancy/state/scene.h
+++ b/engines/nancy/state/scene.h
@@ -64,6 +64,7 @@ class Scene {
     friend class Nancy::Action::ActionManager;
     friend class Nancy::NancyConsole;
     friend class Nancy::NancyEngine;
+    friend class Nancy::CheatDialog;
 public:
     struct SceneSummary { // SSUM
         Common::String description;             // 0x00
diff --git a/engines/nancy/time.h b/engines/nancy/time.h
index d9f7524e3a..a316976aaa 100644
--- a/engines/nancy/time.h
+++ b/engines/nancy/time.h
@@ -68,9 +68,9 @@ public:
     friend bool operator>= (const Time &l, const uint32 &r) { return !(l < r); }
     friend bool operator>= (const uint32 &l, const Time &r) { return !(l < r); }
 
-    uint16 getSecondsOnly()     { return (_milliseconds / 1000) % 60; }
-    uint16 getMinutesOnly()     { return (_milliseconds / 60000) % 60; }
-    uint16 getHoursOnly()       { return _milliseconds / 3600000; }
+    uint16 getSeconds()     { return (_milliseconds / 1000) % 60; }
+    uint16 getMinutes()     { return (_milliseconds / 60000) % 60; }
+    uint16 getTotalHours()       { return _milliseconds / 3600000; }
     
     uint16 getHours()   { return (_milliseconds / 3600000) % 24; } // Used for player time
     uint16 getDays()        { return _milliseconds / 86400000; } // up to 49.7 days


Commit: 4f9f97d40c20304cbaed6cfdf9b025e372ace5f0
    https://github.com/scummvm/scummvm/commit/4f9f97d40c20304cbaed6cfdf9b025e372ace5f0
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Fix secondary video timings

Nancy1 adds exactly 12ms to every frame of a secondary video, making it visibly slower than what the current implementation was playing. I'm not sure where exactly that happens yet, but this commit adds a (slightly hacky) fix for the problem

Changed paths:
    engines/nancy/action/secondaryvideo.cpp
    engines/nancy/video.cpp
    engines/nancy/video.h


diff --git a/engines/nancy/action/secondaryvideo.cpp b/engines/nancy/action/secondaryvideo.cpp
index 36a3ec58f2..9860cbaa24 100644
--- a/engines/nancy/action/secondaryvideo.cpp
+++ b/engines/nancy/action/secondaryvideo.cpp
@@ -47,7 +47,9 @@ void PlaySecondaryVideo::init() {
     }
 
     _decoder.loadFile(filename + ".avf");
-    // TODO add 12 ms to the frame time, not sure why this happens in the engine yet
+    // Every secondary video frame (in nancy1) plays exactly 12ms slower than what its metadata says.
+    // I'm still not sure how/why that happens so for now I'm using this hack to fix the timings
+    _decoder.addFrameTime(12);
     _drawSurface.create(_decoder.getWidth(), _decoder.getHeight(), GraphicsManager::pixelFormat);
 
     setVisible(false);
diff --git a/engines/nancy/video.cpp b/engines/nancy/video.cpp
index 7b869f7133..ab59145104 100644
--- a/engines/nancy/video.cpp
+++ b/engines/nancy/video.cpp
@@ -72,6 +72,10 @@ const Graphics::Surface *AVFDecoder::decodeFrame(uint frameNr) {
 	return ((AVFDecoder::AVFVideoTrack *)getTrack(0))->decodeFrame(frameNr);
 }
 
+void AVFDecoder::addFrameTime(const uint16 timeToAdd) {
+	((AVFDecoder::AVFVideoTrack *)getTrack(0))->_frameTime += timeToAdd;
+}
+
 AVFDecoder::AVFVideoTrack::AVFVideoTrack(Common::SeekableReadStream *stream) {
 	assert(stream);
 	_fileStream = stream;
diff --git a/engines/nancy/video.h b/engines/nancy/video.h
index 83127f2dfe..e5ad023de9 100644
--- a/engines/nancy/video.h
+++ b/engines/nancy/video.h
@@ -46,9 +46,11 @@ public:
 
 	virtual bool loadStream(Common::SeekableReadStream *stream) override;
 	const Graphics::Surface *decodeFrame(uint frameNr);
+	void addFrameTime(const uint16 timeToAdd);
 
 private:
 	class AVFVideoTrack : public FixedRateVideoTrack {
+	friend class AVFDecoder;
 	public:
 		AVFVideoTrack(Common::SeekableReadStream *stream);
 		virtual ~AVFVideoTrack();
@@ -67,7 +69,7 @@ private:
 		const Graphics::Surface *decodeFrame(uint frameNr);
 
 	protected:
-		Common::Rational getFrameRate() const { return Common::Rational(1000, _frameTime); }
+		virtual Common::Rational getFrameRate() const override { return Common::Rational(1000, _frameTime); }
 
 	private:
 		struct ChunkInfo {


Commit: 1773522233def83be3fb39b2a41ba6e5ffc7f5a9
    https://github.com/scummvm/scummvm/commit/1773522233def83be3fb39b2a41ba6e5ffc7f5a9
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Fix nancy1 night map locations

Fixed an issue where the night map in nancy1 would load in the wrong scene, blocking the player from reaching the end sequence.

Changed paths:
    engines/nancy/state/map.cpp


diff --git a/engines/nancy/state/map.cpp b/engines/nancy/state/map.cpp
index 505f763adf..40ad2b1a68 100644
--- a/engines/nancy/state/map.cpp
+++ b/engines/nancy/state/map.cpp
@@ -55,8 +55,8 @@ void Map::init() {
     _viewport.init();
     _label.init();
 
-    if (_engine->scene->getEventFlag(40, kTrue) &&
-        _engine->scene->getEventFlag(95, kTrue)) {
+    if (_engine->scene->getEventFlag(40, kTrue) && // Has set up sting
+        _engine->scene->getEventFlag(95, kTrue)) { // Connie chickens
         _mapID = 1;
     } else {
         _mapID = 0;
@@ -78,13 +78,14 @@ void Map::init() {
     _engine->sound->loadSound(sound);
     _engine->sound->playSound(sound);
 
+    _locations.clear();
+
     for (uint i = 0; i < 4; ++i) {
         chunk->seek(0x162 + i * 16, SEEK_SET);
         _locations.push_back(Location());
-        Location &loc = _locations[i];
+        Location &loc = _locations.back();
         readRect(*chunk, loc.hotspot);
 
-        // HARDCODED, TODO
         if (_mapID == 1 && (i % 2) != 0) {
             loc.isActive = false;
         } else {
@@ -94,7 +95,7 @@ void Map::init() {
         for (uint j = 0; j < 2; ++j) {
             loc.scenes.push_back(Location::SceneChange());
             Location::SceneChange &sc = loc.scenes[j];
-            chunk->seek(0x1BE + 6 * i * (j + 1), SEEK_SET);
+            chunk->seek(0x1BE + 6 * i + j * 24, SEEK_SET);
             sc.sceneID = chunk->readUint16LE();
             sc.frameID = chunk->readUint16LE();
             sc.verticalOffset = chunk->readUint16LE();


Commit: aa68bdef0fc1dda60099abbc9d1989f93c317a21
    https://github.com/scummvm/scummvm/commit/aa68bdef0fc1dda60099abbc9d1989f93c317a21
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Implement AddInventoryNoHS action record

Implemented the AddInventoryNoHS action record, which is used during nancy1's end sequence.  With this commit the game is technically completable, though the credits sequence is still not implemented.

Changed paths:
    engines/nancy/action/recordtypes.cpp
    engines/nancy/action/recordtypes.h


diff --git a/engines/nancy/action/recordtypes.cpp b/engines/nancy/action/recordtypes.cpp
index 8f12d19765..d73d0e9ba5 100644
--- a/engines/nancy/action/recordtypes.cpp
+++ b/engines/nancy/action/recordtypes.cpp
@@ -328,7 +328,16 @@ uint16 WinGame::readData(Common::SeekableReadStream &stream) {
 }
 
 uint16 AddInventoryNoHS::readData(Common::SeekableReadStream &stream) {
-    return readRaw(stream, 0x2); // TODO
+    itemID = stream.readUint16LE();
+    return 2;
+}
+
+void AddInventoryNoHS::execute(Nancy::NancyEngine *engine) {
+    if (engine->scene->hasItem(itemID) == kFalse) {
+        engine->scene->addItemToInventory(itemID);
+    }
+
+    isDone = true;
 }
 
 uint16 RemoveInventoryNoHS::readData(Common::SeekableReadStream &stream) {
diff --git a/engines/nancy/action/recordtypes.h b/engines/nancy/action/recordtypes.h
index fbb38119f8..bfa1a34049 100644
--- a/engines/nancy/action/recordtypes.h
+++ b/engines/nancy/action/recordtypes.h
@@ -312,6 +312,9 @@ protected:
 class AddInventoryNoHS : public ActionRecord {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void execute(Nancy::NancyEngine *engine) override;
+    
+    uint itemID;
     
 protected:
     virtual Common::String getRecordTypeName() const override { return "AddInventoryNoHS"; }


Commit: 6ada1a6b90aae2bb3ab83e9bf804d5fe9bfb62c8
    https://github.com/scummvm/scummvm/commit/6ada1a6b90aae2bb3ab83e9bf804d5fe9bfb62c8
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Fix laggy movement when repeatedly clicking viewport edges

Fixed an issue where movement wouldn't work correctly if the user clicks the viewport edges several times in quick succession instead of clicking and holding.

Changed paths:
    engines/nancy/ui/viewport.cpp


diff --git a/engines/nancy/ui/viewport.cpp b/engines/nancy/ui/viewport.cpp
index 9d5db84815..969bac35c6 100644
--- a/engines/nancy/ui/viewport.cpp
+++ b/engines/nancy/ui/viewport.cpp
@@ -171,8 +171,6 @@ void Viewport::handleInput(NancyInput &input) {
                 scrollDown(summary.verticalScrollDelta);
             }
 
-            _nextMovementTime = playTime + movementDelta;
-        } else if (direction != _movementLastFrame) {
             _nextMovementTime = playTime + movementDelta;
         }
     }


Commit: 5f35b8abcbc9efcb3a9916d1d357795f5dc5559e
    https://github.com/scummvm/scummvm/commit/5f35b8abcbc9efcb3a9916d1d357795f5dc5559e
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Implement help screen and UI buttons

The menu and help buttons on the sides of the frame are now functional, and the help screen is now also accessible. Also replaced the Frame class with a more general FullScreenImage that will be used across different game states.

Changed paths:
  A engines/nancy/state/help.cpp
  A engines/nancy/state/help.h
  A engines/nancy/ui/button.cpp
  A engines/nancy/ui/button.h
  A engines/nancy/ui/fullscreenimage.cpp
  A engines/nancy/ui/fullscreenimage.h
  R engines/nancy/ui/frame.cpp
  R engines/nancy/ui/frame.h
    engines/nancy/module.mk
    engines/nancy/nancy.cpp
    engines/nancy/nancy.h
    engines/nancy/state/scene.cpp
    engines/nancy/state/scene.h


diff --git a/engines/nancy/module.mk b/engines/nancy/module.mk
index 7b4b8c333a..f8ea51ac89 100644
--- a/engines/nancy/module.mk
+++ b/engines/nancy/module.mk
@@ -14,12 +14,14 @@ MODULE_OBJS = \
   action/sliderpuzzle.o \
   action/staticbitmapanim.o \
   action/telephone.o \
-  ui/frame.o \
+  ui/fullscreenimage.o \
+  ui/button.o \
   ui/inventorybox.o \
   ui/scrollbar.o \
   ui/textbox.o \
   ui/viewport.o \
   state/logo.o \
+  state/help.o \
   state/map.o \
   state/scene.o \
   cheat.o \
diff --git a/engines/nancy/nancy.cpp b/engines/nancy/nancy.cpp
index 2b31f74ede..869748a25c 100644
--- a/engines/nancy/nancy.cpp
+++ b/engines/nancy/nancy.cpp
@@ -22,6 +22,8 @@
 
 #include "engines/nancy/state/logo.h"
 #include "engines/nancy/state/scene.h"
+#include "engines/nancy/state/help.h"
+#include "engines/nancy/state/map.h"
 
 #include "engines/nancy/nancy.h"
 #include "engines/nancy/resource.h"
@@ -29,7 +31,6 @@
 #include "engines/nancy/sound.h"
 #include "engines/nancy/input.h"
 #include "engines/nancy/sound.h"
-#include "engines/nancy/state/map.h"
 #include "engines/nancy/graphics.h"
 #include "engines/nancy/cursor.h"
 #include "engines/nancy/cheat.h"
@@ -73,6 +74,7 @@ NancyEngine::NancyEngine(OSystem *syst, const NancyGameDescription *gd) :
 	logo = new State::Logo(this);
 	scene = new State::Scene(this);
 	map = new State::Map(this);
+	help = new State::Help(this);
 	input = new InputManager(this);
 	sound = new SoundManager(this);
 	graphicsManager = new GraphicsManager(this);
@@ -147,42 +149,51 @@ Common::Error NancyEngine::run() {
 	while (!shouldQuit()) {
 		cursorManager->setCursorType(CursorManager::kNormalArrow);
 		input->processEvents();
+		
 		switch (_gameFlow.minGameState) {
-		case kBoot:
-			bootGameEngine();
-			graphicsManager->init();
-			cursorManager->init();
-			setGameState(kLogo);
-			break;
-		case kLogo:
-			logo->process();
-			break;
-		case kMainMenu:
-			// TODO
-			break;
-		case kScene:
-			scene->process();
-			break;
-		case kMap:
-			map->process();
-			break;
-		case kCheat: {
-			if (_cheatTypeIsEventFlag) {
-				EventFlagDialog *dialog = new EventFlagDialog(this);
-				dialog->runModal();
-				delete dialog;
-			} else {
-				CheatDialog *dialog = new CheatDialog(this);
-				dialog->runModal();
-				delete dialog;
+			case kBoot:
+				bootGameEngine();
+				graphicsManager->init();
+				cursorManager->init();
+				setGameState(kLogo);
+				break;
+			case kLogo:
+				logo->process();
+				break;
+			case kMainMenu: {
+				GameState prevState = getPreviousGameState();
+				// TODO until the game's own menus are implemented we simply open the GMM
+				openMainMenuDialog();
+				setGameState(prevState);
+				break;
 			}
-			setGameState(getPreviousGameState());
-			input->forceCleanInput();
-			break;
-		}
-		case kIdle:
-		default:
-			break;
+			case kHelp:
+				help->process();
+				break;
+			case kScene:
+				scene->process();
+				break;
+			case kMap:
+				map->process();
+				break;
+			case kCheat: {
+				if (_cheatTypeIsEventFlag) {
+					EventFlagDialog *dialog = new EventFlagDialog(this);
+					dialog->runModal();
+					delete dialog;
+				} else {
+					CheatDialog *dialog = new CheatDialog(this);
+					dialog->runModal();
+					delete dialog;
+				}
+				setGameState(getPreviousGameState());
+				input->forceCleanInput();
+				break;
+			}
+			case kIdle:
+				break;
+			default:
+				break;
 		}
 
 		graphicsManager->draw();
diff --git a/engines/nancy/nancy.h b/engines/nancy/nancy.h
index e5fb6c0b82..5b3263a91c 100644
--- a/engines/nancy/nancy.h
+++ b/engines/nancy/nancy.h
@@ -68,6 +68,7 @@ namespace State {
 class Logo;
 class Scene;
 class Map;
+class Help;
 }
 
 class NancyEngine : public Engine {
@@ -143,6 +144,7 @@ public:
 	State::Logo *logo;
 	State::Scene *scene;
 	State::Map *map;
+	State::Help *help;
 	
 	OSystem *_system;
 	Common::RandomSource *_rnd;
diff --git a/engines/nancy/state/help.cpp b/engines/nancy/state/help.cpp
new file mode 100644
index 0000000000..616ca99626
--- /dev/null
+++ b/engines/nancy/state/help.cpp
@@ -0,0 +1,106 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/nancy/state/help.h"
+
+#include "engines/nancy/nancy.h"
+#include "engines/nancy/commontypes.h"
+#include "engines/nancy/sound.h"
+#include "engines/nancy/input.h"
+#include "engines/nancy/cursor.h"
+
+#include "common/stream.h"
+
+namespace Nancy {
+namespace State {
+
+void Help::process() {
+    switch (_state) {
+        case kInit:
+            init();
+            // fall through
+        case kBegin:       
+            begin();
+            // fall through
+        case kRun:
+            run();
+            break;
+        case kWaitForSound:
+            waitForSound();
+            break;
+    }
+}
+
+void Help::init() {
+    Common::SeekableReadStream *chunk = _engine->getBootChunkStream("HELP");
+
+    chunk->seek(0);
+    char buf[10];
+    chunk->read(buf, 10);
+    _image.init(buf);
+
+    chunk->skip(20);
+    _hotspot.left = chunk->readUint16LE();
+    _hotspot.top = chunk->readUint16LE();
+    _hotspot.right = chunk->readUint16LE();
+    _hotspot.bottom = chunk->readUint16LE();
+    
+    chunk = _engine->getBootChunkStream("MSND");
+    chunk->seek(0);
+	_sound.read(*chunk, SoundDescription::kMenu);
+
+    _state = kBegin;
+}
+
+void Help::begin() {
+	_engine->sound->loadSound(_sound);
+	_engine->sound->playSound(_sound);
+    
+    _image.registerGraphics();
+    _image.setVisible(true);
+
+    _engine->cursorManager->setCursorType(CursorManager::kNormalArrow);
+    _previousState = _engine->getPreviousGameState();
+    
+    _state = kRun;
+}
+
+void Help::run() {
+    NancyInput input = _engine->input->getInput();
+
+    if (_hotspot.contains(input.mousePos) && input.input & NancyInput::kLeftMouseButtonUp)  {
+        _engine->sound->playSound(0x18); // Hardcoded by original engine
+        _state = kWaitForSound;
+    }
+}
+
+void Help::waitForSound() {
+    if (!_engine->sound->isSoundPlaying(18)) {
+        _engine->setGameState((NancyEngine::GameState)_previousState);
+        
+	    _engine->sound->stopSound(_sound);
+        _state = kBegin;
+    }
+}
+
+} // End of namespace State
+} // End of namespace Nancy
diff --git a/engines/nancy/state/help.h b/engines/nancy/state/help.h
new file mode 100644
index 0000000000..92e8c11490
--- /dev/null
+++ b/engines/nancy/state/help.h
@@ -0,0 +1,62 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/nancy/ui/fullscreenimage.h"
+
+#include "engines/nancy/commontypes.h"
+
+#include "common/rect.h"
+
+#ifndef NANCY_STATE_HELP_H
+#define NANCY_STATE_HELP_H
+
+namespace Nancy {
+
+class NancyEngine;
+
+namespace State {
+
+class Help {
+public:
+    enum State { kInit, kBegin, kRun, kWaitForSound };
+    Help(NancyEngine *engine) : _engine(engine), _state(kInit), _image(engine) {}
+
+    void process();
+
+private:
+    void init();
+    void begin();
+    void run();
+    void waitForSound();
+
+    NancyEngine *_engine;
+    State _state;
+    UI::FullScreenImage _image;
+    Common::Rect _hotspot; // Can be an array, but isn't in nancy1
+    SoundDescription _sound;
+    uint _previousState;
+};
+
+} // End of namespace State
+} // End of namespace Nancy
+
+#endif // NANCY_STATE_HELP_H
diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index 4341f8c22c..e52cbf3ddf 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -131,6 +131,9 @@ void Scene::registerGraphics() {
     _viewport.registerGraphics();
     _textbox.registerGraphics();
     _inventoryBox.registerGraphics();
+    _menuButton.registerGraphics();
+
+    _engine->graphicsManager->redrawAll();
 
     // Used to clear the map label
     _textbox.setVisible(false);
@@ -168,10 +171,12 @@ void Scene::init() {
     }
     _lastHint = -1;
 
-    _frame.init();
+    _frame.init("FRAME"); // TODO should be extracted from BSUM
     _viewport.init();
     _textbox.init();
     _inventoryBox.init();
+    _menuButton.init();
+    _helpButton.init();
     _engine->cursorManager->showCursor(true);
 
     _state = kLoad;
@@ -278,6 +283,8 @@ void Scene::run() {
             registerGraphics();
         }
         unpauseSceneSpecificSounds();
+        _menuButton.setVisible(false);
+        _menuButton.setVisible(false);
 
         return;
     }
@@ -314,6 +321,8 @@ void Scene::run() {
     // Update the UI elements and handle input
     NancyInput input = _engine->input->getInput();
     _viewport.handleInput(input);
+    _menuButton.handleInput(input);
+    _helpButton.handleInput(input);
     _textbox.handleInput(input);
     _inventoryBox.handleInput(input);
     _actionManager.handleInput(input);
diff --git a/engines/nancy/state/scene.h b/engines/nancy/state/scene.h
index c1618804af..4e70e6e73f 100644
--- a/engines/nancy/state/scene.h
+++ b/engines/nancy/state/scene.h
@@ -25,10 +25,11 @@
 
 #include "engines/nancy/action/actionmanager.h"
 
-#include "engines/nancy/ui/frame.h"
+#include "engines/nancy/ui/fullscreenimage.h"
 #include "engines/nancy/ui/viewport.h"
 #include "engines/nancy/ui/textbox.h"
 #include "engines/nancy/ui/inventorybox.h"
+#include "engines/nancy/ui/button.h"
 
 #include "engines/nancy/time.h"
 #include "engines/nancy/commontypes.h"
@@ -92,6 +93,8 @@ public:
         _viewport(engine),
         _textbox(_frame),
         _inventoryBox(_frame),
+        _menuButton(_frame),
+        _helpButton(_frame),
         _actionManager(engine) {}
 
     void process();
@@ -134,7 +137,7 @@ public:
 
     void registerGraphics();
 
-    UI::Frame &getFrame() { return _frame; }
+    UI::FullScreenImage &getFrame() { return _frame; }
     UI::Viewport &getViewport() { return _viewport; }
     UI::Textbox &getTextbox() { return _textbox; }
     UI::InventoryBox &getInventoryBox() { return _inventoryBox; }
@@ -220,10 +223,12 @@ protected:
     Nancy::NancyEngine *_engine;
 
     // RenderObjects
-    UI::Frame _frame;
+    UI::FullScreenImage _frame;
     UI::Viewport _viewport;
     UI::Textbox _textbox;
     UI::InventoryBox _inventoryBox;
+    UI::MenuButton _menuButton;
+    UI::HelpButton _helpButton;
 
     // Data
     SceneState _sceneState;
diff --git a/engines/nancy/ui/button.cpp b/engines/nancy/ui/button.cpp
new file mode 100644
index 0000000000..31622648c9
--- /dev/null
+++ b/engines/nancy/ui/button.cpp
@@ -0,0 +1,86 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/nancy/ui/button.h"
+
+#include "engines/nancy/input.h"
+#include "engines/nancy/cursor.h"
+#include "engines/nancy/nancy.h"
+#include "engines/nancy/util.h"
+#include "engines/nancy/graphics.h"
+#include "engines/nancy/state/scene.h"
+
+#include "common/stream.h"
+
+namespace Nancy {
+namespace UI {
+
+void Button::handleInput(NancyInput &input) {
+    if (_screenPosition.contains(input.mousePos)) {
+        _engine->cursorManager->setCursorType(CursorManager::kHotspotArrow);
+
+        if (input.input & NancyInput::kLeftMouseButtonUp) {
+            onClick();
+        }
+    }
+}
+
+void MenuButton::init() {
+    Common::SeekableReadStream *bsum = _engine->getBootChunkStream("BSUM");
+
+    bsum->seek(0x184, SEEK_SET);
+    Common::Rect src;
+    readRect(*bsum, src);
+    _drawSurface.create(_engine->graphicsManager->object0, src);
+    bsum->skip(16);
+    readRect(*bsum, _screenPosition);
+    setVisible(false);
+
+    RenderObject::init();
+}
+
+void MenuButton::onClick() {
+    _engine->scene->requestStateChange(NancyEngine::kMainMenu);
+    setVisible(true);
+}
+
+void HelpButton::init() {
+    Common::SeekableReadStream *bsum = _engine->getBootChunkStream("BSUM");
+
+    bsum->seek(0x194, SEEK_SET);
+    Common::Rect src;
+    readRect(*bsum, src);
+    _drawSurface.create(_engine->graphicsManager->object0, src);
+    bsum->skip(16);
+    readRect(*bsum, _screenPosition);
+    setVisible(false);
+
+    RenderObject::init();
+}
+
+void HelpButton::onClick() {
+    _engine->scene->requestStateChange(NancyEngine::kHelp);
+    setVisible(true);
+}
+
+} // End of namespace UI
+} // End of namespace Nancy
diff --git a/engines/nancy/ui/button.h b/engines/nancy/ui/button.h
new file mode 100644
index 0000000000..a58ee378f0
--- /dev/null
+++ b/engines/nancy/ui/button.h
@@ -0,0 +1,70 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef NANCY_UI_BUTTON_H
+#define NANCY_UI_BUTTON_H
+
+#include "engines/nancy/renderobject.h"
+
+namespace Nancy {
+
+struct NancyInput;
+
+namespace UI {
+
+class Button : public RenderObject {
+public:
+    Button(RenderObject &redrawFrom) : RenderObject(redrawFrom) {}
+    virtual ~Button() =default;
+
+    virtual void onClick() =0;
+
+    void handleInput(NancyInput &input);
+
+protected:
+    virtual uint16 getZOrder() const override { return 5; }
+    virtual BlitType getBlitType() const override { return kNoTrans; }
+
+};
+
+class MenuButton : public Button {
+public:
+    MenuButton(RenderObject &redrawFrom) : Button(redrawFrom) {}
+    virtual ~MenuButton() =default;
+
+    virtual void init() override;
+    virtual void onClick() override;
+};
+
+class HelpButton : public Button {
+public:
+    HelpButton(RenderObject &redrawFrom) : Button(redrawFrom) {}
+    virtual ~HelpButton() =default;
+
+    virtual void init() override;
+    virtual void onClick() override;
+};
+
+} // End of namespace UI
+} // End of namespace Nancy
+
+#endif // NANCY_UI_BUTTON_H
diff --git a/engines/nancy/ui/frame.cpp b/engines/nancy/ui/fullscreenimage.cpp
similarity index 89%
rename from engines/nancy/ui/frame.cpp
rename to engines/nancy/ui/fullscreenimage.cpp
index 24f1d30e77..183d8fe0b7 100644
--- a/engines/nancy/ui/frame.cpp
+++ b/engines/nancy/ui/fullscreenimage.cpp
@@ -20,7 +20,7 @@
  *
  */
 
-#include "engines/nancy/ui/frame.h"
+#include "engines/nancy/ui/fullscreenimage.h"
 
 #include "engines/nancy/nancy.h"
 #include "engines/nancy/resource.h"
@@ -29,10 +29,9 @@
 namespace Nancy {
 namespace UI {
 
-void Frame::init() {
-    // TODO
+void FullScreenImage::init(Common::String imageName) {
     Graphics::Surface surf;
-    _engine->_res->loadImage("ciftree", "FRAME", surf);
+    _engine->_res->loadImage("ciftree", imageName, surf);
 
     Common::Rect srcBounds = Common::Rect(0,0, surf.w, surf.h);
     _screenPosition = srcBounds;
diff --git a/engines/nancy/ui/frame.h b/engines/nancy/ui/fullscreenimage.h
similarity index 78%
rename from engines/nancy/ui/frame.h
rename to engines/nancy/ui/fullscreenimage.h
index 5047128b97..506087d5aa 100644
--- a/engines/nancy/ui/frame.h
+++ b/engines/nancy/ui/fullscreenimage.h
@@ -20,22 +20,22 @@
  *
  */
 
-#ifndef NANCY_UI_FRAME_H
-#define NANCY_UI_FRAME_H
+#ifndef NANCY_UI_FULLSCREENIMAGE_H
+#define NANCY_UI_FULLSCREENIMAGE_H
 
 #include "engines/nancy/renderobject.h"
 
 namespace Nancy {
 namespace UI {
 
-class Frame : public RenderObject {
+class FullScreenImage : public RenderObject {
 public:
-    Frame(NancyEngine *engine) : RenderObject(engine) {}
-    virtual ~Frame() =default;
-
-    virtual void init() override;
+    FullScreenImage(NancyEngine *engine) : RenderObject(engine) {}
+    virtual ~FullScreenImage() =default;
 
+    void init(Common::String imageName);
 protected:
+    virtual void init() override {}
     virtual uint16 getZOrder() const override { return 0; }
     virtual BlitType getBlitType() const override { return kNoTrans; }
 };
@@ -43,4 +43,4 @@ protected:
 } // End of namespace UI
 } // End of namespace Nancy
 
-#endif // NANCY_UI_FRAME_H
+#endif // NANCY_UI_FULLSCREENIMAGE_H


Commit: 89ea3ec0cf700b9630fda1d3e770c572a56789c8
    https://github.com/scummvm/scummvm/commit/89ea3ec0cf700b9630fda1d3e770c572a56789c8
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add map button

Implemented the button on the map screen that returns the player to the last scene, as well as the secret button in the same position that teleports the player to the map from select few scenes.

Changed paths:
    engines/nancy/state/map.cpp
    engines/nancy/state/map.h
    engines/nancy/state/scene.cpp
    engines/nancy/state/scene.h
    engines/nancy/ui/viewport.cpp


diff --git a/engines/nancy/state/map.cpp b/engines/nancy/state/map.cpp
index 40ad2b1a68..8439260733 100644
--- a/engines/nancy/state/map.cpp
+++ b/engines/nancy/state/map.cpp
@@ -42,10 +42,13 @@ void Map::process() {
     switch (_state) {
         case kInit:
             init();
-            break;
+            // fall through
         case kRun:
             run();
             break;
+        case kStop:
+            stop();
+            break;
     }
 }
 
@@ -54,6 +57,7 @@ void Map::init() {
 
     _viewport.init();
     _label.init();
+    _button.init();
 
     if (_engine->scene->getEventFlag(40, kTrue) && // Has set up sting
         _engine->scene->getEventFlag(95, kTrue)) { // Connie chickens
@@ -121,6 +125,13 @@ void Map::run() {
 
     _label.setLabel(-1);
 
+    _button.handleInput(input);
+
+    if (_mapButtonClicked) {
+        _state = kStop;
+        return;
+    }
+
     for (uint i = 0; i < 4; ++i) {
         auto &loc = _locations[i];
         if (loc.isActive && _viewport.convertToScreen(loc.hotspot).contains(input.mousePos)) {
@@ -128,30 +139,39 @@ void Map::run() {
 
             _label.setLabel(i);
 
-            // TODO handle map button as well
-
             if (input.input & NancyInput::kLeftMouseButtonUp) {
-                stopSound();
-                _engine->setGameState(NancyEngine::kScene);
-                _engine->scene->changeScene(loc.scenes[_mapID].sceneID, loc.scenes[_mapID].frameID, loc.scenes[_mapID].verticalOffset, false);
-                _state = kInit;
+                _pickedLocationID = i;
+                _state = kStop;
             }
             return;
         }
     }
 }
 
-void Map::stopSound() {
+void Map::stop() {
     Common::SeekableReadStream *chunk = _engine->getBootChunkStream("MAP");
     SoundDescription sound;
     chunk->seek(0x18 + _mapID * 0x20, SEEK_SET);
     sound.read(*chunk, SoundDescription::kMenu);
     _engine->sound->stopSound(sound);
+    
+    _engine->setGameState(NancyEngine::kScene);
+
+    if (_pickedLocationID != -1) {
+        auto &loc = _locations[_pickedLocationID];
+        _engine->scene->changeScene(loc.scenes[_mapID].sceneID, loc.scenes[_mapID].frameID, loc.scenes[_mapID].verticalOffset, false);
+        _pickedLocationID = -1;
+    }
+
+    _mapButtonClicked = false;
+
+    _state = kInit;
 }
 
 void Map::registerGraphics() {
     _viewport.registerGraphics();
     _label.registerGraphics();
+    _button.registerGraphics();
 }
 
 void Map::MapLabel::init() {
@@ -170,5 +190,22 @@ void Map::MapLabel::setLabel(int labelID) {
     }
 }
 
+void Map::MapButton::init() {
+    Common::SeekableReadStream *map = _engine->getBootChunkStream("MAP");
+
+    map->seek(0x7A, SEEK_SET);
+    Common::Rect src;
+    readRect(*map, src);
+    _drawSurface.create(_engine->graphicsManager->object0, src);
+    readRect(*map, _screenPosition);
+    setVisible(true);
+
+    RenderObject::init();
+}
+
+void Map::MapButton::onClick() {
+    _parent->_mapButtonClicked = true;
+}
+
 } // End of namespace State
 } // End of namespace Nancy
diff --git a/engines/nancy/state/map.h b/engines/nancy/state/map.h
index 25129be636..4478e32e73 100644
--- a/engines/nancy/state/map.h
+++ b/engines/nancy/state/map.h
@@ -24,6 +24,7 @@
 #define NANCY_STATE_MAP_H
 
 #include "engines/nancy/ui/viewport.h"
+#include "engines/nancy/ui/button.h"
 
 #include "engines/nancy/state/scene.h"
 
@@ -44,14 +45,18 @@ namespace State {
 
 class Map {
     friend class MapLabel;
+    friend class MapButton;
 public:
-    enum State { kInit, kRun };
+    enum State { kInit, kRun, kStop };
     Map(Nancy::NancyEngine *engine) :
         _engine(engine),
         _state(kInit),
         _mapID(0),
+        _mapButtonClicked(false),
+        _pickedLocationID(-1),
         _viewport(engine),
-        _label(engine->scene->getFrame(), this) {}
+        _label(engine->scene->getFrame(), this),
+        _button(engine->scene->getFrame(), this) {}
 
     void process();
 
@@ -87,9 +92,23 @@ private:
         Map *_parent;
     };
 
+    class MapButton : public UI::Button {
+    public:
+        MapButton(RenderObject &redrawFrom, Map *parent) : Button(redrawFrom), _parent(parent) {}
+        virtual ~MapButton() =default;
+
+        virtual void init() override;
+        virtual void onClick() override;
+
+    protected:
+        virtual uint16 getZOrder() const override { return 9; }
+
+        Map *_parent;
+    };
+
     void init();
     void run();
-    void stopSound();
+    void stop();
 
     void registerGraphics();
 
@@ -97,9 +116,12 @@ private:
 
     Nancy::UI::Viewport _viewport;
     MapLabel _label;
+    MapButton _button;
 
     State _state;
     uint16 _mapID;
+    bool _mapButtonClicked;
+    int16 _pickedLocationID;
     Common::Array<Location> _locations;
 };
 
diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index e52cbf3ddf..be89f82cf2 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -30,6 +30,7 @@
 #include "engines/nancy/graphics.h"
 #include "engines/nancy/cursor.h"
 #include "engines/nancy/time.h"
+#include "engines/nancy/util.h"
 
 #include "common/memstream.h"
 #include "common/rect.h"
@@ -164,13 +165,28 @@ void Scene::init() {
 
     _sceneState.nextScene.sceneID = _engine->firstSceneID;
 
-    Common::SeekableReadStream *hint = _engine->getBootChunkStream("HINT");
-    hint->seek(0);
+    Common::SeekableReadStream *chunk = _engine->getBootChunkStream("HINT");
+    chunk->seek(0);
     for (uint i = 0; i < 3; ++i) {
-        _hintsRemaining.push_back(hint->readByte());
+        _hintsRemaining.push_back(chunk->readByte());
     }
     _lastHint = -1;
 
+    chunk = _engine->getBootChunkStream("MAP");
+    chunk->seek(0x8A);
+    readRect(*chunk, _mapHotspot);
+
+    // Hardcoded by original engine
+    _mapAccessSceneIDs.clear();
+    _mapAccessSceneIDs.push_back(9);
+    _mapAccessSceneIDs.push_back(10);
+    _mapAccessSceneIDs.push_back(11);
+    _mapAccessSceneIDs.push_back(0x4B0);
+    _mapAccessSceneIDs.push_back(0x378);
+    _mapAccessSceneIDs.push_back(0x29A);
+    _mapAccessSceneIDs.push_back(0x4E2);
+    _mapAccessSceneIDs.push_back(0x682);
+
     _frame.init("FRAME"); // TODO should be extracted from BSUM
     _viewport.init();
     _textbox.init();
@@ -282,9 +298,10 @@ void Scene::run() {
         if (_engine->getPreviousGameState() != Nancy::NancyEngine::kPause) {
             registerGraphics();
         }
+
         unpauseSceneSpecificSounds();
         _menuButton.setVisible(false);
-        _menuButton.setVisible(false);
+        _helpButton.setVisible(false);
 
         return;
     }
@@ -317,7 +334,6 @@ void Scene::run() {
         _timers.timeOfDay = Timers::kDuskDawn;
     }
 
-
     // Update the UI elements and handle input
     NancyInput input = _engine->input->getInput();
     _viewport.handleInput(input);
@@ -327,6 +343,22 @@ void Scene::run() {
     _inventoryBox.handleInput(input);
     _actionManager.handleInput(input);
 
+    _sceneState.currentScene.frameID = _viewport.getCurFrame();
+    _sceneState.currentScene.verticalOffset = _viewport.getCurVerticalScroll();
+
+    // Handle invisible map button
+    for (uint i = 0; i < _mapAccessSceneIDs.size(); ++i) {
+        if (_sceneState.currentScene.sceneID == _mapAccessSceneIDs[i]) {
+            if (_mapHotspot.contains(input.mousePos)) {
+                _engine->cursorManager->setCursorType(CursorManager::kHotspotArrow);
+
+                if (input.input & NancyInput::kLeftMouseButtonUp) {
+                    requestStateChange(NancyEngine::kMap);
+                }
+            }
+        }
+    }
+
     _actionManager.processActionRecords();
 }
 
diff --git a/engines/nancy/state/scene.h b/engines/nancy/state/scene.h
index 4e70e6e73f..675a2b0580 100644
--- a/engines/nancy/state/scene.h
+++ b/engines/nancy/state/scene.h
@@ -239,6 +239,9 @@ protected:
     int16 _lastHint;
     NancyEngine::GameState _gameStateRequested;
 
+    Common::Rect _mapHotspot;
+    Common::Array<uint16> _mapAccessSceneIDs;
+
     Action::ActionManager _actionManager;
 
     State _state;
diff --git a/engines/nancy/ui/viewport.cpp b/engines/nancy/ui/viewport.cpp
index 969bac35c6..4be6d18cec 100644
--- a/engines/nancy/ui/viewport.cpp
+++ b/engines/nancy/ui/viewport.cpp
@@ -205,7 +205,6 @@ void Viewport::setFrame(uint frameNr) {
     _fullFrame.blitFrom(*newFrame);
     _needsRedraw = true;
 
-    _engine->scene->getSceneInfo().frameID = frameNr;
     _currentFrame = frameNr;
 }
 
@@ -234,8 +233,6 @@ void Viewport::setVerticalScroll(uint scroll) {
     } else {
         enableEdges(kUp | kDown);
     }
-    
-    _engine->scene->getSceneInfo().verticalOffset = scroll;
 }
 
 void Viewport::scrollUp(uint delta) {


Commit: cb373ff04e32d746163ecef3cf24c2b4fc3c03ae
    https://github.com/scummvm/scummvm/commit/cb373ff04e32d746163ecef3cf24c2b4fc3c03ae
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add missing UI sfx

Added some missing sound effects when clicking on various UI elements.

Changed paths:
    engines/nancy/state/map.cpp
    engines/nancy/ui/button.cpp
    engines/nancy/ui/inventorybox.cpp
    engines/nancy/ui/inventorybox.h


diff --git a/engines/nancy/state/map.cpp b/engines/nancy/state/map.cpp
index 8439260733..b9fbdc4d90 100644
--- a/engines/nancy/state/map.cpp
+++ b/engines/nancy/state/map.cpp
@@ -80,7 +80,7 @@ void Map::init() {
     SoundDescription sound;
     sound.read(*chunk, SoundDescription::kMenu);
     _engine->sound->loadSound(sound);
-    _engine->sound->playSound(sound);
+    _engine->sound->playSound(0x14);
 
     _locations.clear();
 
@@ -121,6 +121,10 @@ void Map::init() {
 }
 
 void Map::run() {
+    if (!_engine->sound->isSoundPlaying(0x14) && !_engine->sound->isSoundPlaying(0x13)) {
+        _engine->sound->playSound(0x13);
+    }
+
     NancyInput input = _engine->input->getInput();
 
     _label.setLabel(-1);
@@ -161,7 +165,11 @@ void Map::stop() {
         auto &loc = _locations[_pickedLocationID];
         _engine->scene->changeScene(loc.scenes[_mapID].sceneID, loc.scenes[_mapID].frameID, loc.scenes[_mapID].verticalOffset, false);
         _pickedLocationID = -1;
+        
+        _engine->sound->playSound(0x18);
     }
+    // The two sounds play at the same time if a location was picked
+    _engine->sound->playSound(0x14);
 
     _mapButtonClicked = false;
 
diff --git a/engines/nancy/ui/button.cpp b/engines/nancy/ui/button.cpp
index 31622648c9..d43ca2dec5 100644
--- a/engines/nancy/ui/button.cpp
+++ b/engines/nancy/ui/button.cpp
@@ -60,6 +60,7 @@ void MenuButton::init() {
 
 void MenuButton::onClick() {
     _engine->scene->requestStateChange(NancyEngine::kMainMenu);
+    _engine->sound->playSound(0x18);
     setVisible(true);
 }
 
@@ -79,6 +80,7 @@ void HelpButton::init() {
 
 void HelpButton::onClick() {
     _engine->scene->requestStateChange(NancyEngine::kHelp);
+    _engine->sound->playSound(0x18);
     setVisible(true);
 }
 
diff --git a/engines/nancy/ui/inventorybox.cpp b/engines/nancy/ui/inventorybox.cpp
index 83e8ca11ca..6892151c55 100644
--- a/engines/nancy/ui/inventorybox.cpp
+++ b/engines/nancy/ui/inventorybox.cpp
@@ -114,11 +114,13 @@ void InventoryBox::handleInput(NancyInput &input) {
                 _engine->cursorManager->setCursorType(CursorManager::kHotspotArrow);
                 if (input.input & NancyInput::kLeftMouseButtonUp) {
                     _engine->scene->addItemToInventory(_engine->scene->getHeldItem());
+                    _engine->sound->playSound(0x16);
                 }                
             } else if (_itemHotspots[i].itemID != -1) {
                 _engine->cursorManager->setCursorType(CursorManager::kHotspotArrow);
                 if (input.input & NancyInput::kLeftMouseButtonUp) {
                     _engine->scene->removeItemFromInventory(_itemHotspots[i].itemID);
+                    _engine->sound->playSound(0x18);
                 }
             }
             break;
@@ -231,13 +233,27 @@ void InventoryBox::Shades::updateGraphics() {
         if (_curFrame < 7 && time > _nextFrameTime) {
             setAnimationFrame(++_curFrame);
             _nextFrameTime = time + _parent->_shadesFrameTime;
+
+            if (!_soundTriggered) {
+                _soundTriggered = true;
+                _engine->sound->playSound(0x12);
+            }
         }
     } else {
         if (_curFrame > 0 && time > _nextFrameTime) {
             setAnimationFrame(--_curFrame);
             _nextFrameTime = time + _parent->_shadesFrameTime;
+
+            if (!_soundTriggered) {
+                _soundTriggered = true;
+                _engine->sound->playSound(0x12);
+            }
         }
     }
+
+    if (_curFrame == 0 || _curFrame == 7) {
+        _soundTriggered = false;
+    }
 }
 
 void InventoryBox::Shades::setAnimationFrame(uint frame) {
diff --git a/engines/nancy/ui/inventorybox.h b/engines/nancy/ui/inventorybox.h
index 0de648d2ea..1d0c3ee03e 100644
--- a/engines/nancy/ui/inventorybox.h
+++ b/engines/nancy/ui/inventorybox.h
@@ -96,7 +96,8 @@ private:
     public:
         Shades(RenderObject &redrawFrom, InventoryBox *parent) :
             RenderObject(redrawFrom),
-            _parent(parent) {}
+            _parent(parent),
+            _soundTriggered(false) {}
         virtual ~Shades() =default;
 
         virtual void init() override;
@@ -114,7 +115,8 @@ private:
 
         uint _curFrame;
         Time _nextFrameTime;
-        bool _areOpen;      
+        bool _areOpen;
+        bool _soundTriggered;
     };
 
     struct ItemHotspot {


Commit: 43358185877efabf67111c5fbbc7f9b719b51426
    https://github.com/scummvm/scummvm/commit/43358185877efabf67111c5fbbc7f9b719b51426
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add c++11 dependency to configure.engine

Added a missing cxx11 dependency to configure.engine so the engine is now properly marked.

Changed paths:
    engines/nancy/configure.engine


diff --git a/engines/nancy/configure.engine b/engines/nancy/configure.engine
index 643723c542..94cb58dde2 100644
--- a/engines/nancy/configure.engine
+++ b/engines/nancy/configure.engine
@@ -1,3 +1,3 @@
 # This file is included from the main "configure" script
 # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps]
-add_engine nancy "Nancy Drew" no
+add_engine nancy "Nancy Drew" no "" "" "cxx11"


Commit: a17e45537c4998f1c4c6aecb2cb037151f84f622
    https://github.com/scummvm/scummvm/commit/a17e45537c4998f1c4c6aecb2cb037151f84f622
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Change textbox text tokens type

Changed all textbox tokens from Common::String to char[] to avoid potential issues with global constructor calling order.

Changed paths:
    engines/nancy/ui/textbox.cpp
    engines/nancy/ui/textbox.h


diff --git a/engines/nancy/ui/textbox.cpp b/engines/nancy/ui/textbox.cpp
index 3288d89409..787cb0e51a 100644
--- a/engines/nancy/ui/textbox.cpp
+++ b/engines/nancy/ui/textbox.cpp
@@ -33,18 +33,19 @@
 #include "common/error.h"
 #include "common/util.h"
 #include "common/events.h"
+#include "common/util.h"
 
 namespace Nancy {
 namespace UI {
 
-const Common::String Textbox::CCBeginToken = Common::String("<i>");
-const Common::String Textbox::CCEndToken = Common::String("<o>");
-const Common::String Textbox::colorBeginToken = Common::String("<c1>");
-const Common::String Textbox::colorEndToken = Common::String("<c0>");
-const Common::String Textbox::hotspotToken = Common::String("<h>");
-const Common::String Textbox::newLineToken = Common::String("<n>");
-const Common::String Textbox::tabToken = Common::String("<t>");
-const Common::String Textbox::telephoneEndToken = Common::String("<e>");
+const char Textbox::CCBeginToken[] = "<i>";
+const char Textbox::CCEndToken[] = "<o>";
+const char Textbox::colorBeginToken[] = "<c1>";
+const char Textbox::colorEndToken[] = "<c0>";
+const char Textbox::hotspotToken[] = "<h>";
+const char Textbox::newLineToken[] = "<n>";
+const char Textbox::tabToken[] = "<t>";
+const char Textbox::telephoneEndToken[] = "<e>";
 
 void Textbox::init() {    
     Common::SeekableReadStream *chunk = _engine->getBootChunkStream("TBOX");
@@ -140,25 +141,25 @@ void Textbox::drawTextbox() {
 
         // Trim the begin and end tokens from the line
         if (currentLine.hasPrefix(CCBeginToken) && currentLine.hasSuffix(CCEndToken)) {
-            currentLine = currentLine.substr(CCBeginToken.size(), currentLine.size() - CCBeginToken.size() - CCEndToken.size());
+            currentLine = currentLine.substr(ARRAYSIZE(CCBeginToken) - 1, currentLine.size() - ARRAYSIZE(CCBeginToken) - ARRAYSIZE(CCEndToken) + 2);
         }
         
         // Replace every newline token with \n
         uint32 newLinePos;
         while (newLinePos = currentLine.find(newLineToken), newLinePos != String::npos) {
-            currentLine.replace(newLinePos, newLineToken.size(), "\n");
+            currentLine.replace(newLinePos, ARRAYSIZE(newLineToken) - 1, "\n");
         }
 
         // Simply remove telephone end token
         if (currentLine.hasSuffix(telephoneEndToken)) {
-            currentLine = currentLine.substr(0, currentLine.size() - telephoneEndToken.size());
+            currentLine = currentLine.substr(0, currentLine.size() - ARRAYSIZE(telephoneEndToken) + 1);
         }
 
         // Remove hotspot token and mark that we need to calculate the bounds
         // Assumes a single text line has a single hotspot
         uint32 hotspotPos = currentLine.find(hotspotToken);
         if (hotspotPos != String::npos) {
-            currentLine.erase(hotspotPos, hotspotToken.size());
+            currentLine.erase(hotspotPos, ARRAYSIZE(hotspotToken) - 1);
             hasHotspot = true;
         }
 
@@ -168,7 +169,7 @@ void Textbox::drawTextbox() {
         {
             if (currentLine.hasPrefix(tabToken)) {
                 horizontalOffset += font->getStringWidth("    "); // Replace tab with 4 spaces
-                currentLine = currentLine.substr(tabToken.size());
+                currentLine = currentLine.substr(ARRAYSIZE(tabToken) - 1);
             }
 
             String currentSubLine;
@@ -187,8 +188,8 @@ void Textbox::drawTextbox() {
                 // Found color string, look for end token
                 uint32 colorEndPos = currentSubLine.find(colorEndToken);
 
-                Common::String colorSubLine = currentSubLine.substr(colorBeginToken.size(), colorEndPos - colorBeginToken.size());
-                currentSubLine = currentSubLine.substr(colorBeginToken.size() + colorEndToken.size() + colorSubLine.size());
+                Common::String colorSubLine = currentSubLine.substr(ARRAYSIZE(colorBeginToken) - 1, colorEndPos - ARRAYSIZE(colorBeginToken) + 1);
+                currentSubLine = currentSubLine.substr(ARRAYSIZE(colorBeginToken) + ARRAYSIZE(colorEndToken) + colorSubLine.size() - 2);
 
                 // Draw the color line
                 font->drawString(&_fullSurface, colorSubLine, _borderWidth + horizontalOffset, _firstLineOffset - font->getFontHeight() + _numLines * lineDist, maxWidth, 1);
diff --git a/engines/nancy/ui/textbox.h b/engines/nancy/ui/textbox.h
index 1842cfb9b7..88dd350cb2 100644
--- a/engines/nancy/ui/textbox.h
+++ b/engines/nancy/ui/textbox.h
@@ -108,14 +108,14 @@ private:
     bool _needsTextRedraw;
     float _scrollbarPos;
 
-    static const Common::String CCBeginToken;
-    static const Common::String CCEndToken;
-    static const Common::String colorBeginToken;
-    static const Common::String colorEndToken;
-    static const Common::String hotspotToken;
-    static const Common::String newLineToken;
-    static const Common::String tabToken;
-    static const Common::String telephoneEndToken;
+    static const char CCBeginToken[];
+    static const char CCEndToken[];
+    static const char colorBeginToken[];
+    static const char colorEndToken[];
+    static const char hotspotToken[];
+    static const char newLineToken[];
+    static const char tabToken[];
+    static const char telephoneEndToken[];
 
 protected:
 };


Commit: a7fab688393056f01cbbfe05836b0c876659a1e1
    https://github.com/scummvm/scummvm/commit/a7fab688393056f01cbbfe05836b0c876659a1e1
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Fix crash after popping scene

Fixed an off-by-one error that was causing an assert failure in the viewport code when popping a scene.

Changed paths:
    engines/nancy/ui/viewport.h


diff --git a/engines/nancy/ui/viewport.h b/engines/nancy/ui/viewport.h
index 078713e3f2..c35026c9f3 100644
--- a/engines/nancy/ui/viewport.h
+++ b/engines/nancy/ui/viewport.h
@@ -63,7 +63,7 @@ public:
 
     uint16 getFrameCount() const { return _decoder.isVideoLoaded() ? _decoder.getFrameCount() : 0; }
     uint16 getCurFrame() const { return _currentFrame; }
-    uint16 getCurVerticalScroll() const { return _drawSurface.getOffsetFromOwner().y; }
+    uint16 getCurVerticalScroll() const { return _drawSurface.getOffsetFromOwner().y - 1; }
     uint16 getMaxScroll() const { return _fullFrame.h - _drawSurface.h - 1; }
 
     Common::Rect getBoundsByFormat(uint format) const; // used by video


Commit: a96fcd78db6ceb1db377f6d5a4b1fd2b9de5e0cc
    https://github.com/scummvm/scummvm/commit/a96fcd78db6ceb1db377f6d5a4b1fd2b9de5e0cc
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Fix infinite hints

Fixed an issue where used hints would not get subtracted from the maximum amount allowed per playthrough, allowing for infinite hints.

Changed paths:
    engines/nancy/state/scene.cpp


diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index be89f82cf2..7658f50ec4 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -470,8 +470,9 @@ void Scene::clearLogicConditions() {
 }
 
 void Scene::useHint(int hintID, int hintWeight) {
-    if (_lastHint == hintID) {
+    if (_lastHint != hintID) {
         _hintsRemaining[_difficulty] += hintWeight;
+        _lastHint = hintID;
     }
 }
 


Commit: 0fdda6fbfcf4ccc30a14371f88ff8d4af94b286f
    https://github.com/scummvm/scummvm/commit/0fdda6fbfcf4ccc30a14371f88ff8d4af94b286f
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Code formatting fixes

Fixed a lot of code formatting issues, including switch case statements being indented, some curly braces not being left hanging, if and for statements not being surrounded by newlines, some double spaces, and more.

Changed paths:
    engines/nancy/action/actionmanager.cpp
    engines/nancy/action/actionrecord.h
    engines/nancy/action/arfactory_v1.cpp
    engines/nancy/action/leverpuzzle.cpp
    engines/nancy/action/orderingpuzzle.cpp
    engines/nancy/action/passwordpuzzle.cpp
    engines/nancy/action/primaryvideo.cpp
    engines/nancy/action/recordtypes.cpp
    engines/nancy/action/rotatinglockpuzzle.cpp
    engines/nancy/action/secondarymovie.cpp
    engines/nancy/action/secondaryvideo.cpp
    engines/nancy/action/sliderpuzzle.cpp
    engines/nancy/action/staticbitmapanim.cpp
    engines/nancy/action/telephone.cpp
    engines/nancy/cheat.cpp
    engines/nancy/commontypes.cpp
    engines/nancy/cursor.cpp
    engines/nancy/font.cpp
    engines/nancy/input.cpp
    engines/nancy/nancy.cpp
    engines/nancy/state/help.cpp
    engines/nancy/state/map.cpp
    engines/nancy/state/scene.cpp
    engines/nancy/time.h
    engines/nancy/ui/textbox.cpp
    engines/nancy/ui/viewport.h


diff --git a/engines/nancy/action/actionmanager.cpp b/engines/nancy/action/actionmanager.cpp
index c2cf0f939f..deeb7f366f 100644
--- a/engines/nancy/action/actionmanager.cpp
+++ b/engines/nancy/action/actionmanager.cpp
@@ -157,70 +157,79 @@ bool ActionManager::addNewActionRecord(Common::SeekableReadStream &inputData) {
     for (uint i = 0; i < newRecord->dependencies.size(); ++i) {
         debugCN(1, kDebugActionRecord, "\tDependency %i: type ", i);
         switch (newRecord->dependencies[i].type) {
-            case kNone :debugCN(1, kDebugActionRecord, "kNone"); break;
-            case kInventory :
-                debugCN(1, kDebugActionRecord, "kInventory, item ID %i %s",
-                            newRecord->dependencies[i].label,
-                            newRecord->dependencies[i].condition == kTrue ? "is in possession" : "is not in possession");
-                break;
-            case kEventFlag :
-                debugCN(1, kDebugActionRecord, "kEventFlag, flag ID %i == %s",
-                            newRecord->dependencies[i].label,
-                            newRecord->dependencies[i].condition == kTrue ? "true" : "false");
-                break;
-            case kLogicCondition :
-                debugCN(1, kDebugActionRecord, "kLogicCondition, logic condition ID %i == %s",
-                            newRecord->dependencies[i].label,
-                            newRecord->dependencies[i].condition == kTrue ? "true" : "false");
-                break;
-            case kTotalTime :
-                debugCN(1, kDebugActionRecord, "kTotalTime, %i hours, %i minutes, %i seconds, %i milliseconds",
-                            newRecord->dependencies[i].hours,
-                            newRecord->dependencies[i].minutes,
-                            newRecord->dependencies[i].seconds,
-                            newRecord->dependencies[i].milliseconds);
-                break;
-            case kSceneTime :
-                debugCN(1, kDebugActionRecord, "kSceneTime, %i hours, %i minutes, %i seconds, %i milliseconds",
-                            newRecord->dependencies[i].hours,
-                            newRecord->dependencies[i].minutes,
-                            newRecord->dependencies[i].seconds,
-                            newRecord->dependencies[i].milliseconds);
-                break;
-            case kPlayerTime :
-                debugCN(1, kDebugActionRecord, "kPlayerTime, %i days, %i hours, %i minutes, %i seconds",
-                            newRecord->dependencies[i].hours,
-                            newRecord->dependencies[i].minutes,
-                            newRecord->dependencies[i].seconds,
-                            newRecord->dependencies[i].milliseconds);
-                break;
-            case kSceneCount :
-                debugCN(1, kDebugActionRecord, "kSceneCount, scene ID %i, hit count %s %i",
-                            newRecord->dependencies[i].hours,
-                            newRecord->dependencies[i].milliseconds == 1 ? ">" : newRecord->dependencies[i].milliseconds == 2 ? "<" : "==",
-                            newRecord->dependencies[i].seconds);
-                break;
-            case kResetOnNewDay : debugCN(1, kDebugActionRecord, "kResetOnNewDay"); break;
-            case kUseItem :
-                debugCN(1, kDebugActionRecord, "kUseItem, item ID %i %s",
-                            newRecord->dependencies[i].label,
-                            newRecord->dependencies[i].condition == kTrue ? "is held" : "is not held");
-                break;
-            case kTimeOfDay :
-                debugCN(1, kDebugActionRecord, "kTimeOfDay, %s",
-                            newRecord->dependencies[i].label == 0 ? "day" : newRecord->dependencies[i].label == 1 ? "night" : "dusk/dawn");
-                break;
-            case kTimerNotDone : debugCN(1, kDebugActionRecord, "kTimerNotDone"); break;
-            case kTimerDone : debugCN(1, kDebugActionRecord, "kTimerDone"); break;
-            case kDifficultyLevel :
-                debugCN(1, kDebugActionRecord, "kDifficultyLevel, level %i", newRecord->dependencies[i].condition);
-                break;
-            default: debugCN(1, kDebugActionRecord, "unknown"); break;
+        case kNone : 
+            debugCN(1, kDebugActionRecord, "kNone");
+            break;
+        case kInventory :
+            debugCN(1, kDebugActionRecord, "kInventory, item ID %i %s",
+                        newRecord->dependencies[i].label,
+                        newRecord->dependencies[i].condition == kTrue ? "is in possession" : "is not in possession");
+            break;
+        case kEventFlag :
+            debugCN(1, kDebugActionRecord, "kEventFlag, flag ID %i == %s",
+                        newRecord->dependencies[i].label,
+                        newRecord->dependencies[i].condition == kTrue ? "true" : "false");
+            break;
+        case kLogicCondition :
+            debugCN(1, kDebugActionRecord, "kLogicCondition, logic condition ID %i == %s",
+                        newRecord->dependencies[i].label,
+                        newRecord->dependencies[i].condition == kTrue ? "true" : "false");
+            break;
+        case kTotalTime :
+            debugCN(1, kDebugActionRecord, "kTotalTime, %i hours, %i minutes, %i seconds, %i milliseconds",
+                        newRecord->dependencies[i].hours,
+                        newRecord->dependencies[i].minutes,
+                        newRecord->dependencies[i].seconds,
+                        newRecord->dependencies[i].milliseconds);
+            break;
+        case kSceneTime :
+            debugCN(1, kDebugActionRecord, "kSceneTime, %i hours, %i minutes, %i seconds, %i milliseconds",
+                        newRecord->dependencies[i].hours,
+                        newRecord->dependencies[i].minutes,
+                        newRecord->dependencies[i].seconds,
+                        newRecord->dependencies[i].milliseconds);
+            break;
+        case kPlayerTime :
+            debugCN(1, kDebugActionRecord, "kPlayerTime, %i days, %i hours, %i minutes, %i seconds",
+                        newRecord->dependencies[i].hours,
+                        newRecord->dependencies[i].minutes,
+                        newRecord->dependencies[i].seconds,
+                        newRecord->dependencies[i].milliseconds);
+            break;
+        case kSceneCount :
+            debugCN(1, kDebugActionRecord, "kSceneCount, scene ID %i, hit count %s %i",
+                        newRecord->dependencies[i].hours,
+                        newRecord->dependencies[i].milliseconds == 1 ? ">" : newRecord->dependencies[i].milliseconds == 2 ? "<" : "==",
+                        newRecord->dependencies[i].seconds);
+            break;
+        case kResetOnNewDay :
+            debugCN(1, kDebugActionRecord, "kResetOnNewDay");
+            break;
+        case kUseItem :
+            debugCN(1, kDebugActionRecord, "kUseItem, item ID %i %s",
+                        newRecord->dependencies[i].label,
+                        newRecord->dependencies[i].condition == kTrue ? "is held" : "is not held");
+            break;
+        case kTimeOfDay :
+            debugCN(1, kDebugActionRecord, "kTimeOfDay, %s",
+                        newRecord->dependencies[i].label == 0 ? "day" : newRecord->dependencies[i].label == 1 ? "night" : "dusk/dawn");
+            break;
+        case kTimerNotDone :
+            debugCN(1, kDebugActionRecord, "kTimerNotDone");
+            break;
+        case kTimerDone :
+            debugCN(1, kDebugActionRecord, "kTimerDone");
+            break;
+        case kDifficultyLevel :
+            debugCN(1, kDebugActionRecord, "kDifficultyLevel, level %i", newRecord->dependencies[i].condition);
+            break;
+        default:
+            debugCN(1, kDebugActionRecord, "unknown");
+            break;
         }
         debugC(1, kDebugActionRecord, ", orFlag == %s", newRecord->dependencies[i].orFlag == true ? "true" : "false");
     }
 
-
     return true;
 }
 
@@ -233,136 +242,167 @@ void ActionManager::processActionRecords() {
         if (!record->isActive) {
             for (uint i = 0; i < record->dependencies.size(); ++i) {
                 DependencyRecord &dep = record->dependencies[i];
+
                 if (!dep.satisfied) {
                     switch (dep.type) {
-                        case kNone:
-                            dep.satisfied = true;
-                            break;
-                        case kInventory:
-                            switch (dep.condition) {
-                                case kFalse:
-                                    // Item not in possession or held
-                                    if (_engine->scene->_flags.items[dep.label] == kFalse &&
-                                        dep.label != _engine->scene->_flags.heldItem) {
-                                        dep.satisfied = true;
-                                    }
-                                    break;
-                                case kTrue:
-                                    if (_engine->scene->_flags.items[dep.label] == kTrue ||
-                                        dep.label == _engine->scene->_flags.heldItem) {
-                                        dep.satisfied = true;
-                                    }
-                                    break;
-                                default:
-                                    break;
+                    case kNone:
+                        dep.satisfied = true;
+                        break;
+                    case kInventory:
+                        switch (dep.condition) {
+                        case kFalse:
+                            // Item not in possession or held
+                            if (_engine->scene->_flags.items[dep.label] == kFalse &&
+                                dep.label != _engine->scene->_flags.heldItem) {
+                                dep.satisfied = true;
                             }
+
                             break;
-                        case kEventFlag:
-                            if (_engine->scene->getEventFlag(dep.label, (NancyFlag)dep.condition))
-                                // nancy1 has code for some timer array that never gets used
-                                // and is discarded from nancy2 onward
+                        case kTrue:
+                            if (_engine->scene->_flags.items[dep.label] == kTrue ||
+                                dep.label == _engine->scene->_flags.heldItem) {
                                 dep.satisfied = true;
-                            break;
-                        case kLogicCondition:
-                            if (_engine->scene->_flags.logicConditions[dep.label].flag == dep.condition) {
-                                // Wait for specified time before satisfying dependency condition
-                                Time elapsed = _engine->scene->_timers.totalTime - _engine->scene->_flags.logicConditions[dep.label].timestamp;
-                                if (elapsed >= dep.timeData)
-                                    dep.satisfied = true;
                             }
+
                             break;
-                        case kTotalTime:
-                            if (_engine->scene->_timers.totalTime >= dep.timeData)
-                                dep.satisfied = true;
+                        default:
                             break;
-                        case kSceneTime:
-                            if (_engine->scene->_timers.sceneTime >= dep.timeData)
+                        }
+
+                        break;
+                    case kEventFlag:
+                        if (_engine->scene->getEventFlag(dep.label, (NancyFlag)dep.condition)) {
+                            // nancy1 has code for some timer array that never gets used
+                            // and is discarded from nancy2 onward
+                            dep.satisfied = true;
+                        }
+
+                        break;
+                    case kLogicCondition:
+                        if (_engine->scene->_flags.logicConditions[dep.label].flag == dep.condition) {
+                            // Wait for specified time before satisfying dependency condition
+                            Time elapsed = _engine->scene->_timers.totalTime - _engine->scene->_flags.logicConditions[dep.label].timestamp;
+
+                            if (elapsed >= dep.timeData) {
                                 dep.satisfied = true;
-                            break;
-                        case kPlayerTime:
-                            // TODO almost definitely wrong, as the original engine treats player time differently
-                            if (_engine->scene->_timers.playerTime >= dep.timeData)
+                            }
+                        }
+
+                        break;
+                    case kTotalTime:
+                        if (_engine->scene->_timers.totalTime >= dep.timeData) {
+                            dep.satisfied = true;
+                        }
+
+                        break;
+                    case kSceneTime:
+                        if (_engine->scene->_timers.sceneTime >= dep.timeData) {
+                            dep.satisfied = true;
+                        }
+
+                        break;
+                    case kPlayerTime:
+                        // TODO almost definitely wrong, as the original engine treats player time differently
+                        if (_engine->scene->_timers.playerTime >= dep.timeData) {
+                            dep.satisfied = true;
+                        }
+
+                        break;
+                    /*case 7:
+                        // TODO
+                        break;
+                    case 8:
+                        // TODO
+                        break;*/
+                    case kSceneCount:
+                        // This dependency type keeps its data in the time variables
+                        // Also, I'm pretty sure it never gets used
+                        switch (dep.milliseconds) {
+                        case 1:
+                            if (dep.seconds < _engine->scene->_sceneState.sceneHitCount[dep.hours]) {
                                 dep.satisfied = true;
-                            break;
-                        /*case 7:
-                            // TODO
-                            break;
-                        case 8:
-                            // TODO
-                            break;*/
-                        case kSceneCount:
-                            // This dependency type keeps its data in the time variables
-                            // Also, I'm pretty sure it never gets used
-                            switch (dep.milliseconds) {
-                                case 1:
-                                    if (dep.seconds < _engine->scene->_sceneState.sceneHitCount[dep.hours])
-                                        dep.satisfied = true;
-                                    break;
-                                case 2:
-                                    if (dep.seconds > _engine->scene->_sceneState.sceneHitCount[dep.hours])
-                                        dep.satisfied = true;
-                                    break;
-                                case 3:
-                                    if (dep.seconds == _engine->scene->_sceneState.sceneHitCount[dep.hours])
-                                        dep.satisfied = true;
-                                    break;
                             }
+
                             break;
-                        case kResetOnNewDay:
-                            if (record->days == -1) {
-                                record->days = _engine->scene->_timers.playerTime.getDays();
+                        case 2:
+                            if (dep.seconds > _engine->scene->_sceneState.sceneHitCount[dep.hours]) {
                                 dep.satisfied = true;
-                                break;
                             }
 
-                            if (record->days < _engine->scene->_timers.playerTime.getDays()) {
-                                record->days = _engine->scene->_timers.playerTime.getDays();
-                                for (uint j = 0; j < record->dependencies.size(); ++j) {
-                                    if (record->dependencies[j].type == kPlayerTime) {
-                                        record->dependencies[j].satisfied = false;
-                                    }
-                                }
+                            break;
+                        case 3:
+                            if (dep.seconds == _engine->scene->_sceneState.sceneHitCount[dep.hours]) {
+                                dep.satisfied = true;
                             }
+
                             break;
-                        case kUseItem: {
-                            bool hasUnsatisfiedDeps = false;
+                        }
+
+                        break;
+                    case kResetOnNewDay:
+                        if (record->days == -1) {
+                            record->days = _engine->scene->_timers.playerTime.getDays();
+                            dep.satisfied = true;
+                            break;
+                        }
+
+                        if (record->days < _engine->scene->_timers.playerTime.getDays()) {
+                            record->days = _engine->scene->_timers.playerTime.getDays();
                             for (uint j = 0; j < record->dependencies.size(); ++j) {
-                                if (j != i && record->dependencies[j].satisfied == false) {
-                                    hasUnsatisfiedDeps = true;
+                                if (record->dependencies[j].type == kPlayerTime) {
+                                    record->dependencies[j].satisfied = false;
                                 }
                             }
+                        }
 
-                            if (hasUnsatisfiedDeps) {
-                                break;
+                        break;
+                    case kUseItem: {
+                        bool hasUnsatisfiedDeps = false;
+                        for (uint j = 0; j < record->dependencies.size(); ++j) {
+                            if (j != i && record->dependencies[j].satisfied == false) {
+                                hasUnsatisfiedDeps = true;
                             }
+                        }
+
+                        if (hasUnsatisfiedDeps) {
+                            break;
+                        }
 
-                            record->itemRequired = dep.label;
+                        record->itemRequired = dep.label;
 
-                            if (dep.condition == 1) {
-                                record->itemRequired += 100;
-                            }
-                            
+                        if (dep.condition == 1) {
+                            record->itemRequired += 100;
+                        }
+                        
+                        dep.satisfied = true;
+                        break;
+                    }
+                    case kTimeOfDay:
+                        if (dep.label == (byte)_engine->scene->_timers.timeOfDay) {
                             dep.satisfied = true;
-                            break;
                         }
-                        case kTimeOfDay:
-                            if (dep.label == (byte)_engine->scene->_timers.timeOfDay)
-                                dep.satisfied = true;
-                            break;
-                        case kTimerNotDone:
-                            if (_engine->scene->_timers.timerTime <= dep.timeData)
-                                dep.satisfied = true;
-                            break;
-                        case kTimerDone:
-                            if (_engine->scene->_timers.timerTime > dep.timeData)
-                                dep.satisfied = true;
-                            break;
-                        case kDifficultyLevel:
-                            if (dep.condition == _engine->scene->_difficulty)
-                                dep.satisfied = true;
-                            break;
-                        default:
-                            break;
+
+                        break;
+                    case kTimerNotDone:
+                        if (_engine->scene->_timers.timerTime <= dep.timeData) {
+                            dep.satisfied = true;
+                        }
+
+                        break;
+                    case kTimerDone:
+                        if (_engine->scene->_timers.timerTime > dep.timeData) {
+                            dep.satisfied = true;
+                        }
+
+                        break;
+                    case kDifficultyLevel:
+                        if (dep.condition == _engine->scene->_difficulty) {
+                            dep.satisfied = true;
+                        }
+
+                        break;
+                    default:
+                        break;
                     }
                 }
             }
diff --git a/engines/nancy/action/actionrecord.h b/engines/nancy/action/actionrecord.h
index cd57af9ea8..b4e131d3d2 100644
--- a/engines/nancy/action/actionrecord.h
+++ b/engines/nancy/action/actionrecord.h
@@ -108,23 +108,23 @@ protected:
     
     void finishExecution() {
         switch (execType) {
-            case kOneShot:
-                isDone = true;
-                state = kBegin;
-                break;
-            case kRepeating:
-                isDone = false;
-                isActive = false;
-                state = kBegin;
-
-                for (uint i = 0; i < dependencies.size(); ++i) {
-                    dependencies[i].satisfied = false;
-                }
-
-                break;
-            default:
-                state = kBegin;
-                break;
+        case kOneShot:
+            isDone = true;
+            state = kBegin;
+            break;
+        case kRepeating:
+            isDone = false;
+            isActive = false;
+            state = kBegin;
+
+            for (uint i = 0; i < dependencies.size(); ++i) {
+                dependencies[i].satisfied = false;
+            }
+
+            break;
+        default:
+            state = kBegin;
+            break;
         }
     }
 
diff --git a/engines/nancy/action/arfactory_v1.cpp b/engines/nancy/action/arfactory_v1.cpp
index 1eef792bdd..7ba3fa6a58 100644
--- a/engines/nancy/action/arfactory_v1.cpp
+++ b/engines/nancy/action/arfactory_v1.cpp
@@ -45,109 +45,109 @@ namespace Action {
 ActionRecord *ActionManager::createActionRecord(uint16 type) {
     type -= 0xA;
     switch (type) {
-        case 0x00:
-            return new Hot1FrSceneChange();
-        case 0x01:
-            return new HotMultiframeSceneChange();
-        case 0x02:
-            return new SceneChange();
-        case 0x03:
-            return new HotMultiframeMultisceneChange();
-        case 0x04:
-            return new Hot1FrExitSceneChange();
-        case 0x0C:
-            return new StartFrameNextScene();
-        case 0x14:
-            return new StartStopPlayerScrolling(); // TODO
-        case 0x15:
-            return new StartStopPlayerScrolling(); // TODO
-        case 0x28:
-            return new PlayPrimaryVideoChan0(_engine->scene->getViewport());
-        case 0x29:
-            return new PlaySecondaryVideo('0', _engine->scene->getViewport());
-        case 0x2A:
-            return new PlaySecondaryVideo('1', _engine->scene->getViewport());
-        case 0x2B:
-            return new PlaySecondaryMovie(_engine->scene->getViewport());
-        case 0x2C:
-            return new PlayStaticBitmapAnimation(false, _engine->scene->getViewport()); // PlayStaticBitmapAnimation
-        case 0x2D:
-            return new PlayStaticBitmapAnimation(true, _engine->scene->getViewport()); // PlayIntStaticBitmapAnimation
-        case 0x32:
-            return new MapCall();
-        case 0x33:
-            return new MapCallHot1Fr();
-        case 0x34:
-            return new MapCallHotMultiframe();
-        case 0x35:
-            return new MapLocationAccess();
-        case 0x38:
-            return new MapSound();
-        case 0x39:
-            return new MapAviOverride();
-        case 0x3A:
-            return new MapAviOverrideOff();
-        case 0x41:
-            return new TextBoxWrite();
-        case 0x42:
-            return new TextBoxClear();
-        case 0x5A:
-            return new BumpPlayerClock();
-        case 0x5B:
-            return new SaveContinueGame();
-        case 0x5C:
-            return new TurnOffMainRendering();
-        case 0x5D:
-            return new TurnOnMainRendering();
-        case 0x5E:
-            return new ResetAndStartTimer();
-        case 0x5F:
-            return new StopTimer();
-        case 0x60:
-            return new EventFlagsMultiHS();
-        case 0x61:
-            return new EventFlags();
-        case 0x62:
-            return new OrderingPuzzle(_engine->scene->getViewport());
-        case 0x63:
-            return new LoseGame();
-        case 0x64:
-            return new PushScene();
-        case 0x65:
-            return new PopScene();
-        case 0x66:
-            return new WinGame();
-        case 0x67:
-            return new DifficultyLevel();
-        case 0x68:
-            return new RotatingLockPuzzle(_engine->scene->getViewport());
-        case 0x69:
-            return new LeverPuzzle(_engine->scene->getViewport());
-        case 0x6A:
-            return new Telephone(_engine->scene->getViewport());
-        case 0x6B:
-            return new SliderPuzzle(_engine->scene->getViewport());
-        case 0x6C:
-            return new PasswordPuzzle(_engine->scene->getViewport());
-        case 0x6E:
-            return new AddInventoryNoHS();
-        case 0x6F:
-            return new RemoveInventoryNoHS();
-        case 0x70:
-            return new ShowInventoryItem(_engine->scene->getViewport());
-        case 0x8C:
-            return new PlayDigiSoundAndDie(); // TODO
-        case 0x8D:
-            return new PlayDigiSoundAndDie(); // TODO
-        case 0x8E:
-            return new PlaySoundPanFrameAnchorAndDie();
-        case 0x8F:
-            return new PlaySoundMultiHS();
-        case 0x96:
-            return new HintSystem();
-        default:
-            error("Action Record type %i is invalid!", type+0xA);
-            return nullptr;
+    case 0x00:
+        return new Hot1FrSceneChange();
+    case 0x01:
+        return new HotMultiframeSceneChange();
+    case 0x02:
+        return new SceneChange();
+    case 0x03:
+        return new HotMultiframeMultisceneChange();
+    case 0x04:
+        return new Hot1FrExitSceneChange();
+    case 0x0C:
+        return new StartFrameNextScene();
+    case 0x14:
+        return new StartStopPlayerScrolling(); // TODO
+    case 0x15:
+        return new StartStopPlayerScrolling(); // TODO
+    case 0x28:
+        return new PlayPrimaryVideoChan0(_engine->scene->getViewport());
+    case 0x29:
+        return new PlaySecondaryVideo('0', _engine->scene->getViewport());
+    case 0x2A:
+        return new PlaySecondaryVideo('1', _engine->scene->getViewport());
+    case 0x2B:
+        return new PlaySecondaryMovie(_engine->scene->getViewport());
+    case 0x2C:
+        return new PlayStaticBitmapAnimation(false, _engine->scene->getViewport()); // PlayStaticBitmapAnimation
+    case 0x2D:
+        return new PlayStaticBitmapAnimation(true, _engine->scene->getViewport()); // PlayIntStaticBitmapAnimation
+    case 0x32:
+        return new MapCall();
+    case 0x33:
+        return new MapCallHot1Fr();
+    case 0x34:
+        return new MapCallHotMultiframe();
+    case 0x35:
+        return new MapLocationAccess();
+    case 0x38:
+        return new MapSound();
+    case 0x39:
+        return new MapAviOverride();
+    case 0x3A:
+        return new MapAviOverrideOff();
+    case 0x41:
+        return new TextBoxWrite();
+    case 0x42:
+        return new TextBoxClear();
+    case 0x5A:
+        return new BumpPlayerClock();
+    case 0x5B:
+        return new SaveContinueGame();
+    case 0x5C:
+        return new TurnOffMainRendering();
+    case 0x5D:
+        return new TurnOnMainRendering();
+    case 0x5E:
+        return new ResetAndStartTimer();
+    case 0x5F:
+        return new StopTimer();
+    case 0x60:
+        return new EventFlagsMultiHS();
+    case 0x61:
+        return new EventFlags();
+    case 0x62:
+        return new OrderingPuzzle(_engine->scene->getViewport());
+    case 0x63:
+        return new LoseGame();
+    case 0x64:
+        return new PushScene();
+    case 0x65:
+        return new PopScene();
+    case 0x66:
+        return new WinGame();
+    case 0x67:
+        return new DifficultyLevel();
+    case 0x68:
+        return new RotatingLockPuzzle(_engine->scene->getViewport());
+    case 0x69:
+        return new LeverPuzzle(_engine->scene->getViewport());
+    case 0x6A:
+        return new Telephone(_engine->scene->getViewport());
+    case 0x6B:
+        return new SliderPuzzle(_engine->scene->getViewport());
+    case 0x6C:
+        return new PasswordPuzzle(_engine->scene->getViewport());
+    case 0x6E:
+        return new AddInventoryNoHS();
+    case 0x6F:
+        return new RemoveInventoryNoHS();
+    case 0x70:
+        return new ShowInventoryItem(_engine->scene->getViewport());
+    case 0x8C:
+        return new PlayDigiSoundAndDie(); // TODO
+    case 0x8D:
+        return new PlayDigiSoundAndDie(); // TODO
+    case 0x8E:
+        return new PlaySoundPanFrameAnchorAndDie();
+    case 0x8F:
+        return new PlaySoundMultiHS();
+    case 0x96:
+        return new HintSystem();
+    default:
+        error("Action Record type %i is invalid!", type+0xA);
+        return nullptr;
     }
 }
 
diff --git a/engines/nancy/action/leverpuzzle.cpp b/engines/nancy/action/leverpuzzle.cpp
index 129f234b46..8bc6e30c22 100644
--- a/engines/nancy/action/leverpuzzle.cpp
+++ b/engines/nancy/action/leverpuzzle.cpp
@@ -95,62 +95,62 @@ uint16 LeverPuzzle::readData(Common::SeekableReadStream &stream) {
 
 void LeverPuzzle::execute(Nancy::NancyEngine *engine) {
     switch (state) {
-        case kBegin:
-            init();
-            registerGraphics();
-            engine->sound->loadSound(moveSound);
-            engine->sound->loadSound(noMoveSound);
+    case kBegin:
+        init();
+        registerGraphics();
+        engine->sound->loadSound(moveSound);
+        engine->sound->loadSound(noMoveSound);
+
+        for (uint i = 0; i < 3; ++i) {
+            drawLever(i);
+        }
 
+        state = kRun;
+        // fall through
+    case kRun:
+        switch (solveState) {
+        case kNotSolved:
             for (uint i = 0; i < 3; ++i) {
-                drawLever(i);
+                if (playerSequence[i] != correctSequence[i]) {
+                    return;
+                }
             }
-
-            state = kRun;
-            // fall through
-        case kRun:
-            switch (solveState) {
-                case kNotSolved:
-                    for (uint i = 0; i < 3; ++i) {
-                        if (playerSequence[i] != correctSequence[i]) {
-                            return;
-                        }
-                    }
-                    
-                    engine->scene->setEventFlag(flagOnSolve);
-                    solveSoundPlayTime = _engine->getTotalPlayTime() + solveSoundDelay * 1000;
-                    solveState = kPlaySound;
-                    break;
-                case kPlaySound:
-                    if (_engine->getTotalPlayTime() <= solveSoundPlayTime) {
-                        break;
-                    }
-
-                    engine->sound->loadSound(solveSound);
-                    _engine->sound->playSound(solveSound);
-                    solveState = kWaitForSound;
-                    break;
-                case kWaitForSound:
-                    if (!_engine->sound->isSoundPlaying(solveSound)) {
-                        _engine->sound->stopSound(solveSound);
-                        state = kActionTrigger;
-                    }
-
-                    break;
+            
+            engine->scene->setEventFlag(flagOnSolve);
+            solveSoundPlayTime = _engine->getTotalPlayTime() + solveSoundDelay * 1000;
+            solveState = kPlaySound;
+            break;
+        case kPlaySound:
+            if (_engine->getTotalPlayTime() <= solveSoundPlayTime) {
+                break;
             }
 
+            engine->sound->loadSound(solveSound);
+            _engine->sound->playSound(solveSound);
+            solveState = kWaitForSound;
             break;
-        case kActionTrigger:
-            _engine->sound->stopSound(moveSound);
-            _engine->sound->stopSound(noMoveSound);
-            
-            if (solveState == kNotSolved) {
-                _engine->scene->changeScene(exitScene);
-                _engine->scene->setEventFlag(flagOnExit);
-            } else {
-                _engine->scene->changeScene(solveExitScene);
+        case kWaitForSound:
+            if (!_engine->sound->isSoundPlaying(solveSound)) {
+                _engine->sound->stopSound(solveSound);
+                state = kActionTrigger;
             }
 
-            finishExecution();
+            break;
+        }
+
+        break;
+    case kActionTrigger:
+        _engine->sound->stopSound(moveSound);
+        _engine->sound->stopSound(noMoveSound);
+        
+        if (solveState == kNotSolved) {
+            _engine->scene->changeScene(exitScene);
+            _engine->scene->setEventFlag(flagOnExit);
+        } else {
+            _engine->scene->changeScene(solveExitScene);
+        }
+
+        finishExecution();
     }
 }
 
@@ -176,19 +176,21 @@ void LeverPuzzle::handleInput(NancyInput &input) {
                 bool isMoving = false;
                 // Hardcoded by the original engine
                 switch (i) {
-                    case 0:
+                case 0:
+                    isMoving = true;
+                    break;
+                case 1:
+                    if (playerSequence[0] == 1) {
                         isMoving = true;
-                        break;
-                    case 1:
-                        if (playerSequence[0] == 1) {
-                            isMoving = true;
-                        }
-                        break;
-                    case 2:
-                        if (playerSequence[0] == 2) {
-                            isMoving = true;
-                        }
-                        break;
+                    }
+
+                    break;
+                case 2:
+                    if (playerSequence[0] == 2) {
+                        isMoving = true;
+                    }
+                    
+                    break;
                 }
 
                 if (isMoving) {
diff --git a/engines/nancy/action/orderingpuzzle.cpp b/engines/nancy/action/orderingpuzzle.cpp
index 7ce695ee7b..1351351217 100644
--- a/engines/nancy/action/orderingpuzzle.cpp
+++ b/engines/nancy/action/orderingpuzzle.cpp
@@ -107,59 +107,59 @@ uint16 OrderingPuzzle::readData(Common::SeekableReadStream &stream) {
 
 void OrderingPuzzle::execute(Nancy::NancyEngine *engine) {
     switch (state) {
-        case kBegin:
-            init();
-            registerGraphics();
-            _engine->sound->loadSound(clickSound);
-            _engine->sound->loadSound(solveSound);
-            state = kRun;
-            // fall through
-        case kRun:
-            switch (solveState) {
-                case kNotSolved:
-                    if (clickedSequence.size() != sequenceLength) {
-                        return;
-                    }
-
-                    for (uint i = 0; i < sequenceLength; ++i) {
-                        if (clickedSequence[i] != (int16)correctSequence[i]) {
-                            return;
-                        }
-                    }
-
-                    _engine->scene->setEventFlag(flagOnSolve);
-                    solveSoundPlayTime = _engine->getTotalPlayTime() + solveSoundDelay * 1000;
-                    solveState = kPlaySound;
-                    // fall through
-                case kPlaySound:
-                    if (_engine->getTotalPlayTime() <= solveSoundPlayTime) {
-                        break;
-                    }
+    case kBegin:
+        init();
+        registerGraphics();
+        _engine->sound->loadSound(clickSound);
+        _engine->sound->loadSound(solveSound);
+        state = kRun;
+        // fall through
+    case kRun:
+        switch (solveState) {
+        case kNotSolved:
+            if (clickedSequence.size() != sequenceLength) {
+                return;
+            }
 
-                    _engine->sound->playSound(solveSound);
-                    solveState = kWaitForSound;
-                    break;
-                case kWaitForSound:
-                    if (!_engine->sound->isSoundPlaying(solveSound)) {
-                        state = kActionTrigger;
-                    }
+            for (uint i = 0; i < sequenceLength; ++i) {
+                if (clickedSequence[i] != (int16)correctSequence[i]) {
+                    return;
+                }
+            }
 
-                    break;
+            _engine->scene->setEventFlag(flagOnSolve);
+            solveSoundPlayTime = _engine->getTotalPlayTime() + solveSoundDelay * 1000;
+            solveState = kPlaySound;
+            // fall through
+        case kPlaySound:
+            if (_engine->getTotalPlayTime() <= solveSoundPlayTime) {
+                break;
             }
+
+            _engine->sound->playSound(solveSound);
+            solveState = kWaitForSound;
             break;
-        case kActionTrigger:
-            _engine->sound->stopSound(clickSound);
-            _engine->sound->stopSound(solveSound);
-
-            if (solveState == kNotSolved) {
-                _engine->scene->changeScene(exitScene);
-                _engine->scene->setEventFlag(flagOnExit);
-            } else {
-                _engine->scene->changeScene(solveExitScene);
+        case kWaitForSound:
+            if (!_engine->sound->isSoundPlaying(solveSound)) {
+                state = kActionTrigger;
             }
 
-            finishExecution();
             break;
+        }
+        break;
+    case kActionTrigger:
+        _engine->sound->stopSound(clickSound);
+        _engine->sound->stopSound(solveSound);
+
+        if (solveState == kNotSolved) {
+            _engine->scene->changeScene(exitScene);
+            _engine->scene->setEventFlag(flagOnExit);
+        } else {
+            _engine->scene->changeScene(solveExitScene);
+        }
+
+        finishExecution();
+        break;
     }
 }
 
@@ -190,6 +190,7 @@ void OrderingPuzzle::handleInput(NancyInput &input) {
                         if (clickedSequence.back() == i) {
                             clickedSequence.pop_back();
                         }
+                        
                         return;
                     }
                 }
diff --git a/engines/nancy/action/passwordpuzzle.cpp b/engines/nancy/action/passwordpuzzle.cpp
index 611153e835..64eea71e90 100644
--- a/engines/nancy/action/passwordpuzzle.cpp
+++ b/engines/nancy/action/passwordpuzzle.cpp
@@ -74,90 +74,90 @@ uint16 PasswordPuzzle::readData(Common::SeekableReadStream &stream) {
 
 void PasswordPuzzle::execute(Nancy::NancyEngine *engine) {
     switch (state) {
-        case kBegin:
-            init();
-            registerGraphics();
-            nextBlinkTime = engine->getTotalPlayTime() + cursorBlinkTime;
-            state = kRun;
-            // fall through
-        case kRun:
-            switch (solveState) {
-                case kNotSolved: {
-                    Common::String &activeField = passwordFieldIsActive ? playerPasswordInput : playerNameInput;
-                    Common::String &correctField = passwordFieldIsActive ? password : name;
-                    Time currentTime = engine->getTotalPlayTime();
-
-                    if (playerHasHitReturn) {
-                        playerHasHitReturn = false;
-
-                        if (activeField.lastChar() == '-') {
-                            activeField.deleteLastChar();
-                            drawText();
-                        }
-
-                        if (activeField.equalsIgnoreCase(correctField)) {
-                            if (!passwordFieldIsActive) {
-                                passwordFieldIsActive = true;
-                            } else {
-                                engine->sound->loadSound(solveSound);
-                                engine->sound->playSound(solveSound);
-                                solveState = kSolved;
-                            }
-                        } else {
-                            engine->sound->loadSound(failSound);
-                            engine->sound->playSound(failSound);
-                            solveState = kFailed;
-                        }
-                        
-                        
-                    } else if (currentTime >= nextBlinkTime) {
-                        nextBlinkTime = currentTime + cursorBlinkTime;
-
-                        if (activeField.size() && activeField.lastChar() == '-') {
-                            activeField.deleteLastChar();
-                        } else {
-                            activeField += '-';
-                        }
-
-                        drawText();
-                    }
+    case kBegin:
+        init();
+        registerGraphics();
+        nextBlinkTime = engine->getTotalPlayTime() + cursorBlinkTime;
+        state = kRun;
+        // fall through
+    case kRun:
+        switch (solveState) {
+        case kNotSolved: {
+            Common::String &activeField = passwordFieldIsActive ? playerPasswordInput : playerNameInput;
+            Common::String &correctField = passwordFieldIsActive ? password : name;
+            Time currentTime = engine->getTotalPlayTime();
+
+            if (playerHasHitReturn) {
+                playerHasHitReturn = false;
 
-                    break;
+                if (activeField.lastChar() == '-') {
+                    activeField.deleteLastChar();
+                    drawText();
                 }
-                case kFailed:
-                    if (!engine->sound->isSoundPlaying(failSound)) {
-                        engine->sound->stopSound(failSound);
-                        state = kActionTrigger;
-                    }
 
-                    break;
-                case kSolved:
-                    if (!engine->sound->isSoundPlaying(solveSound)) {
-                        engine->sound->stopSound(solveSound);
-                        state = kActionTrigger;
+                if (activeField.equalsIgnoreCase(correctField)) {
+                    if (!passwordFieldIsActive) {
+                        passwordFieldIsActive = true;
+                    } else {
+                        engine->sound->loadSound(solveSound);
+                        engine->sound->playSound(solveSound);
+                        solveState = kSolved;
                     }
+                } else {
+                    engine->sound->loadSound(failSound);
+                    engine->sound->playSound(failSound);
+                    solveState = kFailed;
+                }
+                
+                
+            } else if (currentTime >= nextBlinkTime) {
+                nextBlinkTime = currentTime + cursorBlinkTime;
+
+                if (activeField.size() && activeField.lastChar() == '-') {
+                    activeField.deleteLastChar();
+                } else {
+                    activeField += '-';
+                }
+
+                drawText();
+            }
 
-                    break;
+            break;
+        }
+        case kFailed:
+            if (!engine->sound->isSoundPlaying(failSound)) {
+                engine->sound->stopSound(failSound);
+                state = kActionTrigger;
             }
 
             break;
-        case kActionTrigger:
-            switch (solveState) {
-                case kNotSolved:
-                    engine->scene->changeScene(exitScene);
-                    engine->scene->setEventFlag(flagOnExit);
-                    break;
-                case kFailed:
-                    engine->scene->changeScene(failExitScene);
-                    engine->scene->setEventFlag(flagOnFail.label);
-                    break;
-                case kSolved:
-                    engine->scene->changeScene(solveExitScene);
-                    engine->scene->setEventFlag(flagOnSolve.label);
-                    break;
+        case kSolved:
+            if (!engine->sound->isSoundPlaying(solveSound)) {
+                engine->sound->stopSound(solveSound);
+                state = kActionTrigger;
             }
 
-            finishExecution();
+            break;
+        }
+
+        break;
+    case kActionTrigger:
+        switch (solveState) {
+        case kNotSolved:
+            engine->scene->changeScene(exitScene);
+            engine->scene->setEventFlag(flagOnExit);
+            break;
+        case kFailed:
+            engine->scene->changeScene(failExitScene);
+            engine->scene->setEventFlag(flagOnFail.label);
+            break;
+        case kSolved:
+            engine->scene->changeScene(solveExitScene);
+            engine->scene->setEventFlag(flagOnSolve.label);
+            break;
+        }
+
+        finishExecution();
     }
 }
 
@@ -172,6 +172,7 @@ void PasswordPuzzle::handleInput(NancyInput &input) {
         if (input.input & NancyInput::kLeftMouseButtonUp) {
             state = kActionTrigger;
         }
+        
         return;
     }
 
diff --git a/engines/nancy/action/primaryvideo.cpp b/engines/nancy/action/primaryvideo.cpp
index ca4fd86524..fa79ce8226 100644
--- a/engines/nancy/action/primaryvideo.cpp
+++ b/engines/nancy/action/primaryvideo.cpp
@@ -48,29 +48,30 @@ void PlayPrimaryVideoChan0::ConditionFlag::read(Common::SeekableReadStream &stre
 
 bool PlayPrimaryVideoChan0::ConditionFlag::isSatisfied(NancyEngine *engine) const {
     switch (type) {
-        case ConditionFlag::kEventFlags:
-            return engine->scene->getEventFlag(flag);
-        case ConditionFlag::kInventory:
-            return engine->scene->hasItem(flag.label) == flag.flag;
-        default:
-            return false;
+    case ConditionFlag::kEventFlags:
+        return engine->scene->getEventFlag(flag);
+    case ConditionFlag::kInventory:
+        return engine->scene->hasItem(flag.label) == flag.flag;
+    default:
+        return false;
     }
 }
 
 void PlayPrimaryVideoChan0::ConditionFlag::set(NancyEngine *engine) const {
     switch (type) {
-        case ConditionFlag::kEventFlags:
-            engine->scene->setEventFlag(flag);
-            break;
-        case ConditionFlag::kInventory:
-            if (flag.flag == kTrue) {
-                engine->scene->addItemToInventory(flag.label);
-            } else {
-                engine->scene->removeItemFromInventory(flag.label);
-            }
-            break;
-        default:
-            break;
+    case ConditionFlag::kEventFlags:
+        engine->scene->setEventFlag(flag);
+        break;
+    case ConditionFlag::kInventory:
+        if (flag.flag == kTrue) {
+            engine->scene->addItemToInventory(flag.label);
+        } else {
+            engine->scene->removeItemFromInventory(flag.label);
+        }
+
+        break;
+    default:
+        break;
     }
 }
 
@@ -197,7 +198,7 @@ uint16 PlayPrimaryVideoChan0::readData(Common::SeekableReadStream &stream) {
     }
 
     uint16 numFlagsStructs = stream.readUint16LE();
-    if (numFlagsStructs > 0)  {
+    if (numFlagsStructs > 0) {
         for (uint16 i = 0; i < numFlagsStructs; ++i) {
             flagsStructs.push_back(FlagsStruct());
             FlagsStruct &flagsStruct = flagsStructs.back();
@@ -218,95 +219,97 @@ void PlayPrimaryVideoChan0::execute(NancyEngine *engine) {
     }
 
     switch (state) {
-        case kBegin:
-            init();
-            registerGraphics();
-            engine->sound->loadSound(sound);
-            engine->sound->playSound(sound);
-            state = kRun;
-		    activePrimaryVideo = this;
-            // fall through
-        case kRun:
-            if (!hasDrawnTextbox) {
-                hasDrawnTextbox = true;
-                engine->scene->getTextbox().clear();
-                engine->scene->getTextbox().addTextLine(text);
-
-                // Add responses when conditions have been satisfied
-                if (conditionalResponseCharacterID != 10) {
-                    addConditionalResponses(engine);
-                }
+    case kBegin:
+        init();
+        registerGraphics();
+        engine->sound->loadSound(sound);
+        engine->sound->playSound(sound);
+        state = kRun;
+        activePrimaryVideo = this;
+        // fall through
+    case kRun:
+        if (!hasDrawnTextbox) {
+            hasDrawnTextbox = true;
+            engine->scene->getTextbox().clear();
+            engine->scene->getTextbox().addTextLine(text);
+
+            // Add responses when conditions have been satisfied
+            if (conditionalResponseCharacterID != 10) {
+                addConditionalResponses(engine);
+            }
 
-                if (goodbyeResponseCharacterID != 10) {
-                    addGoodbye(engine);
-                }
+            if (goodbyeResponseCharacterID != 10) {
+                addGoodbye(engine);
+            }
 
-                for (uint i = 0; i < responses.size(); ++i) {
-                    auto &res = responses[i];
-                    if (res.conditionFlags.isSatisfied(engine)) {
-                        engine->scene->getTextbox().addTextLine(res.text);
-                    }
+            for (uint i = 0; i < responses.size(); ++i) {
+                auto &res = responses[i];
+
+                if (res.conditionFlags.isSatisfied(engine)) {
+                    engine->scene->getTextbox().addTextLine(res.text);
                 }
             }
+        }
 
-            if (!engine->sound->isSoundPlaying(sound)) {
-                engine->sound->stopSound(sound);
-                if (responses.size() == 0) {
-                    // NPC has finished talking with no responses available, auto-advance to next scene
-                    state = kActionTrigger;
-                } else {
-                    // NPC has finished talking, we have responses
-                    for (uint i = 0; i < 30; ++i) {
-                        if (engine->scene->getLogicCondition(i, kTrue)) {
-                            pickedResponse = i;
-                            break;
-                        }
+        if (!engine->sound->isSoundPlaying(sound)) {
+            engine->sound->stopSound(sound);
+            
+            if (responses.size() == 0) {
+                // NPC has finished talking with no responses available, auto-advance to next scene
+                state = kActionTrigger;
+            } else {
+                // NPC has finished talking, we have responses
+                for (uint i = 0; i < 30; ++i) {
+                    if (engine->scene->getLogicCondition(i, kTrue)) {
+                        pickedResponse = i;
+                        break;
                     }
+                }
 
-                    if (pickedResponse != -1) {
-                        // Player has picked response, play sound file and change state
-                        responseGenericSound.name = responses[pickedResponse].soundName;
-                        // TODO this is probably not correct
-                        engine->sound->loadSound(responseGenericSound);
-                        engine->sound->playSound(responseGenericSound);
-                        state = kActionTrigger;
-                    }
+                if (pickedResponse != -1) {
+                    // Player has picked response, play sound file and change state
+                    responseGenericSound.name = responses[pickedResponse].soundName;
+                    // TODO this is probably not correct
+                    engine->sound->loadSound(responseGenericSound);
+                    engine->sound->playSound(responseGenericSound);
+                    state = kActionTrigger;
                 }
             }
-            break;
-        case kActionTrigger:
-            // process flags structs
-            for (auto flags : flagsStructs) {
-                if (flags.conditions.isSatisfied(engine)) {
-                    flags.flagToSet.set(engine);
-                }
+        }
+        break;
+    case kActionTrigger:
+        // process flags structs
+        for (auto flags : flagsStructs) {
+            if (flags.conditions.isSatisfied(engine)) {
+                flags.flagToSet.set(engine);
             }
+        }
+        
+        if (pickedResponse != -1) {
+            // Set response's event flag, if any
+            engine->scene->setEventFlag(responses[pickedResponse].flagDesc);
+        }
+
+        if (!engine->sound->isSoundPlaying(responseGenericSound)) {
+            engine->sound->stopSound(responseGenericSound);
             
             if (pickedResponse != -1) {
-                // Set response's event flag, if any
-                engine->scene->setEventFlag(responses[pickedResponse].flagDesc);
-            }
+                engine->scene->changeScene(responses[pickedResponse].sceneChange);
+            } else {
+                // Evaluate scene branch structs here
 
-            if (!engine->sound->isSoundPlaying(responseGenericSound)) {
-                engine->sound->stopSound(responseGenericSound);
-                
-                if (pickedResponse != -1) {
-                    engine->scene->changeScene(responses[pickedResponse].sceneChange);
-                } else {
-                    // Evaluate scene branch structs here
-
-                    if (isDialogueExitScene == kFalse) {
-                        engine->scene->changeScene(sceneChange);
-                    } else if (doNotPop == kFalse) {
-                        // Exit dialogue
-                        engine->scene->popScene();
-                    }
+                if (isDialogueExitScene == kFalse) {
+                    engine->scene->changeScene(sceneChange);
+                } else if (doNotPop == kFalse) {
+                    // Exit dialogue
+                    engine->scene->popScene();
                 }
-                
-                finishExecution();
             }
+            
+            finishExecution();
+        }
 
-            break;
+        break;
     }
 }
 
diff --git a/engines/nancy/action/recordtypes.cpp b/engines/nancy/action/recordtypes.cpp
index d73d0e9ba5..9cc13cd0d6 100644
--- a/engines/nancy/action/recordtypes.cpp
+++ b/engines/nancy/action/recordtypes.cpp
@@ -65,22 +65,22 @@ uint16 HotMultiframeSceneChange::readData(Common::SeekableReadStream &stream) {
 
 void HotMultiframeSceneChange::execute(NancyEngine *engine) {
     switch (state) {
-        case kBegin:
-            // turn main rendering on
-            state = kRun;
-            // fall through
-        case kRun:
-            hasHotspot = false;
-            for (uint i = 0; i < hotspots.size(); ++i) {
-                if (hotspots[i].frameID == engine->scene->getSceneInfo().frameID) {
-                    hasHotspot = true;
-                    hotspot = hotspots[i].coords;
-                }
+    case kBegin:
+        // turn main rendering on
+        state = kRun;
+        // fall through
+    case kRun:
+        hasHotspot = false;
+        for (uint i = 0; i < hotspots.size(); ++i) {
+            if (hotspots[i].frameID == engine->scene->getSceneInfo().frameID) {
+                hasHotspot = true;
+                hotspot = hotspots[i].coords;
             }
-            break;
-        case kActionTrigger:
-            SceneChange::execute(engine);
-            break;
+        }
+        break;
+    case kActionTrigger:
+        SceneChange::execute(engine);
+        break;
     }
 }
 
@@ -92,20 +92,20 @@ uint16 Hot1FrSceneChange::readData(Common::SeekableReadStream &stream) {
 
 void Hot1FrSceneChange::execute(NancyEngine *engine) {
     switch (state) {
-        case kBegin:
-            hotspot = hotspotDesc.coords;
-            state = kRun;
-            // fall through
-        case kRun:
-            if (hotspotDesc.frameID == engine->scene->getSceneInfo().frameID) {
-                hasHotspot = true;
-            } else {
-                hasHotspot = false;
-            }
-            break;
-        case kActionTrigger:
-            SceneChange::execute(engine);
-            break;
+    case kBegin:
+        hotspot = hotspotDesc.coords;
+        state = kRun;
+        // fall through
+    case kRun:
+        if (hotspotDesc.frameID == engine->scene->getSceneInfo().frameID) {
+            hasHotspot = true;
+        } else {
+            hasHotspot = false;
+        }
+        break;
+    case kActionTrigger:
+        SceneChange::execute(engine);
+        break;
     }
 }
 
@@ -144,18 +144,18 @@ uint16 MapCallHot1Fr::readData(Common::SeekableReadStream &stream) {
 
 void MapCallHot1Fr::execute(NancyEngine *engine) {
     switch (state) {
-        case kBegin:
-            hotspot = hotspotDesc.coords;
-            state = kRun;
-            // fall through
-        case kRun:
-            if (hotspotDesc.frameID == engine->scene->getSceneInfo().frameID) {
-                hasHotspot = true;
-            }
-            break;
-        case kActionTrigger:
-            MapCall::execute(engine);
-            break;
+    case kBegin:
+        hotspot = hotspotDesc.coords;
+        state = kRun;
+        // fall through
+    case kRun:
+        if (hotspotDesc.frameID == engine->scene->getSceneInfo().frameID) {
+            hasHotspot = true;
+        }
+        break;
+    case kActionTrigger:
+        MapCall::execute(engine);
+        break;
     }
 }
 
@@ -171,21 +171,21 @@ uint16 MapCallHotMultiframe::readData(Common::SeekableReadStream &stream) {
 
 void MapCallHotMultiframe::execute(NancyEngine *engine) {
     switch (state) {
-        case kBegin:
-            state = kRun;
-            // fall through
-        case kRun:
-            hasHotspot = false;
-            for (uint i = 0; i < hotspots.size(); ++i) {
-                if (hotspots[i].frameID == engine->scene->getSceneInfo().frameID) {
-                    hasHotspot = true;
-                    hotspot = hotspots[i].coords;
-                }
+    case kBegin:
+        state = kRun;
+        // fall through
+    case kRun:
+        hasHotspot = false;
+        for (uint i = 0; i < hotspots.size(); ++i) {
+            if (hotspots[i].frameID == engine->scene->getSceneInfo().frameID) {
+                hasHotspot = true;
+                hotspot = hotspots[i].coords;
             }
-            break;
-        case kActionTrigger:
-            MapCall::execute(engine);
-            break;  
+        }
+        break;
+    case kActionTrigger:
+        MapCall::execute(engine);
+        break;  
     }
 }
 
@@ -274,11 +274,13 @@ void EventFlags::execute(NancyEngine *engine) {
 uint16 EventFlagsMultiHS::readData(Common::SeekableReadStream &stream) {
     uint16 returnSize = EventFlags::readData(stream);
     uint16 numHotspots = stream.readUint16LE();
+
     for (uint16 i = 0; i < numHotspots; ++i) {
         hotspots.push_back(HotspotDescription());
         HotspotDescription &newDesc = hotspots[i];
         newDesc.readData(stream);
     }
+
     returnSize += numHotspots * 0x12 + 0x2;
 
     return returnSize;
@@ -286,24 +288,26 @@ uint16 EventFlagsMultiHS::readData(Common::SeekableReadStream &stream) {
 
 void EventFlagsMultiHS::execute(NancyEngine *engine) {
     switch (state) {
-        case kBegin:
-            // turn main rendering on
-            state = kRun;
-            // fall through
-        case kRun:
-            hasHotspot = false;
-            for (uint i = 0; i < hotspots.size(); ++i) {
-                if (hotspots[i].frameID == engine->scene->getSceneInfo().frameID) {
-                    hasHotspot = true;
-                    hotspot = hotspots[i].coords;
-                }
+    case kBegin:
+        // turn main rendering on
+        state = kRun;
+        // fall through
+    case kRun:
+        hasHotspot = false;
+
+        for (uint i = 0; i < hotspots.size(); ++i) {
+            if (hotspots[i].frameID == engine->scene->getSceneInfo().frameID) {
+                hasHotspot = true;
+                hotspot = hotspots[i].coords;
             }
-            break;
-        case kActionTrigger:
-            hasHotspot = false;
-            EventFlags::execute(engine);
-            finishExecution();
-            break;
+        }
+
+        break;
+    case kActionTrigger:
+        hasHotspot = false;
+        EventFlags::execute(engine);
+        finishExecution();
+        break;
     }
 }
 
@@ -387,44 +391,45 @@ uint16 ShowInventoryItem::readData(Common::SeekableReadStream &stream) {
 
 void ShowInventoryItem::execute(NancyEngine *engine) {
     switch (state) {
-        case kBegin:
-            init();
-            registerGraphics();
-            state = kRun;
-            // fall through
-        case kRun: {
-            int newFrame = -1;
-
-            for (uint i = 0; i < bitmaps.size(); ++i) {
-                if (bitmaps[i].frameID == engine->scene->getSceneInfo().frameID) {
-                    newFrame = i;
-                    break;
-                }
+    case kBegin:
+        init();
+        registerGraphics();
+        state = kRun;
+        // fall through
+    case kRun: {
+        int newFrame = -1;
+
+        for (uint i = 0; i < bitmaps.size(); ++i) {
+            if (bitmaps[i].frameID == engine->scene->getSceneInfo().frameID) {
+                newFrame = i;
+                break;
             }
+        }
 
-            if (newFrame != drawnFrameID) {
-                drawnFrameID = newFrame;
-                if (newFrame != -1) {
-                    hasHotspot = true;
-                    hotspot = bitmaps[newFrame].dest;
-                    _drawSurface.create(_fullSurface, bitmaps[newFrame].src);
-                    _screenPosition = bitmaps[newFrame].dest;
-                    setVisible(true);
-                } else {
-                    hasHotspot = false;
-                    setVisible(false);
-                }
+        if (newFrame != drawnFrameID) {
+            drawnFrameID = newFrame;
+
+            if (newFrame != -1) {
+                hasHotspot = true;
+                hotspot = bitmaps[newFrame].dest;
+                _drawSurface.create(_fullSurface, bitmaps[newFrame].src);
+                _screenPosition = bitmaps[newFrame].dest;
+                setVisible(true);
+            } else {
+                hasHotspot = false;
+                setVisible(false);
             }
-                   
-            break;
         }
-        case kActionTrigger:
-            engine->sound->playSound(24); // Hardcoded by original engine
-            engine->scene->addItemToInventory(objectID);
-            setVisible(false);
-            hasHotspot = false;
-            finishExecution();
-            break;
+                
+        break;
+    }
+    case kActionTrigger:
+        engine->sound->playSound(24); // Hardcoded by original engine
+        engine->scene->addItemToInventory(objectID);
+        setVisible(false);
+        hasHotspot = false;
+        finishExecution();
+        break;
     }
 }
 
@@ -439,26 +444,27 @@ uint16 PlayDigiSoundAndDie::readData(Common::SeekableReadStream &stream) {
 
 void PlayDigiSoundAndDie::execute(NancyEngine *engine) {
     switch (state) {
-        case kBegin:
-            engine->sound->loadSound(sound);
-            engine->sound->playSound(sound);
-            state = kRun;
-            break;
-        case kRun:
-            if (!engine->sound->isSoundPlaying(sound)) {
-                state = kActionTrigger;
-            }
-            break;
-        case kActionTrigger:
-            if (sceneChange.sceneID != 9999) {
-                engine->scene->changeScene(sceneChange);
-            }
-            
-            engine->scene->setEventFlag(flagOnTrigger);
-            engine->sound->stopSound(sound);
+    case kBegin:
+        engine->sound->loadSound(sound);
+        engine->sound->playSound(sound);
+        state = kRun;
+        break;
+    case kRun:
+        if (!engine->sound->isSoundPlaying(sound)) {
+            state = kActionTrigger;
+        }
 
-            finishExecution();
-            break;
+        break;
+    case kActionTrigger:
+        if (sceneChange.sceneID != 9999) {
+            engine->scene->changeScene(sceneChange);
+        }
+        
+        engine->scene->setEventFlag(flagOnTrigger);
+        engine->sound->stopSound(sound);
+
+        finishExecution();
+        break;
     }
 }
 
@@ -485,30 +491,30 @@ uint16 PlaySoundMultiHS::readData(Common::SeekableReadStream &stream) {
 
 void PlaySoundMultiHS::execute(Nancy::NancyEngine *engine) {
     switch (state) {
-        case kBegin:
-            state = kRun;
-            // fall through
-        case kRun: {
-            hasHotspot = false;
-            uint currentFrame = engine->scene->getSceneInfo().frameID;
-
-            for (uint i = 0; i < hotspots.size(); ++i) {
-                if (hotspots[i].frameID == currentFrame) {
-                    hotspot = hotspots[i].coords;
-                    hasHotspot = true;
-                    break;
-                }
+    case kBegin:
+        state = kRun;
+        // fall through
+    case kRun: {
+        hasHotspot = false;
+        uint currentFrame = engine->scene->getSceneInfo().frameID;
+
+        for (uint i = 0; i < hotspots.size(); ++i) {
+            if (hotspots[i].frameID == currentFrame) {
+                hotspot = hotspots[i].coords;
+                hasHotspot = true;
+                break;
             }
-
-            break;
         }
-        case kActionTrigger:
-            engine->sound->loadSound(sound);
-            engine->sound->playSound(sound);
-            engine->scene->changeScene(sceneChange);
-            engine->scene->setEventFlag(flag);
-            finishExecution();
-            break;
+
+        break;
+    }
+    case kActionTrigger:
+        engine->sound->loadSound(sound);
+        engine->sound->playSound(sound);
+        engine->scene->changeScene(sceneChange);
+        engine->scene->setEventFlag(flag);
+        finishExecution();
+        break;
     }
 }
 
@@ -520,36 +526,37 @@ uint16 HintSystem::readData(Common::SeekableReadStream &stream) {
 
 void HintSystem::execute(Nancy::NancyEngine *engine) {
     switch (state) {
-        case kBegin:
-            if (engine->scene->getHintsRemaining() > 0) {
-                selectHint(engine);
-            } else {
-                getHint(0, engine->scene->getDifficulty());
-            }
-
-            engine->scene->getTextbox().clear();
-            engine->scene->getTextbox().addTextLine(text);
+    case kBegin:
+        if (engine->scene->getHintsRemaining() > 0) {
+            selectHint(engine);
+        } else {
+            getHint(0, engine->scene->getDifficulty());
+        }
 
-            engine->sound->loadSound(genericSound);
-            engine->sound->playSound(genericSound);
-            state = kRun;
+        engine->scene->getTextbox().clear();
+        engine->scene->getTextbox().addTextLine(text);
+
+        engine->sound->loadSound(genericSound);
+        engine->sound->playSound(genericSound);
+        state = kRun;
+        break;
+    case kRun:
+        if (!engine->sound->isSoundPlaying(genericSound)) {
+            engine->sound->stopSound(genericSound);
+            state = kActionTrigger;
+        } else {
             break;
-        case kRun:
-            if (!engine->sound->isSoundPlaying(genericSound)) {
-                engine->sound->stopSound(genericSound);
-                state = kActionTrigger;
-            } else {
-                break;
-            }
-            // fall through
-        case kActionTrigger:
-            engine->scene->useHint(hintID, hintWeight);
-            engine->scene->getTextbox().clear();
+        }
 
-            engine->scene->changeScene(sceneChange);
+        // fall through
+    case kActionTrigger:
+        engine->scene->useHint(hintID, hintWeight);
+        engine->scene->getTextbox().clear();
 
-            isDone = true;
-            break;
+        engine->scene->changeScene(sceneChange);
+
+        isDone = true;
+        break;
     }
 }
 
@@ -565,6 +572,7 @@ void HintSystem::selectHint(Nancy::NancyEngine *engine) {
             if (flag.label == -1) {
                 break;
             }
+            
             if (!engine->scene->getEventFlag(flag.label, flag.flag)) {
                 satisfied = false;
                 break;
@@ -575,6 +583,7 @@ void HintSystem::selectHint(Nancy::NancyEngine *engine) {
             if (inv.label == -1) {
                 break;
             }
+
             if (engine->scene->hasItem(inv.label) != inv.flag) {
                 satisfied = false;
                 break;
diff --git a/engines/nancy/action/rotatinglockpuzzle.cpp b/engines/nancy/action/rotatinglockpuzzle.cpp
index 06cc7cfd69..293b046b05 100644
--- a/engines/nancy/action/rotatinglockpuzzle.cpp
+++ b/engines/nancy/action/rotatinglockpuzzle.cpp
@@ -108,60 +108,60 @@ uint16 RotatingLockPuzzle::readData(Common::SeekableReadStream &stream) {
 
 void RotatingLockPuzzle::execute(Nancy::NancyEngine *engine) {
     switch (state) {
-        case kBegin:
-            init();
-            registerGraphics();
+    case kBegin:
+        init();
+        registerGraphics();
 
+        for (uint i = 0; i < correctSequence.size(); ++i) {
+            currentSequence.push_back(_engine->_rnd->getRandomNumber(9));
+            drawDial(i);
+        }
+
+        _engine->sound->loadSound(clickSound);
+        _engine->sound->loadSound(solveSound);
+        state = kRun;
+        // fall through
+    case kRun:
+        switch (solveState) {
+        case kNotSolved:
             for (uint i = 0; i < correctSequence.size(); ++i) {
-                currentSequence.push_back(_engine->_rnd->getRandomNumber(9));
-                drawDial(i);
+                if (currentSequence[i] != (int16)correctSequence[i]) {
+                    return;
+                }
             }
 
-            _engine->sound->loadSound(clickSound);
-            _engine->sound->loadSound(solveSound);
-            state = kRun;
+            _engine->scene->setEventFlag(flagOnSolve);
+            solveSoundPlayTime = _engine->getTotalPlayTime() + solveSoundDelay * 1000;
+            solveState = kPlaySound;
             // fall through
-        case kRun:
-            switch (solveState) {
-                case kNotSolved:
-                    for (uint i = 0; i < correctSequence.size(); ++i) {
-                        if (currentSequence[i] != (int16)correctSequence[i]) {
-                            return;
-                        }
-                    }
-
-                    _engine->scene->setEventFlag(flagOnSolve);
-                    solveSoundPlayTime = _engine->getTotalPlayTime() + solveSoundDelay * 1000;
-                    solveState = kPlaySound;
-                    // fall through
-                case kPlaySound:
-                    if (_engine->getTotalPlayTime() <= solveSoundPlayTime) {
-                        break;
-                    }
-
-                    _engine->sound->playSound(solveSound);
-                    solveState = kWaitForSound;
-                    break;
-                case kWaitForSound:
-                    if (!_engine->sound->isSoundPlaying(solveSound)) {
-                        state = kActionTrigger;
-                    }
-
-                    break;
+        case kPlaySound:
+            if (_engine->getTotalPlayTime() <= solveSoundPlayTime) {
+                break;
             }
+
+            _engine->sound->playSound(solveSound);
+            solveState = kWaitForSound;
             break;
-        case kActionTrigger:
-            _engine->sound->stopSound(clickSound);
-            _engine->sound->stopSound(solveSound);
-
-            if (solveState == kNotSolved) {
-                _engine->scene->changeScene(exitScene);
-                _engine->scene->setEventFlag(flagOnExit);
-            } else {
-                _engine->scene->changeScene(solveExitScene);
+        case kWaitForSound:
+            if (!_engine->sound->isSoundPlaying(solveSound)) {
+                state = kActionTrigger;
             }
 
-            finishExecution();
+            break;
+        }
+        break;
+    case kActionTrigger:
+        _engine->sound->stopSound(clickSound);
+        _engine->sound->stopSound(solveSound);
+
+        if (solveState == kNotSolved) {
+            _engine->scene->changeScene(exitScene);
+            _engine->scene->setEventFlag(flagOnExit);
+        } else {
+            _engine->scene->changeScene(solveExitScene);
+        }
+
+        finishExecution();
     }
 }
 
@@ -176,6 +176,7 @@ void RotatingLockPuzzle::handleInput(NancyInput &input) {
         if (input.input & NancyInput::kLeftMouseButtonUp) {
             state = kActionTrigger;
         }
+
         return;
     }
 
@@ -189,6 +190,7 @@ void RotatingLockPuzzle::handleInput(NancyInput &input) {
                 currentSequence[i] = ++currentSequence[i] > 9 ? 0 : currentSequence[i];
                 drawDial(i);
             }
+
             return;
         }
     }
@@ -205,6 +207,7 @@ void RotatingLockPuzzle::handleInput(NancyInput &input) {
                 currentSequence[i] = n;
                 drawDial(i);
             }
+            
             return;
         }
     }
diff --git a/engines/nancy/action/secondarymovie.cpp b/engines/nancy/action/secondarymovie.cpp
index 66c502eb13..86834d89c7 100644
--- a/engines/nancy/action/secondarymovie.cpp
+++ b/engines/nancy/action/secondarymovie.cpp
@@ -29,8 +29,9 @@
 namespace Nancy {
 namespace Action {
 
-PlaySecondaryMovie::~PlaySecondaryMovie()  {
-    _decoder.close(); 
+PlaySecondaryMovie::~PlaySecondaryMovie() {
+    _decoder.close();
+    
     if (hideMouse == kTrue && unknown == 5) {
         _engine->setMouseEnabled(true);
     }
@@ -47,6 +48,7 @@ uint16 PlaySecondaryMovie::readData(Common::SeekableReadStream &stream) {
     isReverse = (NancyFlag)stream.readUint16LE();
     firstFrame = (NancyFlag)stream.readUint16LE();
     lastFrame = (NancyFlag)stream.readUint16LE();
+
     for (uint i = 0; i < 15; ++i) {
         frameFlags[i].frameID = stream.readSint16LE();
         frameFlags[i].flagDesc.label = stream.readSint16LE();
@@ -67,9 +69,10 @@ uint16 PlaySecondaryMovie::readData(Common::SeekableReadStream &stream) {
 }
 
 void PlaySecondaryMovie::init() {
-    if(_decoder.isVideoLoaded()) {
+    if (_decoder.isVideoLoaded()) {
         _decoder.close();
     }
+
     _decoder.loadFile(videoName + ".avf");
     _drawSurface.create(_decoder.getWidth(), _decoder.getHeight(), GraphicsManager::pixelFormat);
     _screenPosition = _drawSurface.getBounds();
@@ -84,6 +87,7 @@ void PlaySecondaryMovie::updateGraphics() {
 
     if (!_decoder.isPlaying() && _isVisible && !isFinished) {
         _decoder.start();
+
 		if (isReverse == kTrue) {
 			_decoder.setRate(-_decoder.getRate());
 			_decoder.seekToFrame(lastFrame);
@@ -94,11 +98,13 @@ void PlaySecondaryMovie::updateGraphics() {
 
     if (_decoder.needsUpdate()) {
         uint descID = 0;
+
         for (uint i = 0; i < videoDescs.size(); ++i) {
             if (videoDescs[i].frameID == _curViewportFrame) {
                 descID = i;
             }
         }
+
         _drawSurface.blitFrom(*_decoder.decodeNextFrame(), videoDescs[descID].srcRect, Common::Point());
         _needsRedraw = true;
         
@@ -128,54 +134,54 @@ void PlaySecondaryMovie::onPause(bool pause) {
 
 void PlaySecondaryMovie::execute(NancyEngine *engine) {
     switch (state) {
-        case kBegin:
-            init();
-            registerGraphics();
-            engine->sound->loadSound(sound);
-            engine->sound->playSound(sound);
-            if (hideMouse == kTrue) {
-                engine->setMouseEnabled(false);
-            }
-            state = kRun;
-            // fall through
-        case kRun: {
-            
-            int newFrame = _engine->scene->getSceneInfo().frameID;
-
-            if (newFrame != _curViewportFrame) {
-                _curViewportFrame = newFrame;
-                int activeFrame = -1;
-                for (uint i = 0; i < videoDescs.size(); ++i) {
-                    if (newFrame == videoDescs[i].frameID) {
-                        activeFrame = i;
-                        break;
-                    }
-                }
+    case kBegin:
+        init();
+        registerGraphics();
+        engine->sound->loadSound(sound);
+        engine->sound->playSound(sound);
+
+        if (hideMouse == kTrue) {
+            engine->setMouseEnabled(false);
+        }
 
-                if (activeFrame != -1) {
-                    _screenPosition = videoDescs[activeFrame].destRect;
-                    setVisible(true);
-                } else {
-                    setVisible(false);
+        state = kRun;
+        // fall through
+    case kRun: {
+        int newFrame = _engine->scene->getSceneInfo().frameID;
+
+        if (newFrame != _curViewportFrame) {
+            _curViewportFrame = newFrame;
+            int activeFrame = -1;
+            for (uint i = 0; i < videoDescs.size(); ++i) {
+                if (newFrame == videoDescs[i].frameID) {
+                    activeFrame = i;
+                    break;
                 }
             }
 
-            break;
-        }
-        case kActionTrigger:
-            triggerFlags.execute(engine);
-            if (unknown == 5) {
-                engine->scene->changeScene(sceneChange);
+            if (activeFrame != -1) {
+                _screenPosition = videoDescs[activeFrame].destRect;
+                setVisible(true);
             } else {
-                // Not changing the scene so enable the mouse now
-                if (hideMouse == kTrue) {
-                    engine->setMouseEnabled(true);
-                }
+                setVisible(false);
+            }
+        }
+
+        break;
+    }
+    case kActionTrigger:
+        triggerFlags.execute(engine);
+        if (unknown == 5) {
+            engine->scene->changeScene(sceneChange);
+        } else {
+            // Not changing the scene so enable the mouse now
+            if (hideMouse == kTrue) {
+                engine->setMouseEnabled(true);
             }
-            
+        }
 
-            finishExecution();
-            break;
+        finishExecution();
+        break;
     }
 }
 
diff --git a/engines/nancy/action/secondaryvideo.cpp b/engines/nancy/action/secondaryvideo.cpp
index 9860cbaa24..596a7b87bf 100644
--- a/engines/nancy/action/secondaryvideo.cpp
+++ b/engines/nancy/action/secondaryvideo.cpp
@@ -68,40 +68,43 @@ void PlaySecondaryVideo::updateGraphics() {
         }
         
         switch (hoverState) {
-            case kNoHover:
-                if (_isHovered) {
-                    _decoder.seekToFrame(onHoverFirstFrame);
-                    
-                    hoverState = kHover;
-                } else {
-                    if (_decoder.getCurFrame() == loopLastFrame) {
-                        // loop back to beginning
-                        _decoder.seekToFrame(loopFirstFrame);
-                    }
-                    break;
-                }
-                // fall through
-            case kHover:
-                if (!_isHovered) {
-                    // Stopped hovering, reverse playback
-                    _decoder.seekToFrame(onHoverEndLastFrame);
-                    _decoder.setRate(-_decoder.getRate());
-                    if (!_decoder.isPlaying()) {
-                        _decoder.start();
-                    }
-                    hoverState = kEndHover;
-                } else {
-                    break;
-                }
-                // fall through
-            case kEndHover:
-                if (_decoder.getCurFrame() == onHoverEndFirstFrame) {
-                    // reversed playback has ended, go back to no hover state
+        case kNoHover:
+            if (_isHovered) {
+                _decoder.seekToFrame(onHoverFirstFrame);
+                
+                hoverState = kHover;
+            } else {
+                if (_decoder.getCurFrame() == loopLastFrame) {
+                    // loop back to beginning
                     _decoder.seekToFrame(loopFirstFrame);
-                    _decoder.setRate(-_decoder.getRate());
-                    hoverState = kNoHover;
                 }
+
                 break;
+            }
+            // fall through
+        case kHover:
+            if (!_isHovered) {
+                // Stopped hovering, reverse playback
+                _decoder.seekToFrame(onHoverEndLastFrame);
+                _decoder.setRate(-_decoder.getRate());
+                if (!_decoder.isPlaying()) {
+                    _decoder.start();
+                }
+
+                hoverState = kEndHover;
+            } else {
+                break;
+            }
+            // fall through
+        case kEndHover:
+            if (_decoder.getCurFrame() == onHoverEndFirstFrame) {
+                // reversed playback has ended, go back to no hover state
+                _decoder.seekToFrame(loopFirstFrame);
+                _decoder.setRate(-_decoder.getRate());
+                hoverState = kNoHover;
+            }
+
+            break;
         }
 
         if (_decoder.needsUpdate() && !_screenPosition.isEmpty()) {
@@ -111,6 +114,7 @@ void PlaySecondaryVideo::updateGraphics() {
                     break;
                 }
             }
+            
             _needsRedraw = true;
         }
     } else {
@@ -157,47 +161,47 @@ uint16 PlaySecondaryVideo::readData(Common::SeekableReadStream &stream) {
 
 void PlaySecondaryVideo::execute(NancyEngine *engine) {
     switch (state) {
-        case kBegin:
-            init();
-            registerGraphics();
-            state = kRun;
-            // fall through
-        case kRun: {
-            // Set correct position according to viewport frame
-            if (_currentViewportFrame != engine->scene->getSceneInfo().frameID) {
-                _currentViewportFrame = engine->scene->getSceneInfo().frameID;
-
-                int activeFrame = -1;
+    case kBegin:
+        init();
+        registerGraphics();
+        state = kRun;
+        // fall through
+    case kRun: {
+        // Set correct position according to viewport frame
+        if (_currentViewportFrame != engine->scene->getSceneInfo().frameID) {
+            _currentViewportFrame = engine->scene->getSceneInfo().frameID;
+
+            int activeFrame = -1;
 
-                for (uint i = 0; i < videoDescs.size(); ++i) {
-                    if ((uint16)videoDescs[i].frameID == _currentViewportFrame) {
-                        activeFrame = i;
-                    }
-                }
-
-                if (activeFrame != -1) {
-                    // Make the drawing destination rectangle valid
-                    _screenPosition = videoDescs[activeFrame].destRect;
-
-                    // Activate the hotspot
-                    hotspot = videoDescs[activeFrame].destRect;
-                    hasHotspot = true;
-                    _isPlaying = true;
-                    setVisible(true);
-                } else {
-                    setVisible(false);
-                    hasHotspot = false;
-                    _isPlaying = false;
+            for (uint i = 0; i < videoDescs.size(); ++i) {
+                if ((uint16)videoDescs[i].frameID == _currentViewportFrame) {
+                    activeFrame = i;
                 }
             }
 
-            break;
+            if (activeFrame != -1) {
+                // Make the drawing destination rectangle valid
+                _screenPosition = videoDescs[activeFrame].destRect;
+
+                // Activate the hotspot
+                hotspot = videoDescs[activeFrame].destRect;
+                hasHotspot = true;
+                _isPlaying = true;
+                setVisible(true);
+            } else {
+                setVisible(false);
+                hasHotspot = false;
+                _isPlaying = false;
+            }
         }
-        case kActionTrigger:
-            engine->scene->pushScene();
-            engine->scene->changeScene(sceneChange);
-            finishExecution();
-            break;
+
+        break;
+    }
+    case kActionTrigger:
+        engine->scene->pushScene();
+        engine->scene->changeScene(sceneChange);
+        finishExecution();
+        break;
     }
 }
 
diff --git a/engines/nancy/action/sliderpuzzle.cpp b/engines/nancy/action/sliderpuzzle.cpp
index c78eb69611..c82e94a0bb 100644
--- a/engines/nancy/action/sliderpuzzle.cpp
+++ b/engines/nancy/action/sliderpuzzle.cpp
@@ -109,74 +109,77 @@ uint16 SliderPuzzle::readData(Common::SeekableReadStream &stream) {
 
 void SliderPuzzle::execute(Nancy::NancyEngine *engine) {
     switch (state) {
-        case kBegin:
-            init();
-            registerGraphics();
-            if (!playerHasTriedPuzzle) {
-                Common::SeekableReadStream *spuz = engine->getBootChunkStream("SPUZ");
-                playerTileOrder.clear();
-                spuz->seek(engine->scene->getDifficulty() * 0x48);
-                for (uint y = 0; y < height; ++y) {
-                    playerTileOrder.push_back(Common::Array<int16>());
-                    for (uint x = 0; x < width; ++x) {
-                        playerTileOrder.back().push_back(spuz->readSint16LE());
-                    }
-                    spuz->skip((6 - width) * 2);
+    case kBegin:
+        init();
+        registerGraphics();
+        if (!playerHasTriedPuzzle) {
+            Common::SeekableReadStream *spuz = engine->getBootChunkStream("SPUZ");
+            playerTileOrder.clear();
+            spuz->seek(engine->scene->getDifficulty() * 0x48);
+            for (uint y = 0; y < height; ++y) {
+                playerTileOrder.push_back(Common::Array<int16>());
+
+                for (uint x = 0; x < width; ++x) {
+                    playerTileOrder.back().push_back(spuz->readSint16LE());
                 }
-                playerHasTriedPuzzle = true;
+
+                spuz->skip((6 - width) * 2);
             }
 
+            playerHasTriedPuzzle = true;
+        }
+
+        for (uint y = 0; y < height; ++y) {
+            for (uint x = 0; x < width; ++x) {
+                if (!srcRects[y][x].isEmpty()) {
+                    drawTile(playerTileOrder[y][x], x, y);
+                }
+            }
+        }
+
+        engine->sound->loadSound(clickSound);
+        state = kRun;
+        // fall through
+    case kRun:
+        switch (solveState) {
+        case kNotSolved:
             for (uint y = 0; y < height; ++y) {
                 for (uint x = 0; x < width; ++x) {
-                    if (!srcRects[y][x].isEmpty()) {
-                        drawTile(playerTileOrder[y][x], x, y);
+                    if (playerTileOrder[y][x] != correctTileOrder[y][x]) {
+                        return;
                     }
                 }
             }
 
-            engine->sound->loadSound(clickSound);
-            state = kRun;
-            // fall through
-        case kRun:
-            switch (solveState) {
-                case kNotSolved:
-                    for (uint y = 0; y < height; ++y) {
-                        for (uint x = 0; x < width; ++x) {
-                            if (playerTileOrder[y][x] != correctTileOrder[y][x]) {
-                                return;
-                            }
-                        }
-                    }
-
-                    engine->sound->loadSound(solveSound);
-                    engine->sound->playSound(solveSound);
-                    solveState = kWaitForSound;
-                    break;
-                case kWaitForSound:
-                    if (!engine->sound->isSoundPlaying(solveSound)) {
-                        engine->sound->stopSound(solveSound);
-                        state = kActionTrigger;
-                    }
-
-                    break;
+            engine->sound->loadSound(solveSound);
+            engine->sound->playSound(solveSound);
+            solveState = kWaitForSound;
+            break;
+        case kWaitForSound:
+            if (!engine->sound->isSoundPlaying(solveSound)) {
+                engine->sound->stopSound(solveSound);
+                state = kActionTrigger;
             }
 
             break;
-        case kActionTrigger:
-            switch (solveState) {
-                case kNotSolved:
-                    engine->scene->changeScene(exitScene);
-                    engine->scene->setEventFlag(flagOnExit);
-                    break;
-                case kWaitForSound:
-                    engine->scene->changeScene(solveExitScene);
-                    engine->scene->setEventFlag(flagOnSolve);
-                    playerHasTriedPuzzle = false;
-                    break;
-            }
+        }
+
+        break;
+    case kActionTrigger:
+        switch (solveState) {
+        case kNotSolved:
+            engine->scene->changeScene(exitScene);
+            engine->scene->setEventFlag(flagOnExit);
+            break;
+        case kWaitForSound:
+            engine->scene->changeScene(solveExitScene);
+            engine->scene->setEventFlag(flagOnSolve);
+            playerHasTriedPuzzle = false;
+            break;
+        }
 
-            engine->sound->stopSound(clickSound);
-            finishExecution();
+        engine->sound->stopSound(clickSound);
+        finishExecution();
     }
 }
 
@@ -191,6 +194,7 @@ void SliderPuzzle::handleInput(NancyInput &input) {
         if (input.input & NancyInput::kLeftMouseButtonUp) {
             state = kActionTrigger;
         }
+        
         return;
     }
 
@@ -246,38 +250,38 @@ void SliderPuzzle::handleInput(NancyInput &input) {
         if (input.input & NancyInput::kLeftMouseButtonUp) {
             _engine->sound->playSound(clickSound);
             switch (direction) {
-                case kUp: {
-                    uint curTileID = playerTileOrder[currentTileY][currentTileX];
-                    drawTile(curTileID, currentTileX, currentTileY - 1);
-                    undrawTile(currentTileX, currentTileY);
-                    playerTileOrder[currentTileY - 1][currentTileX] = curTileID;
-                    playerTileOrder[currentTileY][currentTileX] = -10;
-                    break;
-                }
-                case kDown: {
-                    uint curTileID = playerTileOrder[currentTileY][currentTileX];
-                    drawTile(curTileID, currentTileX, currentTileY + 1);
-                    undrawTile(currentTileX, currentTileY);
-                    playerTileOrder[currentTileY + 1][currentTileX] = curTileID;
-                    playerTileOrder[currentTileY][currentTileX] = -10;
-                    break;
-                }
-                case kLeft: {
-                    uint curTileID = playerTileOrder[currentTileY][currentTileX];
-                    drawTile(curTileID, currentTileX - 1, currentTileY);
-                    undrawTile(currentTileX, currentTileY);
-                    playerTileOrder[currentTileY][currentTileX - 1] = curTileID;
-                    playerTileOrder[currentTileY][currentTileX] = -10;
-                    break;
-                }
-                case kRight: {
-                    uint curTileID = playerTileOrder[currentTileY][currentTileX];
-                    drawTile(curTileID, currentTileX + 1, currentTileY);
-                    undrawTile(currentTileX, currentTileY);
-                    playerTileOrder[currentTileY][currentTileX + 1] = curTileID;
-                    playerTileOrder[currentTileY][currentTileX] = -10;
-                    break;
-                }
+            case kUp: {
+                uint curTileID = playerTileOrder[currentTileY][currentTileX];
+                drawTile(curTileID, currentTileX, currentTileY - 1);
+                undrawTile(currentTileX, currentTileY);
+                playerTileOrder[currentTileY - 1][currentTileX] = curTileID;
+                playerTileOrder[currentTileY][currentTileX] = -10;
+                break;
+            }
+            case kDown: {
+                uint curTileID = playerTileOrder[currentTileY][currentTileX];
+                drawTile(curTileID, currentTileX, currentTileY + 1);
+                undrawTile(currentTileX, currentTileY);
+                playerTileOrder[currentTileY + 1][currentTileX] = curTileID;
+                playerTileOrder[currentTileY][currentTileX] = -10;
+                break;
+            }
+            case kLeft: {
+                uint curTileID = playerTileOrder[currentTileY][currentTileX];
+                drawTile(curTileID, currentTileX - 1, currentTileY);
+                undrawTile(currentTileX, currentTileY);
+                playerTileOrder[currentTileY][currentTileX - 1] = curTileID;
+                playerTileOrder[currentTileY][currentTileX] = -10;
+                break;
+            }
+            case kRight: {
+                uint curTileID = playerTileOrder[currentTileY][currentTileX];
+                drawTile(curTileID, currentTileX + 1, currentTileY);
+                undrawTile(currentTileX, currentTileY);
+                playerTileOrder[currentTileY][currentTileX + 1] = curTileID;
+                playerTileOrder[currentTileY][currentTileX] = -10;
+                break;
+            }
             }
         }
     }
diff --git a/engines/nancy/action/staticbitmapanim.cpp b/engines/nancy/action/staticbitmapanim.cpp
index d78da910a7..49af328bcf 100644
--- a/engines/nancy/action/staticbitmapanim.cpp
+++ b/engines/nancy/action/staticbitmapanim.cpp
@@ -94,61 +94,38 @@ uint16 PlayStaticBitmapAnimation::readData(Common::SeekableReadStream &stream) {
 void PlayStaticBitmapAnimation::execute(NancyEngine *engine) {
     uint32 currentFrameTime = engine->getTotalPlayTime();
     switch (state) {
-        case kBegin:
-            init();
-            registerGraphics();
-            _engine->sound->loadSound(sound);
-            _engine->sound->playSound(sound);
-            state = kRun;
-            // fall through
-        case kRun: {
-            // Check the timer to see if we need to draw the next animation frame
-            if (nextFrameTime <= currentFrameTime) {
-                // World's worst if statement
-                if (engine->scene->getEventFlag(interruptCondition) ||
-                    (   (((currentFrame == loopLastFrame) && (isReverse == kFalse) && (isLooping == kFalse)) ||
-                        ((currentFrame == loopFirstFrame) && (isReverse == kTrue) && (isLooping == kFalse))) &&
-                            !engine->sound->isSoundPlaying(sound))   ) {
-                    
-                    state = kActionTrigger;
-
-                    // Not sure if hiding when triggered is a hack or the intended behavior, but it's here to fix
-                    // nancy1's safe lock light not turning off.
-                    setVisible(false);
-        
-                    if (!engine->sound->isSoundPlaying(sound)) {
-                        engine->sound->stopSound(sound);
-                    }
-                } else {
-                    // Check if we've moved the viewport
-                    uint16 newFrame = engine->scene->getSceneInfo().frameID;
-                    if (currentViewportFrame != newFrame) {
-                        currentViewportFrame = newFrame;
-                        for (uint i = 0; i < bitmaps.size(); ++i) {
-                            if (currentViewportFrame == bitmaps[i].frameID) {
-                                _screenPosition = bitmaps[i].dest;
-                                break;
-                            }
-                        }
-                    }
-                    
-                    nextFrameTime = currentFrameTime + frameTime;
-                    setFrame(currentFrame);
-                    if (isReverse == kTrue) {
-                        --currentFrame;
-                        currentFrame = currentFrame < loopFirstFrame ? loopLastFrame : currentFrame;
-                        return;
-                    } else {
-                        ++currentFrame;
-                        currentFrame = currentFrame > loopLastFrame ? loopFirstFrame : currentFrame;
-                        return;
-                    }
-                }                
+    case kBegin:
+        init();
+        registerGraphics();
+        _engine->sound->loadSound(sound);
+        _engine->sound->playSound(sound);
+        state = kRun;
+        // fall through
+    case kRun: {
+        // Check the timer to see if we need to draw the next animation frame
+        if (nextFrameTime <= currentFrameTime) {
+            // World's worst if statement
+            if (engine->scene->getEventFlag(interruptCondition) ||
+                (   (((currentFrame == loopLastFrame) && (isReverse == kFalse) && (isLooping == kFalse)) ||
+                    ((currentFrame == loopFirstFrame) && (isReverse == kTrue) && (isLooping == kFalse))) &&
+                        !engine->sound->isSoundPlaying(sound))   ) {
+                
+                state = kActionTrigger;
+
+                // Not sure if hiding when triggered is a hack or the intended behavior, but it's here to fix
+                // nancy1's safe lock light not turning off.
+                setVisible(false);
+    
+                if (!engine->sound->isSoundPlaying(sound)) {
+                    engine->sound->stopSound(sound);
+                }
             } else {
                 // Check if we've moved the viewport
                 uint16 newFrame = engine->scene->getSceneInfo().frameID;
+
                 if (currentViewportFrame != newFrame) {
                     currentViewportFrame = newFrame;
+
                     for (uint i = 0; i < bitmaps.size(); ++i) {
                         if (currentViewportFrame == bitmaps[i].frameID) {
                             _screenPosition = bitmaps[i].dest;
@@ -156,17 +133,45 @@ void PlayStaticBitmapAnimation::execute(NancyEngine *engine) {
                         }
                     }
                 }
-            }      
-            
-            break;
-        }
-        case kActionTrigger:
-            triggerFlags.execute(engine);
-            if (doNotChangeScene == kFalse) {
-                engine->scene->changeScene(sceneChange);
-                finishExecution();
+                
+                nextFrameTime = currentFrameTime + frameTime;
+                setFrame(currentFrame);
+
+                if (isReverse == kTrue) {
+                    --currentFrame;
+                    currentFrame = currentFrame < loopFirstFrame ? loopLastFrame : currentFrame;
+                    return;
+                } else {
+                    ++currentFrame;
+                    currentFrame = currentFrame > loopLastFrame ? loopFirstFrame : currentFrame;
+                    return;
+                }
+            }                
+        } else {
+            // Check if we've moved the viewport
+            uint16 newFrame = engine->scene->getSceneInfo().frameID;
+
+            if (currentViewportFrame != newFrame) {
+                currentViewportFrame = newFrame;
+                
+                for (uint i = 0; i < bitmaps.size(); ++i) {
+                    if (currentViewportFrame == bitmaps[i].frameID) {
+                        _screenPosition = bitmaps[i].dest;
+                        break;
+                    }
+                }
             }
-            break;
+        }      
+        
+        break;
+    }
+    case kActionTrigger:
+        triggerFlags.execute(engine);
+        if (doNotChangeScene == kFalse) {
+            engine->scene->changeScene(sceneChange);
+            finishExecution();
+        }
+        break;
     }
 }
 
diff --git a/engines/nancy/action/telephone.cpp b/engines/nancy/action/telephone.cpp
index db176baffe..06f6eb5ded 100644
--- a/engines/nancy/action/telephone.cpp
+++ b/engines/nancy/action/telephone.cpp
@@ -100,6 +100,7 @@ uint16 Telephone::readData(Common::SeekableReadStream &stream) {
     for (uint i = 0; i < numCalls; ++i) {
         calls.push_back(PhoneCall());
         PhoneCall &call = calls.back();
+
         for (uint j = 0; j < 11; ++j) {
             call.phoneNumber.push_back(stream.readByte());
         }
@@ -119,132 +120,133 @@ uint16 Telephone::readData(Common::SeekableReadStream &stream) {
 
 void Telephone::execute(NancyEngine *engine) {
     switch (state) {
-        case kBegin:
-            init();
-            registerGraphics();
-            _engine->sound->loadSound(dialToneSound);
-            _engine->sound->playSound(dialToneSound);
-            _engine->scene->getTextbox().clear();
-            _engine->scene->getTextbox().addTextLine(addressBookString);
-            state = kRun;
-            // fall through
-        case kRun:
-            switch (callState) {
-                case kWaiting:
-                    // Long phone numbers start with 1
-                    if (calledNumber.size() >= 11 || (calledNumber.size() >= 7 && (calledNumber[0] != 1))) {
-                        _engine->scene->getTextbox().clear();
-                        _engine->scene->getTextbox().addTextLine("ringing...<n><e>"); // Hardcoded in the original engine
-                        _engine->sound->loadSound(ringSound);
-                        _engine->sound->playSound(ringSound);
-                        callState = kRinging;
-                    }
-                    break;
-                case kButtonPress:
-                    if (!_engine->sound->isSoundPlaying(genericButtonSound)) {
-                        _engine->sound->stopSound(genericButtonSound);
-                        undrawButton(selected);
-                        callState = kWaiting;
-                    }
+    case kBegin:
+        init();
+        registerGraphics();
+        _engine->sound->loadSound(dialToneSound);
+        _engine->sound->playSound(dialToneSound);
+        _engine->scene->getTextbox().clear();
+        _engine->scene->getTextbox().addTextLine(addressBookString);
+        state = kRun;
+        // fall through
+    case kRun:
+        switch (callState) {
+        case kWaiting:
+            // Long phone numbers start with 1
+            if (calledNumber.size() >= 11 || (calledNumber.size() >= 7 && (calledNumber[0] != 1))) {
+                _engine->scene->getTextbox().clear();
+                _engine->scene->getTextbox().addTextLine("ringing...<n><e>"); // Hardcoded in the original engine
+                _engine->sound->loadSound(ringSound);
+                _engine->sound->playSound(ringSound);
+                callState = kRinging;
+            }
 
-                    break;
-                case kRinging:
-                    if (!_engine->sound->isSoundPlaying(ringSound)) {
-                        _engine->sound->stopSound(ringSound);
-
-                        uint numberLength = calledNumber[0] == 1 ? 11 : 7;
-                        for (uint i = 0; i < calls.size(); ++i) {
-                            bool invalid = false;
-                            for (uint j = 0; j < numberLength; ++j) {
-                                if (calledNumber[j] != calls[i].phoneNumber[j]) {
-                                    // Invalid number, move onto next
-                                    invalid = true;
-                                    break;
-                                }
-                            }
-
-                            if (invalid) {
-                                continue;
-                            }
-
-                            _engine->scene->getTextbox().clear();
-                            _engine->scene->getTextbox().addTextLine(calls[i].text);
-
-                            genericDialogueSound.name = calls[i].soundName;
-                            _engine->sound->loadSound(genericDialogueSound);
-                            _engine->sound->playSound(genericDialogueSound);
-                            selected = i;
-                            callState = kCall;
-
-                            return;
+            break;
+        case kButtonPress:
+            if (!_engine->sound->isSoundPlaying(genericButtonSound)) {
+                _engine->sound->stopSound(genericButtonSound);
+                undrawButton(selected);
+                callState = kWaiting;
+            }
+
+            break;
+        case kRinging:
+            if (!_engine->sound->isSoundPlaying(ringSound)) {
+                _engine->sound->stopSound(ringSound);
+                uint numberLength = calledNumber[0] == 1 ? 11 : 7;
+
+                for (uint i = 0; i < calls.size(); ++i) {
+                    bool invalid = false;
+
+                    for (uint j = 0; j < numberLength; ++j) {
+                        if (calledNumber[j] != calls[i].phoneNumber[j]) {
+                            // Invalid number, move onto next
+                            invalid = true;
+                            break;
                         }
-                        
-                        _engine->scene->getTextbox().clear();
-                        _engine->scene->getTextbox().addTextLine(dialAgainString);
-
-                        _engine->sound->loadSound(dialAgainSound);
-                        _engine->sound->playSound(dialAgainSound);
-                        callState = kBadNumber;
-                        return;
                     }
 
-                    break;
-                case kBadNumber:
-                    if (!_engine->sound->isSoundPlaying(dialAgainSound)) {
-                        _engine->sound->stopSound(dialAgainSound);
-
-                        state = kActionTrigger;
+                    if (invalid) {
+                        continue;
                     }
 
-                    break;
-                case kCall:
-                    if (!_engine->sound->isSoundPlaying(genericDialogueSound)) {
-                        _engine->sound->stopSound(genericDialogueSound);
+                    _engine->scene->getTextbox().clear();
+                    _engine->scene->getTextbox().addTextLine(calls[i].text);
 
-                        state = kActionTrigger;
-                    }
+                    genericDialogueSound.name = calls[i].soundName;
+                    _engine->sound->loadSound(genericDialogueSound);
+                    _engine->sound->playSound(genericDialogueSound);
+                    selected = i;
+                    callState = kCall;
 
-                    break;
-                case kHangUp:
-                    if (!_engine->sound->isSoundPlaying(hangUpSound)) {
-                        _engine->sound->stopSound(hangUpSound);
+                    return;
+                }
+                
+                _engine->scene->getTextbox().clear();
+                _engine->scene->getTextbox().addTextLine(dialAgainString);
+
+                _engine->sound->loadSound(dialAgainSound);
+                _engine->sound->playSound(dialAgainSound);
+                callState = kBadNumber;
+                return;
+            }
 
-                        state = kActionTrigger;
-                    }
+            break;
+        case kBadNumber:
+            if (!_engine->sound->isSoundPlaying(dialAgainSound)) {
+                _engine->sound->stopSound(dialAgainSound);
 
-                    break;
+                state = kActionTrigger;
             }
 
             break;
-        case kActionTrigger:
-            switch (callState) {
-                case kBadNumber:
-                    engine->scene->changeScene(reloadScene);
-                    calledNumber.clear();
-                    _engine->scene->setEventFlag(flagOnReload);
-                    state = kRun;
-                    callState = kWaiting;
-
-                    break;
-                case kCall: {
-                    PhoneCall &call = calls[selected];
-                    _engine->scene->changeScene(call.sceneChange);
-                    _engine->scene->setEventFlag(call.flag);
-
-                    break;
-                }
-                case kHangUp:
-                    _engine->scene->changeScene(exitScene);
-                    _engine->scene->setEventFlag(flagOnExit);
-                    
-                    break;
-                default:
-                    break;
+        case kCall:
+            if (!_engine->sound->isSoundPlaying(genericDialogueSound)) {
+                _engine->sound->stopSound(genericDialogueSound);
+
+                state = kActionTrigger;
+            }
+
+            break;
+        case kHangUp:
+            if (!_engine->sound->isSoundPlaying(hangUpSound)) {
+                _engine->sound->stopSound(hangUpSound);
 
+                state = kActionTrigger;
             }
 
-            finishExecution();
-            _engine->scene->getTextbox().clear();
+            break;
+        }
+
+        break;
+    case kActionTrigger:
+        switch (callState) {
+        case kBadNumber:
+            engine->scene->changeScene(reloadScene);
+            calledNumber.clear();
+            _engine->scene->setEventFlag(flagOnReload);
+            state = kRun;
+            callState = kWaiting;
+
+            break;
+        case kCall: {
+            PhoneCall &call = calls[selected];
+            _engine->scene->changeScene(call.sceneChange);
+            _engine->scene->setEventFlag(call.flag);
+
+            break;
+        }
+        case kHangUp:
+            _engine->scene->changeScene(exitScene);
+            _engine->scene->setEventFlag(flagOnExit);
+            
+            break;
+        default:
+            break;
+        }
+
+        finishExecution();
+        _engine->scene->getTextbox().clear();
     }
 }
 
@@ -272,6 +274,7 @@ void Telephone::handleInput(NancyInput &input) {
 
             callState = kHangUp;
         }
+        
         return;
     }
 
diff --git a/engines/nancy/cheat.cpp b/engines/nancy/cheat.cpp
index 26a1831681..100bfed29a 100644
--- a/engines/nancy/cheat.cpp
+++ b/engines/nancy/cheat.cpp
@@ -150,88 +150,88 @@ CheatDialog::CheatDialog(NancyEngine *engine) :
 
 void CheatDialog::handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) {
     switch (cmd) {
-        case GUI::kOKCmd: {
-            if (_restartScene->getState()) {
-                uint sceneID = atoi(Common::String(_scene->getEditString()).c_str());
-                IFF iff(_engine, Common::String::format("S%u", sceneID));
-                if (iff.load()) {
-                    _engine->scene->changeScene(
-                        atoi(Common::String(_scene->getEditString()).c_str()),
-                        atoi(Common::String(_frame->getEditString()).c_str()),
-                        atoi(Common::String(_offset->getEditString()).c_str()),
-                        true);
-                } else {
-                    new GUI::StaticTextWidget(this, 20, 410, 150, 20, _("Invalid Scene ID!"), Graphics::kTextAlignLeft);
-                    return;
-                }
-            }
-
-            if (_timerOn->getState()) {
-                _engine->scene->_timers.timerIsActive = true;
-                Time &timer = _engine->scene->_timers.timerTime;
-                timer = 0;
-                timer += 1000 * atoi(Common::String(_timerSeconds->getEditString()).c_str());
-                timer += 60000 * atoi(Common::String(_timerMinutes->getEditString()).c_str());
-                timer += 3600000 * atoi(Common::String(_timerHours->getEditString()).c_str());
+    case GUI::kOKCmd: {
+        if (_restartScene->getState()) {
+            uint sceneID = atoi(Common::String(_scene->getEditString()).c_str());
+            IFF iff(_engine, Common::String::format("S%u", sceneID));
+            if (iff.load()) {
+                _engine->scene->changeScene(
+                    atoi(Common::String(_scene->getEditString()).c_str()),
+                    atoi(Common::String(_frame->getEditString()).c_str()),
+                    atoi(Common::String(_offset->getEditString()).c_str()),
+                    true);
             } else {
-                _engine->scene->stopTimer();
+                new GUI::StaticTextWidget(this, 20, 410, 150, 20, _("Invalid Scene ID!"), Graphics::kTextAlignLeft);
+                return;
             }
+        }
 
-            Time &playerTime = _engine->scene->_timers.timerTime;
-            playerTime = 0;
-            playerTime += 60000 * atoi(Common::String(_playerTimeMinutes->getEditString()).c_str());
-            playerTime += 3600000 * atoi(Common::String(_playerTimeHours->getEditString()).c_str());
-            playerTime += 86400000 * atoi(Common::String(_playerTimeMinutes->getEditString()).c_str());
-
-            _engine->scene->_difficulty = atoi(Common::String(_difficulty->getEditString()).c_str());
-            _engine->scene->_hintsRemaining[0] = atoi(Common::String(_hintsRemainingEasy->getEditString()).c_str());
-            _engine->scene->_hintsRemaining[1] = atoi(Common::String(_hintsRemainingMedium->getEditString()).c_str());
-            _engine->scene->_hintsRemaining[2] = atoi(Common::String(_hintsRemainingHard->getEditString()).c_str());
-
-            for (uint i = 0; i < _inventory.size(); ++i) {
-                if (_engine->scene->hasItem(i) == kTrue && !_inventory[i]->getState()) {
-                    _engine->scene->removeItemFromInventory(i, false);
-                } else if (_engine->scene->hasItem(i) == kFalse && _inventory[i]->getState()) {
-                    _engine->scene->addItemToInventory(i);
-                }
-            }
-            cmd = GUI::kCloseCmd;
+        if (_timerOn->getState()) {
+            _engine->scene->_timers.timerIsActive = true;
+            Time &timer = _engine->scene->_timers.timerTime;
+            timer = 0;
+            timer += 1000 * atoi(Common::String(_timerSeconds->getEditString()).c_str());
+            timer += 60000 * atoi(Common::String(_timerMinutes->getEditString()).c_str());
+            timer += 3600000 * atoi(Common::String(_timerHours->getEditString()).c_str());
+        } else {
+            _engine->scene->stopTimer();
+        }
 
-            break;
+        Time &playerTime = _engine->scene->_timers.timerTime;
+        playerTime = 0;
+        playerTime += 60000 * atoi(Common::String(_playerTimeMinutes->getEditString()).c_str());
+        playerTime += 3600000 * atoi(Common::String(_playerTimeHours->getEditString()).c_str());
+        playerTime += 86400000 * atoi(Common::String(_playerTimeMinutes->getEditString()).c_str());
+
+        _engine->scene->_difficulty = atoi(Common::String(_difficulty->getEditString()).c_str());
+        _engine->scene->_hintsRemaining[0] = atoi(Common::String(_hintsRemainingEasy->getEditString()).c_str());
+        _engine->scene->_hintsRemaining[1] = atoi(Common::String(_hintsRemainingMedium->getEditString()).c_str());
+        _engine->scene->_hintsRemaining[2] = atoi(Common::String(_hintsRemainingHard->getEditString()).c_str());
+
+        for (uint i = 0; i < _inventory.size(); ++i) {
+            if (_engine->scene->hasItem(i) == kTrue && !_inventory[i]->getState()) {
+                _engine->scene->removeItemFromInventory(i, false);
+            } else if (_engine->scene->hasItem(i) == kFalse && _inventory[i]->getState()) {
+                _engine->scene->addItemToInventory(i);
+            }
         }
-        case kInputSceneNr:
-            sanitizeInput(_scene);
-            break;
-        case kInputFrameNr: 
-            sanitizeInput(_frame);
-            break;
-        case kInputScroll:
-            sanitizeInput(_offset);
-            break;
-        case kInputDifficulty:
-            sanitizeInput(_scene, 2);
-            break;
-        case kInputHintsEasy:
-            sanitizeInput(_hintsRemainingEasy);
-            break;
-        case kInputHintsMedium:
-            sanitizeInput(_hintsRemainingMedium);
-            break;
-        case kInputHintsHard:
-            sanitizeInput(_hintsRemainingHard);
-            break;
-        case kInputPlayerTime:
-            sanitizeInput(_playerTimeMinutes, 59);
-            sanitizeInput(_playerTimeHours, 23);
-            sanitizeInput(_playerTimeDays);
-            break;
-        case kInputTimer:
-            sanitizeInput(_timerSeconds, 59);
-            sanitizeInput(_timerMinutes, 59);
-            sanitizeInput(_timerHours, 23);
-            break;
-        default:
-            break;
+        cmd = GUI::kCloseCmd;
+
+        break;
+    }
+    case kInputSceneNr:
+        sanitizeInput(_scene);
+        break;
+    case kInputFrameNr: 
+        sanitizeInput(_frame);
+        break;
+    case kInputScroll:
+        sanitizeInput(_offset);
+        break;
+    case kInputDifficulty:
+        sanitizeInput(_scene, 2);
+        break;
+    case kInputHintsEasy:
+        sanitizeInput(_hintsRemainingEasy);
+        break;
+    case kInputHintsMedium:
+        sanitizeInput(_hintsRemainingMedium);
+        break;
+    case kInputHintsHard:
+        sanitizeInput(_hintsRemainingHard);
+        break;
+    case kInputPlayerTime:
+        sanitizeInput(_playerTimeMinutes, 59);
+        sanitizeInput(_playerTimeHours, 23);
+        sanitizeInput(_playerTimeDays);
+        break;
+    case kInputTimer:
+        sanitizeInput(_timerSeconds, 59);
+        sanitizeInput(_timerMinutes, 59);
+        sanitizeInput(_timerHours, 23);
+        break;
+    default:
+        break;
     }
     
     Dialog::handleCommand(sender, cmd, data);
diff --git a/engines/nancy/commontypes.cpp b/engines/nancy/commontypes.cpp
index 4bf8d0d00b..a4f2b7ca78 100644
--- a/engines/nancy/commontypes.cpp
+++ b/engines/nancy/commontypes.cpp
@@ -84,17 +84,17 @@ void SoundDescription::read(Common::SeekableReadStream &stream, Type type) {
 	// The difference between these is a couple members found at the same position
 	// whose purpose I don't understand, so for now just skip them
 	switch (type) {
-		case kNormal:
-			stream.skip(8);
-			break;
-		case kMenu:
-			stream.skip(6);	
-			break;
-		case kScene:
-			// fall through
-		case kDIGI:
-			stream.skip(4);
-			break;
+	case kNormal:
+		stream.skip(8);
+		break;
+	case kMenu:
+		stream.skip(6);	
+		break;
+	case kScene:
+		// fall through
+	case kDIGI:
+		stream.skip(4);
+		break;
 	}
 
 	numLoops = stream.readUint16LE();
diff --git a/engines/nancy/cursor.cpp b/engines/nancy/cursor.cpp
index e79aea3ef3..72b4a38bb3 100644
--- a/engines/nancy/cursor.cpp
+++ b/engines/nancy/cursor.cpp
@@ -80,27 +80,27 @@ void CursorManager::setCursor(CursorType type, int16 itemID) {
     bool hasItem = false;
 
     switch (type) {
-        case kNormalArrow:
-            newID = 4;
-            break;
-        case kHotspotArrow:
-            newID = 6;
-            break;
-        case kExitArrow:
-            newID = 3;
-            break;
-        default: {
-            if (itemID == -1) {
-                // No item held, set to eyeglass
-                itemID = 0;
-            } else {
-                // Item held
-                itemID += 3;
-                hasItem = true;
-            }
-
-            newID = itemID * 4 + type;
+    case kNormalArrow:
+        newID = 4;
+        break;
+    case kHotspotArrow:
+        newID = 6;
+        break;
+    case kExitArrow:
+        newID = 3;
+        break;
+    default: {
+        if (itemID == -1) {
+            // No item held, set to eyeglass
+            itemID = 0;
+        } else {
+            // Item held
+            itemID += 3;
+            hasItem = true;
         }
+
+        newID = itemID * 4 + type;
+    }
     }
 
     Graphics::ManagedSurface *surf;
diff --git a/engines/nancy/font.cpp b/engines/nancy/font.cpp
index da65d0dc8e..87a53ca94d 100644
--- a/engines/nancy/font.cpp
+++ b/engines/nancy/font.cpp
@@ -122,56 +122,56 @@ Common::Rect Font::getCharacterSourceRect(char chr) const {
         return ret;
     } else if (isPunct(chr)) {
         switch (chr) {
-            case '.':
-                offset = _periodOffset;
-                break;
-            case ',':
-                offset = _commaOffset;
-                break;
-            case '=':
-                offset = _equalitySignOffset;
-                break;
-            case ':':
-                offset = _colonOffset;
-                break;
-            case '-':
-                offset = _dashOffset;
-                break;
-            case '?':
-                offset = _questionMarkOffset;
-                break;
-            case '!':
-                offset = _exclamationMarkOffset;
-                break;
-            case '%':
-                offset = _percentOffset;
-                break;
-            case '&':
-                offset = _ampersandOffset;
-                break;
-            case '*':
-                offset = _asteriskOffset;
-                break;
-            case '(':
-                offset = _leftBracketOffset;
-                break;
-            case ')':
-                offset = _rightBracketOffset;
-                break;
-            case '+':
-                offset = _plusOffset;
-                break;
-            case '\'':
-                offset = _apostropheOffset;
-                break;
-            case ';':
-                offset = _semicolonOffset;
-                break;
-            case '/':
-                offset = _slashOffset;
-                break;
-            default:
-                error("Unsupported FONT character: %c", chr);
+        case '.':
+            offset = _periodOffset;
+            break;
+        case ',':
+            offset = _commaOffset;
+            break;
+        case '=':
+            offset = _equalitySignOffset;
+            break;
+        case ':':
+            offset = _colonOffset;
+            break;
+        case '-':
+            offset = _dashOffset;
+            break;
+        case '?':
+            offset = _questionMarkOffset;
+            break;
+        case '!':
+            offset = _exclamationMarkOffset;
+            break;
+        case '%':
+            offset = _percentOffset;
+            break;
+        case '&':
+            offset = _ampersandOffset;
+            break;
+        case '*':
+            offset = _asteriskOffset;
+            break;
+        case '(':
+            offset = _leftBracketOffset;
+            break;
+        case ')':
+            offset = _rightBracketOffset;
+            break;
+        case '+':
+            offset = _plusOffset;
+            break;
+        case '\'':
+            offset = _apostropheOffset;
+            break;
+        case ';':
+            offset = _semicolonOffset;
+            break;
+        case '/':
+            offset = _slashOffset;
+            break;
+        default:
+            error("Unsupported FONT character: %c", chr);
         }
     }
     ret = _symbolRects[offset];
diff --git a/engines/nancy/input.cpp b/engines/nancy/input.cpp
index 4922a20127..e0fd3e36d4 100644
--- a/engines/nancy/input.cpp
+++ b/engines/nancy/input.cpp
@@ -39,86 +39,86 @@ void InputManager::processEvents() {
 
     while (_engine->getEventManager()->pollEvent(event)) {
         switch (event.type) {
-            case EVENT_KEYDOWN:
-                if (event.kbd.keycode == KEYCODE_d && event.kbd.flags & Common::KBD_CTRL) {
-                    // Launch debug console
-                    _engine->launchConsole = true;
-                } else if (event.kbd.keycode == KEYCODE_q && event.kbd.flags & Common::KBD_CTRL) {
-                    // Quit
-                    _engine->quitGame();
-                } else {
-                    // Push all other keyboard events into an array and let getInput() callers handle them
-                    _otherKbdInput.push_back(event.kbd);
-                }
-                break;
-            case EVENT_CUSTOM_ENGINE_ACTION_START:
-                switch (event.customType) {
-                    case kNancyActionLeftClick:
-                        _inputs |= NancyInput::kLeftMouseButtonDown;
-                        _inputs |= NancyInput::kLeftMouseButtonHeld;
-                        break;
-                    case kNancyActionRightClick:
-                        _inputs |= NancyInput::kRightMouseButtonDown;
-                        _inputs |= NancyInput::kRightMouseButtonHeld;
-                        break;
-                    case kNancyActionMoveUp:
-                        _inputs |= NancyInput::kMoveUp;
-                        break;
-                    case kNancyActionMoveDown:
-                        _inputs |= NancyInput::kMoveDown;
-                        break;
-                    case kNancyActionMoveLeft:
-                        _inputs |= NancyInput::kMoveLeft;
-                        break;
-                    case kNancyActionMoveRight:
-                        _inputs |= NancyInput::kMoveRight;
-                        break;
-                    case kNancyActionMoveFast:
-                        _inputs |= NancyInput::kMoveFastModifier;
-                        break;
-                    case kNancyActionRequestCheatMenu:
-                        _engine->callCheatMenu(false);
-                        break;
-                    case kNancyActionRequestEventMenu:
-                        _engine->callCheatMenu(true);
-                        break;
-                    default:
-                        break;
-                }
-
-                break;
-            case EVENT_CUSTOM_ENGINE_ACTION_END:
-                switch (event.customType) {
-                    case kNancyActionLeftClick:
-                        _inputs |= NancyInput::kLeftMouseButtonUp;
-                        _inputs &= ~NancyInput::kLeftMouseButtonHeld;
-                        break;
-                    case kNancyActionRightClick:
-                        _inputs |= NancyInput::kRightMouseButtonUp;
-                        _inputs &= ~NancyInput::kRightMouseButtonHeld;
-                        break;
-                    case kNancyActionMoveUp:
-                        _inputs &= ~NancyInput::kMoveUp;
-                        break;
-                    case kNancyActionMoveDown:
-                        _inputs &= ~NancyInput::kMoveDown;
-                        break;
-                    case kNancyActionMoveLeft:
-                        _inputs &= ~NancyInput::kMoveLeft;
-                        break;
-                    case kNancyActionMoveRight:
-                        _inputs &= ~NancyInput::kMoveRight;
-                        break;
-                    case kNancyActionMoveFast:
-                        _inputs &= ~NancyInput::kMoveFastModifier;
-                        break;
-                    default:
-                        break;
-                }
-
+        case EVENT_KEYDOWN:
+            if (event.kbd.keycode == KEYCODE_d && event.kbd.flags & Common::KBD_CTRL) {
+                // Launch debug console
+                _engine->launchConsole = true;
+            } else if (event.kbd.keycode == KEYCODE_q && event.kbd.flags & Common::KBD_CTRL) {
+                // Quit
+                _engine->quitGame();
+            } else {
+                // Push all other keyboard events into an array and let getInput() callers handle them
+                _otherKbdInput.push_back(event.kbd);
+            }
+            break;
+        case EVENT_CUSTOM_ENGINE_ACTION_START:
+            switch (event.customType) {
+            case kNancyActionLeftClick:
+                _inputs |= NancyInput::kLeftMouseButtonDown;
+                _inputs |= NancyInput::kLeftMouseButtonHeld;
+                break;
+            case kNancyActionRightClick:
+                _inputs |= NancyInput::kRightMouseButtonDown;
+                _inputs |= NancyInput::kRightMouseButtonHeld;
+                break;
+            case kNancyActionMoveUp:
+                _inputs |= NancyInput::kMoveUp;
+                break;
+            case kNancyActionMoveDown:
+                _inputs |= NancyInput::kMoveDown;
+                break;
+            case kNancyActionMoveLeft:
+                _inputs |= NancyInput::kMoveLeft;
+                break;
+            case kNancyActionMoveRight:
+                _inputs |= NancyInput::kMoveRight;
+                break;
+            case kNancyActionMoveFast:
+                _inputs |= NancyInput::kMoveFastModifier;
+                break;
+            case kNancyActionRequestCheatMenu:
+                _engine->callCheatMenu(false);
+                break;
+            case kNancyActionRequestEventMenu:
+                _engine->callCheatMenu(true);
                 break;
             default:
                 break;
+            }
+
+            break;
+        case EVENT_CUSTOM_ENGINE_ACTION_END:
+            switch (event.customType) {
+            case kNancyActionLeftClick:
+                _inputs |= NancyInput::kLeftMouseButtonUp;
+                _inputs &= ~NancyInput::kLeftMouseButtonHeld;
+                break;
+            case kNancyActionRightClick:
+                _inputs |= NancyInput::kRightMouseButtonUp;
+                _inputs &= ~NancyInput::kRightMouseButtonHeld;
+                break;
+            case kNancyActionMoveUp:
+                _inputs &= ~NancyInput::kMoveUp;
+                break;
+            case kNancyActionMoveDown:
+                _inputs &= ~NancyInput::kMoveDown;
+                break;
+            case kNancyActionMoveLeft:
+                _inputs &= ~NancyInput::kMoveLeft;
+                break;
+            case kNancyActionMoveRight:
+                _inputs &= ~NancyInput::kMoveRight;
+                break;
+            case kNancyActionMoveFast:
+                _inputs &= ~NancyInput::kMoveFastModifier;
+                break;
+            default:
+                break;
+            }
+
+            break;
+        default:
+            break;
         }
     }
 }
diff --git a/engines/nancy/nancy.cpp b/engines/nancy/nancy.cpp
index 869748a25c..2360645492 100644
--- a/engines/nancy/nancy.cpp
+++ b/engines/nancy/nancy.cpp
@@ -57,12 +57,7 @@ namespace Nancy {
 
 NancyEngine *NancyEngine::s_Engine = nullptr;
 
-NancyEngine::NancyEngine(OSystem *syst, const NancyGameDescription *gd) :
-	Engine(syst),
-	_gameDescription(gd)
-{
-	_system = syst;
-
+NancyEngine::NancyEngine(OSystem *syst, const NancyGameDescription *gd) : Engine(syst), _gameDescription(gd), _system(syst) {
 	DebugMan.addDebugChannel(kDebugEngine, "Engine", "Engine debug level");
 	DebugMan.addDebugChannel(kDebugActionRecord, "ActionRecord", "Action Record debug level");
 	DebugMan.addDebugChannel(kDebugScene, "Scene", "Scene debug level");
@@ -151,49 +146,49 @@ Common::Error NancyEngine::run() {
 		input->processEvents();
 		
 		switch (_gameFlow.minGameState) {
-			case kBoot:
-				bootGameEngine();
-				graphicsManager->init();
-				cursorManager->init();
-				setGameState(kLogo);
-				break;
-			case kLogo:
-				logo->process();
-				break;
-			case kMainMenu: {
-				GameState prevState = getPreviousGameState();
-				// TODO until the game's own menus are implemented we simply open the GMM
-				openMainMenuDialog();
-				setGameState(prevState);
-				break;
-			}
-			case kHelp:
-				help->process();
-				break;
-			case kScene:
-				scene->process();
-				break;
-			case kMap:
-				map->process();
-				break;
-			case kCheat: {
-				if (_cheatTypeIsEventFlag) {
-					EventFlagDialog *dialog = new EventFlagDialog(this);
-					dialog->runModal();
-					delete dialog;
-				} else {
-					CheatDialog *dialog = new CheatDialog(this);
-					dialog->runModal();
-					delete dialog;
-				}
-				setGameState(getPreviousGameState());
-				input->forceCleanInput();
-				break;
+		case kBoot:
+			bootGameEngine();
+			graphicsManager->init();
+			cursorManager->init();
+			setGameState(kLogo);
+			break;
+		case kLogo:
+			logo->process();
+			break;
+		case kMainMenu: {
+			GameState prevState = getPreviousGameState();
+			// TODO until the game's own menus are implemented we simply open the GMM
+			openMainMenuDialog();
+			setGameState(prevState);
+			break;
+		}
+		case kHelp:
+			help->process();
+			break;
+		case kScene:
+			scene->process();
+			break;
+		case kMap:
+			map->process();
+			break;
+		case kCheat: {
+			if (_cheatTypeIsEventFlag) {
+				EventFlagDialog *dialog = new EventFlagDialog(this);
+				dialog->runModal();
+				delete dialog;
+			} else {
+				CheatDialog *dialog = new CheatDialog(this);
+				dialog->runModal();
+				delete dialog;
 			}
-			case kIdle:
-				break;
-			default:
-				break;
+			setGameState(getPreviousGameState());
+			input->forceCleanInput();
+			break;
+		}
+		case kIdle:
+			break;
+		default:
+			break;
 		}
 
 		graphicsManager->draw();
diff --git a/engines/nancy/state/help.cpp b/engines/nancy/state/help.cpp
index 616ca99626..402c0ac479 100644
--- a/engines/nancy/state/help.cpp
+++ b/engines/nancy/state/help.cpp
@@ -35,18 +35,18 @@ namespace State {
 
 void Help::process() {
     switch (_state) {
-        case kInit:
-            init();
-            // fall through
-        case kBegin:       
-            begin();
-            // fall through
-        case kRun:
-            run();
-            break;
-        case kWaitForSound:
-            waitForSound();
-            break;
+    case kInit:
+        init();
+        // fall through
+    case kBegin:       
+        begin();
+        // fall through
+    case kRun:
+        run();
+        break;
+    case kWaitForSound:
+        waitForSound();
+        break;
     }
 }
 
@@ -87,7 +87,7 @@ void Help::begin() {
 void Help::run() {
     NancyInput input = _engine->input->getInput();
 
-    if (_hotspot.contains(input.mousePos) && input.input & NancyInput::kLeftMouseButtonUp)  {
+    if (_hotspot.contains(input.mousePos) && input.input & NancyInput::kLeftMouseButtonUp) {
         _engine->sound->playSound(0x18); // Hardcoded by original engine
         _state = kWaitForSound;
     }
diff --git a/engines/nancy/state/map.cpp b/engines/nancy/state/map.cpp
index b9fbdc4d90..6d5f5194f4 100644
--- a/engines/nancy/state/map.cpp
+++ b/engines/nancy/state/map.cpp
@@ -40,15 +40,15 @@ namespace State {
 
 void Map::process() {
     switch (_state) {
-        case kInit:
-            init();
-            // fall through
-        case kRun:
-            run();
-            break;
-        case kStop:
-            stop();
-            break;
+    case kInit:
+        init();
+        // fall through
+    case kRun:
+        run();
+        break;
+    case kStop:
+        stop();
+        break;
     }
 }
 
@@ -147,6 +147,7 @@ void Map::run() {
                 _pickedLocationID = i;
                 _state = kStop;
             }
+
             return;
         }
     }
@@ -168,6 +169,7 @@ void Map::stop() {
         
         _engine->sound->playSound(0x18);
     }
+    
     // The two sounds play at the same time if a location was picked
     _engine->sound->playSound(0x14);
 
diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index 7658f50ec4..4ac71398cb 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -64,7 +64,6 @@ void Scene::process() {
     case kRun:
         run();
         break;
-    
     }
 }
 
@@ -167,9 +166,11 @@ void Scene::init() {
 
     Common::SeekableReadStream *chunk = _engine->getBootChunkStream("HINT");
     chunk->seek(0);
+
     for (uint i = 0; i < 3; ++i) {
         _hintsRemaining.push_back(chunk->readByte());
     }
+
     _lastHint = -1;
 
     chunk = _engine->getBootChunkStream("MAP");
@@ -204,11 +205,13 @@ void Scene::load() {
     // Scene IDs are prefixed with S inside the cif tree; e.g 100 -> S100                                                                                    
     Common::String sceneName = Common::String::format("S%u", _sceneState.nextScene.sceneID);
     IFF sceneIFF(_engine, sceneName);
+
 	if (!sceneIFF.load()) {
 		error("Faled to load IFF %s", sceneName.c_str());
 	}
 
     Common::SeekableReadStream *sceneSummaryChunk = sceneIFF.getChunkStream("SSUM");
+
     if (!sceneSummaryChunk) {
         error("Invalid IFF Chunk SSUM");
     }
@@ -225,8 +228,7 @@ void Scene::load() {
     // Search for Action Records, maximum for a scene is 30
     Common::SeekableReadStream *actionRecordChunk = nullptr;
 
-    while (actionRecordChunk = sceneIFF.getChunkStream("ACT", _actionManager._records.size()), actionRecordChunk != nullptr)
-    {
+    while (actionRecordChunk = sceneIFF.getChunkStream("ACT", _actionManager._records.size()), actionRecordChunk != nullptr) {
         if (_actionManager._records.size() >= 30) {
             error("Invalid number of Action Records");
         }
@@ -277,6 +279,7 @@ void Scene::run() {
     if (isComingFromMenu) {
         // TODO
     }
+
     isComingFromMenu = false;
 
     Time playTime = _engine->getTotalPlayTime();
@@ -414,9 +417,11 @@ void Scene::clearSceneData() {
     for (uint i = 44; i < 54; ++i) {
         _flags.eventFlags[i] = kFalse;
     }
+
     for (uint i = 63; i < 74; ++i) {
         _flags.eventFlags[i] = kFalse;
     }
+    
     for (uint i = 75; i < 85; ++i) {
         _flags.eventFlags[i] = kFalse;
     }
diff --git a/engines/nancy/time.h b/engines/nancy/time.h
index a316976aaa..6fea33d5e0 100644
--- a/engines/nancy/time.h
+++ b/engines/nancy/time.h
@@ -68,12 +68,12 @@ public:
     friend bool operator>= (const Time &l, const uint32 &r) { return !(l < r); }
     friend bool operator>= (const uint32 &l, const Time &r) { return !(l < r); }
 
-    uint16 getSeconds()     { return (_milliseconds / 1000) % 60; }
-    uint16 getMinutes()     { return (_milliseconds / 60000) % 60; }
-    uint16 getTotalHours()       { return _milliseconds / 3600000; }
+    uint16 getSeconds()         { return (_milliseconds / 1000) % 60; }
+    uint16 getMinutes()         { return (_milliseconds / 60000) % 60; }
+    uint16 getTotalHours()      { return _milliseconds / 3600000; }
     
-    uint16 getHours()   { return (_milliseconds / 3600000) % 24; } // Used for player time
-    uint16 getDays()        { return _milliseconds / 86400000; } // up to 49.7 days
+    uint16 getHours()           { return (_milliseconds / 3600000) % 24; } // Used for player time
+    uint16 getDays()            { return _milliseconds / 86400000; } // up to 49.7 days
 
 private:
     uint32 _milliseconds;
diff --git a/engines/nancy/ui/textbox.cpp b/engines/nancy/ui/textbox.cpp
index 787cb0e51a..e47315628e 100644
--- a/engines/nancy/ui/textbox.cpp
+++ b/engines/nancy/ui/textbox.cpp
@@ -165,8 +165,7 @@ void Textbox::drawTextbox() {
 
         // Subdivide current line into sublines for proper handling of the tab and color tokens
         // Assumes the tab token is on a new line
-        while (!currentLine.empty())
-        {
+        while (!currentLine.empty()) {
             if (currentLine.hasPrefix(tabToken)) {
                 horizontalOffset += font->getStringWidth("    "); // Replace tab with 4 spaces
                 currentLine = currentLine.substr(ARRAYSIZE(tabToken) - 1);
diff --git a/engines/nancy/ui/viewport.h b/engines/nancy/ui/viewport.h
index c35026c9f3..8fdbccd637 100644
--- a/engines/nancy/ui/viewport.h
+++ b/engines/nancy/ui/viewport.h
@@ -43,7 +43,7 @@ public:
     Viewport(NancyEngine *engine) :
         RenderObject(engine),
         _movementLastFrame(0),
-        _edgesMask(0)  {}
+        _edgesMask(0) {}
     virtual ~Viewport() { _decoder.close(); _fullFrame.free(); }
 
     virtual void init() override;


Commit: e516df94dfc874d050347a33ba8e0bcbabab53cc
    https://github.com/scummvm/scummvm/commit/e516df94dfc874d050347a33ba8e0bcbabab53cc
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Move includes inside include guards

Moved the state/help.h includes inside the include guards.

Changed paths:
    engines/nancy/state/help.h


diff --git a/engines/nancy/state/help.h b/engines/nancy/state/help.h
index 92e8c11490..57d1e5209b 100644
--- a/engines/nancy/state/help.h
+++ b/engines/nancy/state/help.h
@@ -20,15 +20,15 @@
  *
  */
 
+#ifndef NANCY_STATE_HELP_H
+#define NANCY_STATE_HELP_H
+
 #include "engines/nancy/ui/fullscreenimage.h"
 
 #include "engines/nancy/commontypes.h"
 
 #include "common/rect.h"
 
-#ifndef NANCY_STATE_HELP_H
-#define NANCY_STATE_HELP_H
-
 namespace Nancy {
 
 class NancyEngine;


Commit: 55fe1a5db42bc6ea65566791299805cac19b0b44
    https://github.com/scummvm/scummvm/commit/55fe1a5db42bc6ea65566791299805cac19b0b44
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Implement credits and WinGame action record

Implemented the WinGame action record, which allows the player to reach the credits after winning, as well as the Credits state itself. Also made some minor changes to Scene and Nancy to accommodate for the addition.

Changed paths:
  A engines/nancy/state/credits.cpp
  A engines/nancy/state/credits.h
    engines/nancy/action/recordtypes.cpp
    engines/nancy/action/recordtypes.h
    engines/nancy/module.mk
    engines/nancy/nancy.cpp
    engines/nancy/nancy.h
    engines/nancy/state/scene.cpp
    engines/nancy/state/scene.h


diff --git a/engines/nancy/action/recordtypes.cpp b/engines/nancy/action/recordtypes.cpp
index 9cc13cd0d6..3aee87609d 100644
--- a/engines/nancy/action/recordtypes.cpp
+++ b/engines/nancy/action/recordtypes.cpp
@@ -327,10 +327,17 @@ uint16 PopScene::readData(Common::SeekableReadStream &stream) {
 }
 
 uint16 WinGame::readData(Common::SeekableReadStream &stream) {
-    winData = stream.readByte();
+    stream.skip(1);
     return 1;
 }
 
+void WinGame::execute(NancyEngine *engine) {
+    engine->stopAndUnloadSpecificSounds();
+    engine->setGameState(NancyEngine::kCredits, NancyEngine::kMainMenu);
+    engine->scene->resetStateToInit();
+    isDone = true;
+}
+
 uint16 AddInventoryNoHS::readData(Common::SeekableReadStream &stream) {
     itemID = stream.readUint16LE();
     return 2;
diff --git a/engines/nancy/action/recordtypes.h b/engines/nancy/action/recordtypes.h
index bfa1a34049..5bce14b0da 100644
--- a/engines/nancy/action/recordtypes.h
+++ b/engines/nancy/action/recordtypes.h
@@ -302,8 +302,7 @@ protected:
 class WinGame : public ActionRecord {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
-
-    byte winData = 0;
+    virtual void execute(Nancy::NancyEngine *engine) override;
     
 protected:
     virtual Common::String getRecordTypeName() const override { return "WinGame"; }
diff --git a/engines/nancy/module.mk b/engines/nancy/module.mk
index f8ea51ac89..0cc1fdc652 100644
--- a/engines/nancy/module.mk
+++ b/engines/nancy/module.mk
@@ -20,6 +20,7 @@ MODULE_OBJS = \
   ui/scrollbar.o \
   ui/textbox.o \
   ui/viewport.o \
+  state/credits.o \
   state/logo.o \
   state/help.o \
   state/map.o \
diff --git a/engines/nancy/nancy.cpp b/engines/nancy/nancy.cpp
index 2360645492..b1f37efb45 100644
--- a/engines/nancy/nancy.cpp
+++ b/engines/nancy/nancy.cpp
@@ -24,6 +24,7 @@
 #include "engines/nancy/state/scene.h"
 #include "engines/nancy/state/help.h"
 #include "engines/nancy/state/map.h"
+#include "engines/nancy/state/credits.h"
 
 #include "engines/nancy/nancy.h"
 #include "engines/nancy/resource.h"
@@ -69,6 +70,7 @@ NancyEngine::NancyEngine(OSystem *syst, const NancyGameDescription *gd) : Engine
 	logo = new State::Logo(this);
 	scene = new State::Scene(this);
 	map = new State::Map(this);
+	credits = new State::Credits(this);
 	help = new State::Help(this);
 	input = new InputManager(this);
 	sound = new SoundManager(this);
@@ -155,6 +157,9 @@ Common::Error NancyEngine::run() {
 		case kLogo:
 			logo->process();
 			break;
+		case kCredits:
+			credits->process();
+			break;
 		case kMainMenu: {
 			GameState prevState = getPreviousGameState();
 			// TODO until the game's own menus are implemented we simply open the GMM
@@ -185,7 +190,7 @@ Common::Error NancyEngine::run() {
 			input->forceCleanInput();
 			break;
 		}
-		case kIdle:
+		case kNone:
 			break;
 		default:
 			break;
@@ -281,10 +286,10 @@ void NancyEngine::pauseEngineIntern(bool pause) {
 			scene->requestStateChange(kPause);
 			scene->changeGameState(true);
 		} else {
-			setGameState(kPause, true);
+			setGameState(kPause, kNone, true);
 		}
 	} else {
-		setGameState(getPreviousGameState(), true);
+		setGameState(getPreviousGameState(), kNone, true);
 	}
 
 	graphicsManager->onPause(pause);
@@ -373,8 +378,13 @@ void NancyEngine::readImageList(const IFF &boot, const Common::String &prefix, I
 	}
 }
 
-void NancyEngine::setGameState(GameState state, bool keepGraphics) {
-	_gameFlow.previousGameState = _gameFlow.minGameState;
+void NancyEngine::setGameState(GameState state, GameState overridePrevious, bool keepGraphics) {
+	if (overridePrevious != kNone) {
+		_gameFlow.previousGameState = overridePrevious;
+	} else {
+		_gameFlow.previousGameState = _gameFlow.minGameState;
+	}
+	
 	_gameFlow.minGameState = state;
 	_gameFlow.justChanged = true;
 
diff --git a/engines/nancy/nancy.h b/engines/nancy/nancy.h
index 5b3263a91c..ccad3bbc58 100644
--- a/engines/nancy/nancy.h
+++ b/engines/nancy/nancy.h
@@ -69,6 +69,7 @@ class Logo;
 class Scene;
 class Map;
 class Help;
+class Credits;
 }
 
 class NancyEngine : public Engine {
@@ -92,7 +93,7 @@ public:
 		kCheat,
 		kQuit,
 		// regain focus
-		kIdle,
+		kNone,
 		kPause, // only used when the GMM is on screen
 		kReloadSave
 	};
@@ -124,7 +125,7 @@ public:
 	// Used for state switching
 	void stopAndUnloadSpecificSounds();
 	
-	void setGameState(GameState state, bool keepGraphics = false);
+	void setGameState(GameState state, GameState overridePrevious = kNone, bool keepGraphics = false);
 	GameState getGameState() const { return _gameFlow.minGameState; }
 	GameState getPreviousGameState() const { return _gameFlow.previousGameState; }
 	void callCheatMenu(bool eventFlags) { setGameState(kCheat), _cheatTypeIsEventFlag = eventFlags; }
@@ -145,6 +146,7 @@ public:
 	State::Scene *scene;
 	State::Map *map;
 	State::Help *help;
+	State::Credits *credits;
 	
 	OSystem *_system;
 	Common::RandomSource *_rnd;
diff --git a/engines/nancy/state/credits.cpp b/engines/nancy/state/credits.cpp
new file mode 100644
index 0000000000..54379b08a9
--- /dev/null
+++ b/engines/nancy/state/credits.cpp
@@ -0,0 +1,123 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/nancy/state/credits.h"
+
+#include "engines/nancy/nancy.h"
+#include "engines/nancy/resource.h"
+#include "engines/nancy/util.h"
+#include "engines/nancy/graphics.h"
+#include "engines/nancy/sound.h"
+#include "engines/nancy/input.h"
+#include "engines/nancy/cursor.h"
+
+#include "common/stream.h"
+
+namespace Nancy {
+namespace State {
+
+void Credits::process() {
+    switch (_state) {
+    case kInit:
+        init();
+        // fall through
+    case kRun:
+        run();
+        break;
+    }
+}
+
+void Credits::init() {
+    Common::SeekableReadStream *cred = _engine->getBootChunkStream("CRED");
+    cred->seek(0);
+
+    char buf[10];
+    cred->read(buf, 10);
+    _background.init(buf);
+
+    cred->read(buf, 10);
+    cred->skip(0x20); // Skip the src and dest rectangles
+    readRect(*cred, _text._screenPosition);
+    cred->skip(0x10);
+    _updateTime = cred->readUint16LE();
+    _pixelsToScroll = cred->readUint16LE();
+    _sound.read(*cred, SoundDescription::kMenu);
+
+    Graphics::Surface surf;
+    _engine->_res->loadImage("ciftree", buf, surf);
+    _fullTextSurface.create(surf.w, surf.h + _text._screenPosition.height() * 2, GraphicsManager::pixelFormat);
+    _fullTextSurface.clear(GraphicsManager::transColor);
+    _fullTextSurface.blitFrom(surf, Common::Point(0, _text._screenPosition.height()));
+    surf.free();
+    
+    Common::Rect src = _text._screenPosition;
+    src.moveTo(Common::Point());
+    _text._drawSurface.create(_fullTextSurface, src);
+    _text.init();
+
+    _engine->sound->loadSound(_sound);
+    _engine->sound->playSound(_sound);
+
+    _returnToState = _engine->getPreviousGameState();
+
+    _background.registerGraphics();
+    _text.registerGraphics();
+
+    _engine->cursorManager->showCursor(false);
+
+    _state = kRun;
+}
+
+void Credits::run() {
+    NancyInput input = _engine->input->getInput();
+
+    if (input.input & NancyInput::kLeftMouseButtonDown) {
+        _state = kInit;
+        _engine->sound->stopSound(_sound);
+        if (_returnToState == NancyEngine::kMainMenu) {
+            _engine->setGameState((NancyEngine::GameState)_returnToState, NancyEngine::kScene);
+        } else {
+            _engine->setGameState((NancyEngine::GameState)_returnToState);
+        }
+        _engine->cursorManager->showCursor(true);
+        _fullTextSurface.free();
+    }
+
+    Time currentTime = _engine->getTotalPlayTime();
+    if (currentTime >= _nextUpdateTime) {
+        _nextUpdateTime = currentTime + _updateTime;
+
+        Common::Rect newSrc = _text._screenPosition;
+        newSrc.moveTo(_text._drawSurface.getOffsetFromOwner());
+        newSrc.translate(0, _pixelsToScroll);
+
+        if (newSrc.bottom > _fullTextSurface.h) {
+            newSrc.moveTo(Common::Point());
+        }
+
+        _text._drawSurface.create(_fullTextSurface, newSrc);
+        _text._needsRedraw = true;
+    }
+}
+
+} // End of namespace State
+} // End of namespace Nancy
diff --git a/engines/nancy/state/credits.h b/engines/nancy/state/credits.h
new file mode 100644
index 0000000000..bd8154db5b
--- /dev/null
+++ b/engines/nancy/state/credits.h
@@ -0,0 +1,79 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef NANCY_STATE_CREDITS_H
+#define NANCY_STATE_CREDITS_H
+
+#include "engines/nancy/ui/fullscreenimage.h"
+
+#include "engines/nancy/time.h"
+#include "engines/nancy/commontypes.h"
+
+#include "common/rect.h"
+
+#include "graphics/managed_surface.h"
+
+namespace Nancy {
+
+class NancyEngine;
+
+namespace State {
+
+class Credits {
+public:
+    enum State { kInit, kRun };
+    Credits(NancyEngine *engine) : _engine(engine), _state(kInit), _background(_engine), _text(_background) {}
+
+    void process();
+
+protected:
+    void init();
+    void run();
+
+    class CreditsText : public RenderObject {
+        friend class Credits;
+    public:
+        CreditsText(RenderObject &redrawFrom) : RenderObject(redrawFrom) {}
+        virtual ~CreditsText() =default;
+
+    protected:
+        virtual uint16 getZOrder() const override { return 1; }
+        virtual BlitType getBlitType() const override { return kTrans; }
+    };
+
+    NancyEngine *_engine;
+    State _state;
+    UI::FullScreenImage _background;
+    CreditsText _text;
+    Time _nextUpdateTime;
+    Graphics::ManagedSurface _fullTextSurface;
+    uint _returnToState;
+
+    Time _updateTime; // 0x54
+    uint16 _pixelsToScroll; // 0x56
+    SoundDescription _sound; // 0x58, kMenu?
+};
+
+} // End of namespace State
+} // End of namespace Nancy
+
+#endif // NANCY_STATE_CREDITS_H
diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index 4ac71398cb..b855e48c1a 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -402,7 +402,7 @@ void Scene::readSceneSummary(Common::SeekableReadStream &stream) {
 bool Scene::changeGameState(bool keepGraphics) {
     if (_gameStateRequested != NancyEngine::kScene) {
         _timers.pushedPlayTime = _engine->getTotalPlayTime();
-        _engine->setGameState(_gameStateRequested, keepGraphics);
+        _engine->setGameState(_gameStateRequested, NancyEngine::kNone, keepGraphics);
         _gameStateRequested = NancyEngine::kScene;
         pauseSceneSpecificSounds();
 
diff --git a/engines/nancy/state/scene.h b/engines/nancy/state/scene.h
index 675a2b0580..18b30bee1c 100644
--- a/engines/nancy/state/scene.h
+++ b/engines/nancy/state/scene.h
@@ -129,6 +129,7 @@ public:
     void useHint(int hintID, int hintWeight);
 
     void requestStateChange(NancyEngine::GameState state) { _gameStateRequested = state; }
+    void resetStateToInit() { _state = kInit; }
 
     void resetAndStartTimer() { _timers.timerIsActive = true; _timers.timerTime = 0; }
     void stopTimer() { _timers.timerIsActive = false; _timers.timerTime = 0; }


Commit: 4656c45549f7b759e573efb43ab6b217dc6baa67
    https://github.com/scummvm/scummvm/commit/4656c45549f7b759e573efb43ab6b217dc6baa67
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Implement LoseGame action record

Implemented the LoseGame action record, which ends the game and pushes the player back to the main menu.

Changed paths:
    engines/nancy/action/recordtypes.cpp
    engines/nancy/action/recordtypes.h


diff --git a/engines/nancy/action/recordtypes.cpp b/engines/nancy/action/recordtypes.cpp
index 3aee87609d..b0732d4b83 100644
--- a/engines/nancy/action/recordtypes.cpp
+++ b/engines/nancy/action/recordtypes.cpp
@@ -312,10 +312,17 @@ void EventFlagsMultiHS::execute(NancyEngine *engine) {
 }
 
 uint16 LoseGame::readData(Common::SeekableReadStream &stream) {
-    loseData = stream.readByte();
+    stream.skip(1);
     return 1;
 }
 
+void LoseGame::execute(NancyEngine *engine) {
+    engine->stopAndUnloadSpecificSounds();
+    engine->setGameState(NancyEngine::kMainMenu);
+    engine->scene->resetStateToInit();
+    isDone = true;
+}
+
 uint16 PushScene::readData(Common::SeekableReadStream &stream) {
     pushData = stream.readByte();
     return 1;
diff --git a/engines/nancy/action/recordtypes.h b/engines/nancy/action/recordtypes.h
index 5bce14b0da..d85eaa1e9a 100644
--- a/engines/nancy/action/recordtypes.h
+++ b/engines/nancy/action/recordtypes.h
@@ -272,8 +272,7 @@ protected:
 class LoseGame : public ActionRecord {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
-
-    byte loseData = 0;
+    virtual void execute(Nancy::NancyEngine *engine) override;
     
 protected:
     virtual Common::String getRecordTypeName() const override { return "LoseGame"; }


Commit: 7c8e0aea3dd0af7946334f0fdd103e7aab853dec
    https://github.com/scummvm/scummvm/commit/7c8e0aea3dd0af7946334f0fdd103e7aab853dec
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Properly clean up when starting new game

The engine now properly cleans up the inventory items and hints so they don't carry over when the player restarts the game (e.g on win/lose).

Changed paths:
    engines/nancy/state/scene.cpp
    engines/nancy/ui/inventorybox.cpp


diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index b855e48c1a..7b078339d1 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -167,6 +167,8 @@ void Scene::init() {
     Common::SeekableReadStream *chunk = _engine->getBootChunkStream("HINT");
     chunk->seek(0);
 
+    _hintsRemaining.clear();
+
     for (uint i = 0; i < 3; ++i) {
         _hintsRemaining.push_back(chunk->readByte());
     }
diff --git a/engines/nancy/ui/inventorybox.cpp b/engines/nancy/ui/inventorybox.cpp
index 6892151c55..831eee29db 100644
--- a/engines/nancy/ui/inventorybox.cpp
+++ b/engines/nancy/ui/inventorybox.cpp
@@ -39,14 +39,18 @@ void InventoryBox::init() {
     Common::SeekableReadStream &stream = *_engine->getBootChunkStream("INV");
     stream.seek(0, SEEK_SET);
 
+    _order.clear();
+
     readRect(stream, _sliderSource);
     _sliderDefaultDest.x = stream.readUint16LE();
     _sliderDefaultDest.y = stream.readUint16LE();
 
     stream.seek(0xD6, SEEK_SET);
+
     for (uint i = 0; i < 14; ++i) {
         readRect(stream, _shadesSrc[i]);
     }
+
     readRect(stream, _screenPosition);
     _shadesFrameTime = stream.readUint16LE();
 
@@ -60,6 +64,7 @@ void InventoryBox::init() {
     readRect(stream, _emptySpace);
 
     char itemName[0x14];
+
     for (uint i = 0; i < 11; ++i) {
         stream.read(itemName, 0x14);
         _itemDescriptions[i].name = Common::String(itemName);


Commit: 5c1843f871688c387c59d97c42104b6573096fd2
    https://github.com/scummvm/scummvm/commit/5c1843f871688c387c59d97c42104b6573096fd2
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Rename state functions in nancy.h

Renamed all functions relating to the engine's internal state so they only mention the word State instead of GameState. This is to avoid confusion between them and functions relating to savegames, whose names already include GameState.

Changed paths:
    engines/nancy/action/recordtypes.cpp
    engines/nancy/console.cpp
    engines/nancy/nancy.cpp
    engines/nancy/nancy.h
    engines/nancy/state/credits.cpp
    engines/nancy/state/help.cpp
    engines/nancy/state/logo.cpp
    engines/nancy/state/map.cpp
    engines/nancy/state/scene.cpp


diff --git a/engines/nancy/action/recordtypes.cpp b/engines/nancy/action/recordtypes.cpp
index b0732d4b83..31cda8241d 100644
--- a/engines/nancy/action/recordtypes.cpp
+++ b/engines/nancy/action/recordtypes.cpp
@@ -318,7 +318,7 @@ uint16 LoseGame::readData(Common::SeekableReadStream &stream) {
 
 void LoseGame::execute(NancyEngine *engine) {
     engine->stopAndUnloadSpecificSounds();
-    engine->setGameState(NancyEngine::kMainMenu);
+    engine->setState(NancyEngine::kMainMenu);
     engine->scene->resetStateToInit();
     isDone = true;
 }
@@ -340,7 +340,7 @@ uint16 WinGame::readData(Common::SeekableReadStream &stream) {
 
 void WinGame::execute(NancyEngine *engine) {
     engine->stopAndUnloadSpecificSounds();
-    engine->setGameState(NancyEngine::kCredits, NancyEngine::kMainMenu);
+    engine->setState(NancyEngine::kCredits, NancyEngine::kMainMenu);
     engine->scene->resetStateToInit();
     isDone = true;
 }
diff --git a/engines/nancy/console.cpp b/engines/nancy/console.cpp
index 677fedd938..b5597f4b5e 100644
--- a/engines/nancy/console.cpp
+++ b/engines/nancy/console.cpp
@@ -323,7 +323,7 @@ bool NancyConsole::Cmd_loadScene(int argc, const char **argv) {
 		return true;
 	}
 	
-	if (_vm->getPreviousGameState() != NancyEngine::GameState::kScene) {
+	if (_vm->getPreviousState() != NancyEngine::GameState::kScene) {
 		debugPrintf("Not in the kScene state\n");
 		return true;
 	}
@@ -341,7 +341,7 @@ bool NancyConsole::Cmd_loadScene(int argc, const char **argv) {
 }
 
 bool NancyConsole::Cmd_sceneID(int argc, const char **argv) {
-	if (_vm->getPreviousGameState() != NancyEngine::GameState::kScene) {
+	if (_vm->getPreviousState() != NancyEngine::GameState::kScene) {
 		debugPrintf("Not in the kScene state\n");
 		return true;
 	}
diff --git a/engines/nancy/nancy.cpp b/engines/nancy/nancy.cpp
index b1f37efb45..d9a7f4cbbf 100644
--- a/engines/nancy/nancy.cpp
+++ b/engines/nancy/nancy.cpp
@@ -152,7 +152,7 @@ Common::Error NancyEngine::run() {
 			bootGameEngine();
 			graphicsManager->init();
 			cursorManager->init();
-			setGameState(kLogo);
+			setState(kLogo);
 			break;
 		case kLogo:
 			logo->process();
@@ -161,10 +161,10 @@ Common::Error NancyEngine::run() {
 			credits->process();
 			break;
 		case kMainMenu: {
-			GameState prevState = getPreviousGameState();
+			GameState prevState = getPreviousState();
 			// TODO until the game's own menus are implemented we simply open the GMM
 			openMainMenuDialog();
-			setGameState(prevState);
+			setState(prevState);
 			break;
 		}
 		case kHelp:
@@ -186,7 +186,7 @@ Common::Error NancyEngine::run() {
 				dialog->runModal();
 				delete dialog;
 			}
-			setGameState(getPreviousGameState());
+			setState(getPreviousState());
 			input->forceCleanInput();
 			break;
 		}
@@ -282,14 +282,14 @@ void NancyEngine::setMouseEnabled(bool enabled) {
 
 void NancyEngine::pauseEngineIntern(bool pause) {
 	if (pause) {
-		if (getGameState() == kScene) {
+		if (getState() == kScene) {
 			scene->requestStateChange(kPause);
 			scene->changeGameState(true);
 		} else {
-			setGameState(kPause, kNone, true);
+			setState(kPause, kNone, true);
 		}
 	} else {
-		setGameState(getPreviousGameState(), kNone, true);
+		setState(getPreviousState(), kNone, true);
 	}
 
 	graphicsManager->onPause(pause);
@@ -378,7 +378,7 @@ void NancyEngine::readImageList(const IFF &boot, const Common::String &prefix, I
 	}
 }
 
-void NancyEngine::setGameState(GameState state, GameState overridePrevious, bool keepGraphics) {
+void NancyEngine::setState(GameState state, GameState overridePrevious, bool keepGraphics) {
 	if (overridePrevious != kNone) {
 		_gameFlow.previousGameState = overridePrevious;
 	} else {
diff --git a/engines/nancy/nancy.h b/engines/nancy/nancy.h
index ccad3bbc58..fa72cb184f 100644
--- a/engines/nancy/nancy.h
+++ b/engines/nancy/nancy.h
@@ -125,10 +125,10 @@ public:
 	// Used for state switching
 	void stopAndUnloadSpecificSounds();
 	
-	void setGameState(GameState state, GameState overridePrevious = kNone, bool keepGraphics = false);
-	GameState getGameState() const { return _gameFlow.minGameState; }
-	GameState getPreviousGameState() const { return _gameFlow.previousGameState; }
-	void callCheatMenu(bool eventFlags) { setGameState(kCheat), _cheatTypeIsEventFlag = eventFlags; }
+	void setState(GameState state, GameState overridePrevious = kNone, bool keepGraphics = false);
+	GameState getState() const { return _gameFlow.minGameState; }
+	GameState getPreviousState() const { return _gameFlow.previousGameState; }
+	void callCheatMenu(bool eventFlags) { setState(kCheat), _cheatTypeIsEventFlag = eventFlags; }
 
 	void setMouseEnabled(bool enabled);
 
diff --git a/engines/nancy/state/credits.cpp b/engines/nancy/state/credits.cpp
index 54379b08a9..1b0e345c19 100644
--- a/engines/nancy/state/credits.cpp
+++ b/engines/nancy/state/credits.cpp
@@ -77,7 +77,7 @@ void Credits::init() {
     _engine->sound->loadSound(_sound);
     _engine->sound->playSound(_sound);
 
-    _returnToState = _engine->getPreviousGameState();
+    _returnToState = _engine->getPreviousState();
 
     _background.registerGraphics();
     _text.registerGraphics();
@@ -94,9 +94,9 @@ void Credits::run() {
         _state = kInit;
         _engine->sound->stopSound(_sound);
         if (_returnToState == NancyEngine::kMainMenu) {
-            _engine->setGameState((NancyEngine::GameState)_returnToState, NancyEngine::kScene);
+            _engine->setState((NancyEngine::GameState)_returnToState, NancyEngine::kScene);
         } else {
-            _engine->setGameState((NancyEngine::GameState)_returnToState);
+            _engine->setState((NancyEngine::GameState)_returnToState);
         }
         _engine->cursorManager->showCursor(true);
         _fullTextSurface.free();
diff --git a/engines/nancy/state/help.cpp b/engines/nancy/state/help.cpp
index 402c0ac479..7c506308f4 100644
--- a/engines/nancy/state/help.cpp
+++ b/engines/nancy/state/help.cpp
@@ -79,7 +79,7 @@ void Help::begin() {
     _image.setVisible(true);
 
     _engine->cursorManager->setCursorType(CursorManager::kNormalArrow);
-    _previousState = _engine->getPreviousGameState();
+    _previousState = _engine->getPreviousState();
     
     _state = kRun;
 }
@@ -95,7 +95,7 @@ void Help::run() {
 
 void Help::waitForSound() {
     if (!_engine->sound->isSoundPlaying(18)) {
-        _engine->setGameState((NancyEngine::GameState)_previousState);
+        _engine->setState((NancyEngine::GameState)_previousState);
         
 	    _engine->sound->stopSound(_sound);
         _state = kBegin;
diff --git a/engines/nancy/state/logo.cpp b/engines/nancy/state/logo.cpp
index d9a053ed1a..0fea18279a 100644
--- a/engines/nancy/state/logo.cpp
+++ b/engines/nancy/state/logo.cpp
@@ -109,7 +109,7 @@ void Logo::stop() {
 	// For the N+C key combo it looks for some kind of cheat file
 	// to initialize the game state with.
 
-	_engine->setGameState(NancyEngine::kScene);
+	_engine->setState(NancyEngine::kScene);
 	_engine->_system->fillScreen(0);
 }
 
diff --git a/engines/nancy/state/map.cpp b/engines/nancy/state/map.cpp
index 6d5f5194f4..98e64f982d 100644
--- a/engines/nancy/state/map.cpp
+++ b/engines/nancy/state/map.cpp
@@ -160,7 +160,7 @@ void Map::stop() {
     sound.read(*chunk, SoundDescription::kMenu);
     _engine->sound->stopSound(sound);
     
-    _engine->setGameState(NancyEngine::kScene);
+    _engine->setState(NancyEngine::kScene);
 
     if (_pickedLocationID != -1) {
         auto &loc = _locations[_pickedLocationID];
diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index 7b078339d1..dd395e22c9 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -291,7 +291,7 @@ void Scene::run() {
     }
 
     // Do some work if we're coming from a different game state
-    if (_engine->getGameState() != _engine->getPreviousGameState()) {
+    if (_engine->getState() != _engine->getPreviousState()) {
         if (hasLoadedFromSavefile) {
             if (playTime > _timers.pushedPlayTime) {
                 _engine->setTotalPlayTime((uint32)_timers.pushedPlayTime);
@@ -300,7 +300,7 @@ void Scene::run() {
         }
 
         // If the GMM was on we shouldn't reregister graphics
-        if (_engine->getPreviousGameState() != Nancy::NancyEngine::kPause) {
+        if (_engine->getPreviousState() != Nancy::NancyEngine::kPause) {
             registerGraphics();
         }
 
@@ -404,7 +404,7 @@ void Scene::readSceneSummary(Common::SeekableReadStream &stream) {
 bool Scene::changeGameState(bool keepGraphics) {
     if (_gameStateRequested != NancyEngine::kScene) {
         _timers.pushedPlayTime = _engine->getTotalPlayTime();
-        _engine->setGameState(_gameStateRequested, NancyEngine::kNone, keepGraphics);
+        _engine->setState(_gameStateRequested, NancyEngine::kNone, keepGraphics);
         _gameStateRequested = NancyEngine::kScene;
         pauseSceneSpecificSounds();
 


Commit: 7fcdeb641c3326b7dfc495f00a18d1523611e8d4
    https://github.com/scummvm/scummvm/commit/7fcdeb641c3326b7dfc495f00a18d1523611e8d4
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Time fixes

Got rid of the useless tickCount variable and made sure the total play time gets adjusted after switching back to Scene from another state.

Changed paths:
    engines/nancy/action/actionmanager.cpp
    engines/nancy/state/scene.cpp
    engines/nancy/state/scene.h


diff --git a/engines/nancy/action/actionmanager.cpp b/engines/nancy/action/actionmanager.cpp
index deeb7f366f..2e720d7efe 100644
--- a/engines/nancy/action/actionmanager.cpp
+++ b/engines/nancy/action/actionmanager.cpp
@@ -281,7 +281,7 @@ void ActionManager::processActionRecords() {
                     case kLogicCondition:
                         if (_engine->scene->_flags.logicConditions[dep.label].flag == dep.condition) {
                             // Wait for specified time before satisfying dependency condition
-                            Time elapsed = _engine->scene->_timers.totalTime - _engine->scene->_flags.logicConditions[dep.label].timestamp;
+                            Time elapsed = _engine->scene->_timers.lastTotalTime - _engine->scene->_flags.logicConditions[dep.label].timestamp;
 
                             if (elapsed >= dep.timeData) {
                                 dep.satisfied = true;
@@ -290,7 +290,7 @@ void ActionManager::processActionRecords() {
 
                         break;
                     case kTotalTime:
-                        if (_engine->scene->_timers.totalTime >= dep.timeData) {
+                        if (_engine->scene->_timers.lastTotalTime >= dep.timeData) {
                             dep.satisfied = true;
                         }
 
diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index dd395e22c9..342aed72f7 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -153,11 +153,11 @@ void Scene::init() {
         _flags.items[i] = kFalse;
     }
 
+    _timers.lastTotalTime = 0;
     _timers.playerTime = _engine->startTimeHours * 3600000;
     _timers.sceneTime = 0;
     _timers.timerTime = 0;
     _timers.timerIsActive = false;
-    _timers.tickCount = 0;
     _timers.playerTimeNextMinute = 0;
     _timers.pushedPlayTime = 0;
     _timers.timeOfDay = Timers::kDay;
@@ -284,7 +284,6 @@ void Scene::run() {
 
     isComingFromMenu = false;
 
-    Time playTime = _engine->getTotalPlayTime();
 
     if (changeGameState()) {
         return;
@@ -292,18 +291,13 @@ void Scene::run() {
 
     // Do some work if we're coming from a different game state
     if (_engine->getState() != _engine->getPreviousState()) {
-        if (hasLoadedFromSavefile) {
-            if (playTime > _timers.pushedPlayTime) {
-                _engine->setTotalPlayTime((uint32)_timers.pushedPlayTime);
-                playTime = _timers.pushedPlayTime;
-            }
-        }
-
         // If the GMM was on we shouldn't reregister graphics
         if (_engine->getPreviousState() != Nancy::NancyEngine::kPause) {
             registerGraphics();
         }
 
+        _engine->setTotalPlayTime((uint32)_timers.pushedPlayTime);
+
         unpauseSceneSpecificSounds();
         _menuButton.setVisible(false);
         _helpButton.setVisible(false);
@@ -311,23 +305,21 @@ void Scene::run() {
         return;
     }
 
-    Time deltaTime = 0;
+    Time currentPlayTime = _engine->getTotalPlayTime();
 
-    if (_timers.tickCount < playTime) {
-        deltaTime = playTime - _timers.tickCount;
-        _timers.tickCount = playTime;
-    }
-
-    _timers.totalTime += deltaTime;
+    Time deltaTime = currentPlayTime - _timers.lastTotalTime;
+    _timers.lastTotalTime = currentPlayTime;
 
-    if (_timers.timerIsActive)
+    if (_timers.timerIsActive) {
         _timers.timerTime += deltaTime;
+    }
+    
     _timers.sceneTime += deltaTime;
 
     // Calculate the in-game time (playerTime)
-    if (playTime > _timers.playerTimeNextMinute) {
+    if (currentPlayTime > _timers.playerTimeNextMinute) {
         _timers.playerTime += 60000; // Add a minute
-        _timers.playerTimeNextMinute = playTime + playerTimeMinuteLength; // Set when we're going to add the next minute
+        _timers.playerTimeNextMinute = currentPlayTime + playerTimeMinuteLength; // Set when we're going to add the next minute
     }
 
     // Set the time of day according to playerTime
diff --git a/engines/nancy/state/scene.h b/engines/nancy/state/scene.h
index 18b30bee1c..5ef38f34a0 100644
--- a/engines/nancy/state/scene.h
+++ b/engines/nancy/state/scene.h
@@ -195,11 +195,8 @@ protected:
 
     struct Timers {
         enum TimeOfDay { kDay = 0, kNight = 1, kDuskDawn = 2 };
-
-        Time tickCount;
         Time pushedPlayTime;
-
-        Time totalTime;
+        Time lastTotalTime;
         Time sceneTime;
         Time timerTime;
         bool timerIsActive = false;


Commit: a752f1a4f2b94865cd731c421775e230b2e606cf
    https://github.com/scummvm/scummvm/commit/a752f1a4f2b94865cd731c421775e230b2e606cf
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Initial save/load support

Added initial support for savestates. The format will most likely change with future commits, but I will only start versioning once the engine supports more than a single game. To support proper serialization the Scene state has had its kInit state split into kInit and kInitStatic, InventoryBox had some of its code reordered, and members of several classes have had their types changed.

Changed paths:
    engines/nancy/action/actionmanager.cpp
    engines/nancy/action/actionmanager.h
    engines/nancy/action/actionrecord.h
    engines/nancy/action/sliderpuzzle.cpp
    engines/nancy/action/sliderpuzzle.h
    engines/nancy/commontypes.h
    engines/nancy/console.cpp
    engines/nancy/metaengine.cpp
    engines/nancy/nancy.cpp
    engines/nancy/nancy.h
    engines/nancy/state/scene.cpp
    engines/nancy/state/scene.h
    engines/nancy/ui/inventorybox.cpp
    engines/nancy/ui/inventorybox.h


diff --git a/engines/nancy/action/actionmanager.cpp b/engines/nancy/action/actionmanager.cpp
index 2e720d7efe..a33942dfd8 100644
--- a/engines/nancy/action/actionmanager.cpp
+++ b/engines/nancy/action/actionmanager.cpp
@@ -34,6 +34,7 @@
 #include "common/memstream.h"
 #include "common/events.h"
 #include "common/str.h"
+#include "common/serializer.h"
 
 namespace Nancy {
 namespace Action {
@@ -443,5 +444,13 @@ void ActionManager::clearActionRecords() {
     _records.clear();
 }
 
+void ActionManager::synchronize(Common::Serializer &ser) {
+    // When loading, the records should already have been initialized by scene
+    for (auto &rec : _records) {
+        ser.syncAsByte(rec->isActive);
+        ser.syncAsByte(rec->isDone);
+    }
+}
+
 } // End of namespace Action
 } // End of namespace Nancy
diff --git a/engines/nancy/action/actionmanager.h b/engines/nancy/action/actionmanager.h
index 15f1205bac..dac22f0034 100644
--- a/engines/nancy/action/actionmanager.h
+++ b/engines/nancy/action/actionmanager.h
@@ -30,6 +30,10 @@
 #include "common/array.h"
 #include "common/func.h"
 
+namespace Common {
+class Serializer;
+}
+
 namespace Nancy {
 
 class NancyEngine;
@@ -58,6 +62,8 @@ public:
     ActionRecord *getActionRecord(uint id) { if (id < _records.size()) return _records[id]; else return nullptr;}
     void clearActionRecords();
 
+    void synchronize(Common::Serializer &serializer);
+
 protected:
     virtual ActionRecord *createActionRecord(uint16 type);
 
diff --git a/engines/nancy/action/actionrecord.h b/engines/nancy/action/actionrecord.h
index b4e131d3d2..2aaf40242e 100644
--- a/engines/nancy/action/actionrecord.h
+++ b/engines/nancy/action/actionrecord.h
@@ -85,7 +85,7 @@ public:
     ActionRecord() :
         type(0),
         execType(kOneShot),
-        isActive(0),
+        isActive(false),
         isDone(false),
         hasHotspot(false),
         state(ExecutionState::kBegin),
diff --git a/engines/nancy/action/sliderpuzzle.cpp b/engines/nancy/action/sliderpuzzle.cpp
index c82e94a0bb..1c60682cf9 100644
--- a/engines/nancy/action/sliderpuzzle.cpp
+++ b/engines/nancy/action/sliderpuzzle.cpp
@@ -29,6 +29,8 @@
 #include "engines/nancy/sound.h"
 #include "engines/nancy/state/scene.h"
 
+#include "common/serializer.h"
+
 namespace Nancy {
 namespace Action {
 
@@ -131,9 +133,7 @@ void SliderPuzzle::execute(Nancy::NancyEngine *engine) {
 
         for (uint y = 0; y < height; ++y) {
             for (uint x = 0; x < width; ++x) {
-                if (!srcRects[y][x].isEmpty()) {
-                    drawTile(playerTileOrder[y][x], x, y);
-                }
+                drawTile(playerTileOrder[y][x], x, y);
             }
         }
 
@@ -287,7 +287,35 @@ void SliderPuzzle::handleInput(NancyInput &input) {
     }
 }
 
-void SliderPuzzle::drawTile(uint tileID, uint posX, uint posY) {
+void SliderPuzzle::synchronize(Common::Serializer &ser) {
+    ser.syncAsByte(playerHasTriedPuzzle);
+
+    byte x, y;
+
+    if (ser.isSaving()) {
+        y = playerTileOrder.size();
+        if (y) {
+            x = playerTileOrder.back().size();
+        }
+    }
+
+    ser.syncAsByte(x);
+    ser.syncAsByte(y);
+
+    playerTileOrder.resize(y);
+
+    for (int i = 0; i < y; ++i) {
+        playerTileOrder[i].resize(x);
+        ser.syncArray(playerTileOrder[i].data(), x, Common::Serializer::Sint16LE);
+    }
+}
+
+void SliderPuzzle::drawTile(int tileID, uint posX, uint posY) {
+    if (tileID < 0) {
+        undrawTile(posX, posY);
+        return;
+    }
+
     Common::Point destPoint(destRects[posY][posX].left - _screenPosition.left, destRects[posY][posX].top - _screenPosition.top);
     _drawSurface.blitFrom(image, srcRects[tileID / height][tileID % width], destPoint);
 
diff --git a/engines/nancy/action/sliderpuzzle.h b/engines/nancy/action/sliderpuzzle.h
index f7144a6a0c..520f36daa3 100644
--- a/engines/nancy/action/sliderpuzzle.h
+++ b/engines/nancy/action/sliderpuzzle.h
@@ -34,6 +34,10 @@
 
 #include "graphics/managed_surface.h"
 
+namespace Common {
+class Serializer;
+}
+
 namespace Nancy {
 namespace Action {
 
@@ -49,6 +53,8 @@ public:
     virtual void execute(Nancy::NancyEngine *engine) override;
     virtual void handleInput(NancyInput &input) override;
 
+    static void synchronize(Common::Serializer &ser);
+
     Common::String imageName; // 0x00
     uint16 width; // 0xA
     uint16 height; // 0xC
@@ -76,7 +82,7 @@ protected:
     virtual BlitType getBlitType() const override { return kTrans; }
     virtual bool isViewportRelative() const override { return true; }
 
-    void drawTile(uint tileID, uint posX, uint posY);
+    void drawTile(int tileID, uint posX, uint posY);
     void undrawTile(uint posX, uint posY);
 };
 
diff --git a/engines/nancy/commontypes.h b/engines/nancy/commontypes.h
index fb445ce6fc..1113edc833 100644
--- a/engines/nancy/commontypes.h
+++ b/engines/nancy/commontypes.h
@@ -35,7 +35,7 @@ namespace Nancy {
 
 class NancyEngine;
 
-enum NancyFlag { kFalse = 1, kTrue = 2 };
+enum NancyFlag : byte { kFalse = 1, kTrue = 2 };
 enum MovementDirection : byte { kUp = 1, kDown = 2, kLeft = 4, kRight = 8, kMoveFast = 16 };
 
 // Describes a scene transition
diff --git a/engines/nancy/console.cpp b/engines/nancy/console.cpp
index b5597f4b5e..810c169934 100644
--- a/engines/nancy/console.cpp
+++ b/engines/nancy/console.cpp
@@ -336,7 +336,7 @@ bool NancyConsole::Cmd_loadScene(int argc, const char **argv) {
 	}
 
 	_vm->scene->changeScene((uint16)atoi(argv[1]), 0, 0, false);
-	_vm->scene->_state = State::Scene::kLoadNew;
+	_vm->scene->_state = State::Scene::kLoad;
 	return cmdExit(0, 0);
 }
 
diff --git a/engines/nancy/metaengine.cpp b/engines/nancy/metaengine.cpp
index 16d4016d22..3284e9bda3 100644
--- a/engines/nancy/metaengine.cpp
+++ b/engines/nancy/metaengine.cpp
@@ -60,9 +60,6 @@ public:
     Common::Error createInstance(OSystem *syst, Engine **engine, const ADGameDescription *gd) const override;
 
     int getMaximumSaveSlot() const override;
-	SaveStateList listSaves(const char *target) const override;
-	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const override;
-	void removeSaveState(const char *target, int slot) const override;
 
 	Common::KeymapArray initKeymaps(const char *target) const override;
 };
@@ -80,7 +77,9 @@ bool NancyMetaEngine::hasFeature(MetaEngineFeature f) const {
 	    (f == kSupportsDeleteSave) ||
 	    (f == kSavesSupportMetaInfo) ||
 	    (f == kSavesSupportThumbnail) ||
-	    (f == kSavesSupportCreationDate);
+	    (f == kSavesSupportCreationDate) ||
+		(f == kSavesSupportPlayTime) ||
+		(f == kSavesUseExtendedFormat);
 }
 
 Common::Error NancyMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *gd) const {
@@ -94,113 +93,7 @@ Common::Error NancyMetaEngine::createInstance(OSystem *syst, Engine **engine, co
 	else return Common::Error();
 }
 
-int NancyMetaEngine::getMaximumSaveSlot() const { return 99; }
-
-SaveStateList NancyMetaEngine::listSaves(const char *target) const {
-	Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
-	Common::StringArray filenames;
-	Common::String pattern = target;
-	pattern += "-??.SAV";
-
-	filenames = saveFileMan->listSavefiles(pattern);
-	sort(filenames.begin(), filenames.end());   // Sort (hopefully ensuring we are sorted numerically..)
-
-	SaveStateList saveList;
-	char slot[3];
-	int slotNum = 0;
-	for (Common::StringArray::const_iterator filename = filenames.begin(); filename != filenames.end(); ++filename) {
-		slot[0] = filename->c_str()[filename->size() - 6];
-		slot[1] = filename->c_str()[filename->size() - 5];
-		slot[2] = '\0';
-		// Obtain the last 2 digits of the filename (without extension), since they correspond to the save slot
-		slotNum = atoi(slot);
-		if (slotNum >= 0 && slotNum <= getMaximumSaveSlot()) {
-			Common::InSaveFile *file = saveFileMan->openForLoading(*filename);
-			if (file) {
-				int saveVersion = file->readByte();
-
-				if (saveVersion != Nancy::kSavegameVersion) {
-					warning("Savegame of incompatible version");
-					delete file;
-					continue;
-				}
-
-				// read name
-				uint16 nameSize = file->readUint16BE();
-				if (nameSize >= 255) {
-					delete file;
-					continue;
-				}
-				char name[256];
-				file->read(name, nameSize);
-				name[nameSize] = 0;
-
-				saveList.push_back(SaveStateDescriptor(slotNum, name));
-				delete file;
-			}
-		}
-	}
-
-	return saveList;
-}
-
-SaveStateDescriptor NancyMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
-	Common::String fileName = Common::String::format("%s-%02d.SAV", target, slot);
-	Common::InSaveFile *file = g_system->getSavefileManager()->openForLoading(fileName);
-
-	if (file) {
-		int saveVersion = file->readByte();
-
-		if (saveVersion != Nancy::kSavegameVersion) {
-			warning("Savegame of incompatible version");
-			delete file;
-			return SaveStateDescriptor();
-		}
-
-		uint32 saveNameLength = file->readUint16BE();
-		char saveName[256];
-		file->read(saveName, saveNameLength);
-		saveName[saveNameLength] = 0;
-
-		SaveStateDescriptor desc(slot, saveName);
-
-		Graphics::Surface *thumbnail = nullptr;
-
-		if (Graphics::loadThumbnail(*file, thumbnail))
-			desc.setThumbnail(thumbnail);
-
-		desc.setDeletableFlag(true);
-		desc.setWriteProtectedFlag(false);
-
-		uint32 saveDate = file->readUint32BE();
-		uint16 saveTime = file->readUint16BE();
-
-		int day = (saveDate >> 24) & 0xFF;
-		int month = (saveDate >> 16) & 0xFF;
-		int year = saveDate & 0xFFFF;
-
-		desc.setSaveDate(year, month, day);
-
-		int hour = (saveTime >> 8) & 0xFF;
-		int minutes = saveTime & 0xFF;
-
-		desc.setSaveTime(hour, minutes);
-
-		// Slot 0 is used for the 'restart game' save in all Nancy games, thus
-		// we prevent it from being deleted.
-		desc.setDeletableFlag(slot != 0);
-		desc.setWriteProtectedFlag(slot == 0);
-
-		delete file;
-		return desc;
-	}
-	return SaveStateDescriptor();
-}
-
-void NancyMetaEngine::removeSaveState(const char *target, int slot) const {
-	Common::String fileName = Common::String::format("%s-%02d.SAV", target, slot);
-	g_system->getSavefileManager()->removeSavefile(fileName);
-}
+int NancyMetaEngine::getMaximumSaveSlot() const { return 8; }
 
 #if PLUGIN_ENABLED_DYNAMIC(NANCY)
     REGISTER_PLUGIN_DYNAMIC(NANCY, PLUGIN_TYPE_ENGINE, NancyMetaEngine);
diff --git a/engines/nancy/nancy.cpp b/engines/nancy/nancy.cpp
index d9a7f4cbbf..6d9a47709e 100644
--- a/engines/nancy/nancy.cpp
+++ b/engines/nancy/nancy.cpp
@@ -26,6 +26,10 @@
 #include "engines/nancy/state/map.h"
 #include "engines/nancy/state/credits.h"
 
+#include "engines/nancy/action/sliderpuzzle.h"
+#include "engines/nancy/action/primaryvideo.h"
+#include "engines/nancy/action/secondarymovie.h"
+
 #include "engines/nancy/nancy.h"
 #include "engines/nancy/resource.h"
 #include "engines/nancy/iff.h"
@@ -46,6 +50,8 @@
 #include "common/memstream.h"
 #include "common/installshield_cab.h"
 #include "common/str.h"
+#include "common/savefile.h"
+#include "common/serializer.h"
 
 #include "graphics/surface.h"
 
@@ -297,6 +303,29 @@ void NancyEngine::pauseEngineIntern(bool pause) {
 	Engine::pauseEngineIntern(pause);
 }
 
+Common::Error NancyEngine::loadGameStream(Common::SeekableReadStream *stream) {
+	Common::Serializer ser(stream, nullptr);
+
+	auto ret = synchronize(ser);
+
+	if (ret.getCode() == Common::kNoError) {
+		scene->hasLoadedFromSavefile = true;
+	}
+
+	return ret;
+}
+
+bool NancyEngine::canSaveGameStateCurrently() {
+	// TODO also disable during secondary movie
+	return Action::PlayPrimaryVideoChan0::activePrimaryVideo == nullptr;
+}
+
+Common::Error NancyEngine::saveGameStream(Common::WriteStream *stream, bool isAutosave) {
+	Common::Serializer ser(nullptr, stream);
+
+	return synchronize(ser);
+}
+
 void NancyEngine::clearBootChunks() {
 	for (auto const& i : _bootChunks) {
 		delete i._value;
@@ -304,6 +333,31 @@ void NancyEngine::clearBootChunks() {
 	_bootChunks.clear();
 }
 
+Common::Error NancyEngine::synchronize(Common::Serializer &ser) {
+	Common::SeekableReadStream *bsum = getBootChunkStream("BSUM");
+	bsum->seek(0);
+	
+	if (ser.isLoading()) {
+		byte buf[90];
+		byte bsumBuf[90];
+		ser.syncBytes(buf, 90);
+		bsum->read(bsumBuf, 90);
+		if (Common::String((char *)bsumBuf) != (char *)buf) {
+			return Common::kReadingFailed;
+		}
+	} else if (ser.isSaving()) {
+		byte buf[90];
+		bsum->read(buf, 90);
+		ser.syncBytes(buf, 90);
+	}
+
+	scene->synchronize(ser);
+	scene->_actionManager.synchronize(ser);
+	Action::SliderPuzzle::synchronize(ser);
+
+	return Common::kNoError;
+}
+
 void NancyEngine::preloadCals(const IFF &boot) {
 	const byte *buf;
 	uint size;
@@ -339,10 +393,6 @@ void NancyEngine::syncSoundSettings() {
 //	_sound->syncVolume();
 }
 
-Common::String NancyEngine::getSavegameFilename(int slot) {
-	return _targetName + Common::String::format("-%02d.SAV", slot);
-}
-
 Common::String NancyEngine::readFilename(Common::ReadStream *stream) const {
 	char buf[kMaxFilenameLen + 1];
 	int read = stream->read(buf, getFilenameLen());
diff --git a/engines/nancy/nancy.h b/engines/nancy/nancy.h
index fa72cb184f..1f8ade2ca8 100644
--- a/engines/nancy/nancy.h
+++ b/engines/nancy/nancy.h
@@ -33,6 +33,7 @@
 
 namespace Common {
 class RandomSource;
+class Serializer;
 }
 
 /**
@@ -114,7 +115,6 @@ public:
 	bool hasFeature(EngineFeature f) const;
 	const char *getCopyrightString() const;
 
-	Common::String getSavegameFilename(int slot);
 	void syncSoundSettings();
 
 	static NancyEngine *create(GameType type, OSystem *syst, const NancyGameDescription *gd);
@@ -134,6 +134,11 @@ public:
 
 	virtual void pauseEngineIntern(bool pause) override;
 
+	virtual Common::Error loadGameStream(Common::SeekableReadStream *stream) override;
+	virtual Common::Error saveGameStream(Common::WriteStream *stream, bool isAutosave = false) override;
+	virtual bool canLoadGameStateCurrently() override { return canSaveGameStateCurrently(); };
+	virtual bool canSaveGameStateCurrently() override;
+
 	// Managers
 	ResourceManager *_res;
 	GraphicsManager *graphicsManager;
@@ -169,6 +174,8 @@ protected:
 	bool addBootChunk(const Common::String &name, Common::SeekableReadStream *stream);
 	void clearBootChunks();
 
+	Common::Error synchronize(Common::Serializer &serializer);
+
 	enum {
 		kMaxFilenameLen = 32
 	};
diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index 342aed72f7..3932bb8076 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -21,10 +21,10 @@
  */
 
 #include "engines/nancy/state/scene.h"
+
 #include "engines/nancy/nancy.h"
 #include "engines/nancy/resource.h"
 #include "engines/nancy/iff.h"
-#include "engines/nancy/action/actionmanager.h"
 #include "engines/nancy/input.h"
 #include "engines/nancy/sound.h"
 #include "engines/nancy/graphics.h"
@@ -32,9 +32,13 @@
 #include "engines/nancy/time.h"
 #include "engines/nancy/util.h"
 
+#include "engines/nancy/action/actionmanager.h"
+#include "engines/nancy/action/sliderpuzzle.h"
+
 #include "common/memstream.h"
 #include "common/rect.h"
 #include "common/func.h"
+#include "common/serializer.h"
 
 #include "graphics/surface.h"
 
@@ -45,10 +49,9 @@ void Scene::process() {
     switch (_state) {
     case kInit:
         init();
-        _state = kLoad;
         // fall through
-    case kLoadNew:
-        _state = kLoad;
+    case kInitStatic:
+        initStatic();
         // fall through
     case kLoad:
         load();
@@ -76,7 +79,7 @@ void Scene::changeScene(uint16 id, uint16 frame, uint16 verticalOffset, bool noS
     _sceneState.nextScene.frameID = frame;
     _sceneState.nextScene.verticalOffset = verticalOffset;
     _sceneState._doNotStartSound = noSound;
-    _state = kLoadNew;
+    _state = kLoad;
 }
 
 void Scene::changeScene(const SceneChangeDescription &sceneDescription) {
@@ -139,13 +142,89 @@ void Scene::registerGraphics() {
     _textbox.setVisible(false);
 }
 
+void Scene::synchronize(Common::Serializer &ser) {
+    if (ser.isSaving()) {
+        ser.syncAsUint16LE(_sceneState.currentScene.sceneID);
+        ser.syncAsUint16LE(_sceneState.currentScene.frameID);
+        ser.syncAsUint16LE(_sceneState.currentScene.verticalOffset);
+    } else if (ser.isLoading()) {
+        ser.syncAsUint16LE(_sceneState.nextScene.sceneID);
+        ser.syncAsUint16LE(_sceneState.nextScene.frameID);
+        ser.syncAsUint16LE(_sceneState.nextScene.verticalOffset);
+        _sceneState._doNotStartSound = false;
+
+        initStatic();
+        load();
+    }
+    
+    ser.syncAsUint16LE(_sceneState.pushedScene.sceneID);
+    ser.syncAsUint16LE(_sceneState.pushedScene.frameID);
+    ser.syncAsUint16LE(_sceneState.pushedScene.verticalOffset);
+    ser.syncAsByte(_sceneState.isScenePushed);
+
+    // hardcoded number of logic conditions, check if there can ever be more/less
+	for (uint i = 0; i < 30; ++i) {
+		ser.syncAsUint32LE(_flags.logicConditions[i].flag);
+	}
+
+	for (uint i = 0; i < 30; ++i) {
+		ser.syncAsUint32LE(_flags.logicConditions[i].timestamp);
+	}
+
+	// TODO hardcoded inventory size
+	auto &order = getInventoryBox()._order;
+	uint prevSize = getInventoryBox()._order.size();
+	getInventoryBox()._order.resize(11);
+	
+	if (ser.isSaving()) {
+		for (uint i = prevSize; i < order.size(); ++i) {
+			order[i] = -1;
+		}
+	}
+
+	ser.syncArray(order.data(), 11, Common::Serializer::Sint16LE);
+	
+	while (order.size() && order.back() == -1) {
+		order.pop_back();
+	}
+
+    if (ser.isLoading()) {
+        // Make sure the shades are open if we have items
+        getInventoryBox().onReorder();
+    }
+
+	// TODO hardcoded inventory size
+	ser.syncArray(_flags.items, 11, Common::Serializer::Byte);
+    ser.syncAsSint16LE(_flags.heldItem);
+    _engine->cursorManager->setCursorItemID(_flags.heldItem);
+
+	ser.syncAsUint32LE(_timers.lastTotalTime);
+	ser.syncAsUint32LE(_timers.sceneTime);
+	ser.syncAsUint32LE(_timers.playerTime);
+	ser.syncAsUint32LE(_timers.pushedPlayTime);
+	ser.syncAsUint32LE(_timers.timerTime);
+	ser.syncAsByte(_timers.timerIsActive);
+    ser.syncAsByte(_timers.timeOfDay);
+
+    _engine->setTotalPlayTime((uint32)_timers.lastTotalTime);
+
+	// TODO hardcoded number of event flags
+	ser.syncArray(_flags.eventFlags, 168, Common::Serializer::Byte);
+	
+	ser.syncArray<uint16>(_sceneState.sceneHitCount, (uint16)2001, Common::Serializer::Uint16LE);
+
+	ser.syncAsUint16LE(_difficulty);
+	ser.syncArray<uint16>(_hintsRemaining.data(), _hintsRemaining.size(), Common::Serializer::Uint16LE);
+	ser.syncAsSint16LE(_lastHint);
+}
+
 void Scene::init() {
     for (uint i = 0; i < 168; ++i) {
         _flags.eventFlags[i] = kFalse;
     }
 
     // Does this ever get used?
-    for (uint i = 0; i < 1000; ++i) {
+    for (uint i = 0; i < 2001; ++i) {
         _sceneState.sceneHitCount[i] = 0;
     }
 
@@ -175,7 +254,13 @@ void Scene::init() {
 
     _lastHint = -1;
 
-    chunk = _engine->getBootChunkStream("MAP");
+    Action::SliderPuzzle::playerHasTriedPuzzle = false;
+
+    _state = kInitStatic;
+}
+
+void Scene::initStatic() {
+    Common::SeekableReadStream *chunk = _engine->getBootChunkStream("MAP");
     chunk->seek(0x8A);
     readRect(*chunk, _mapHotspot);
 
diff --git a/engines/nancy/state/scene.h b/engines/nancy/state/scene.h
index 5ef38f34a0..b54859da7f 100644
--- a/engines/nancy/state/scene.h
+++ b/engines/nancy/state/scene.h
@@ -45,7 +45,8 @@ namespace Graphics {
 }
 
 namespace Common {
-    class SeekableReadStream;
+class SeekableReadStream;
+class Serializer;
 }
 
 namespace Nancy {
@@ -138,6 +139,8 @@ public:
 
     void registerGraphics();
 
+    void synchronize(Common::Serializer &serializer);
+
     UI::FullScreenImage &getFrame() { return _frame; }
     UI::Viewport &getViewport() { return _viewport; }
     UI::Textbox &getTextbox() { return _textbox; }
@@ -150,6 +153,7 @@ public:
 
 private:
     void init();
+    void initStatic();
     void load();
     void run();
 
@@ -162,10 +166,10 @@ private:
 public:
     enum State {
         kInit,
+        kInitStatic,
         kLoad,
         kStartSound,
-        kRun,
-        kLoadNew
+        kRun
     };
 
     enum GameStateChange : byte {
@@ -188,7 +192,7 @@ protected:
         SceneInfo nextScene;
         SceneInfo pushedScene;
         bool isScenePushed;
-        byte sceneHitCount[1000];
+        uint16 sceneHitCount[2001];
 
         bool _doNotStartSound = false;
     };
@@ -233,7 +237,7 @@ protected:
     PlayFlags _flags;
     Timers _timers;
     uint16 _difficulty;
-    Common::Array<byte> _hintsRemaining;
+    Common::Array<uint16> _hintsRemaining;
     int16 _lastHint;
     NancyEngine::GameState _gameStateRequested;
 
diff --git a/engines/nancy/ui/inventorybox.cpp b/engines/nancy/ui/inventorybox.cpp
index 831eee29db..fecf30b1a5 100644
--- a/engines/nancy/ui/inventorybox.cpp
+++ b/engines/nancy/ui/inventorybox.cpp
@@ -133,12 +133,12 @@ void InventoryBox::handleInput(NancyInput &input) {
     }
 }
 
-void InventoryBox::addItem(uint itemID) {
+void InventoryBox::addItem(int16 itemID) {
     if (_order.size() == 0) {
         // Adds first item, start shades animation
         _shades.setOpen(true);
     }
-    Common::Array<uint> back = _order;
+    Common::Array<int16> back = _order;
     _order.clear();
     _order.push_back(itemID);
     _order.push_back(back);
@@ -146,13 +146,9 @@ void InventoryBox::addItem(uint itemID) {
     onReorder();
 }
 
-void InventoryBox::removeItem(uint itemID) {
+void InventoryBox::removeItem(int16 itemID) {
     for (auto &i : _order) {
         if (i == itemID) {
-            if (_order.size() == 1) {
-                // Removes last item, start shades animation
-                _shades.setOpen(false);
-            }
             _order.erase(&i);
             onReorder();
             break;
@@ -174,6 +170,11 @@ void InventoryBox::onReorder() {
         _fullInventorySurface.blitFrom(_iconsSurface, _itemDescriptions[_order[i]].sourceRect, destPoint);
     }
 
+    if (_order.size() > 0) {
+        _shades.setOpen(true);
+    } else {
+        _shades.setOpen(false);
+    }
 
     _needsRedraw = true;
 }
@@ -225,8 +226,7 @@ void InventoryBox::Shades::init() {
     Common::Rect bounds = _parent->getBounds();
     _drawSurface.create(bounds.width(), bounds.height(), GraphicsManager::pixelFormat);
     _screenPosition = _parent->getScreenPosition();
-    _curFrame = 0;
-    _areOpen = false;
+    _nextFrameTime = 0;
     setAnimationFrame(_curFrame);
 
     RenderObject::init();
diff --git a/engines/nancy/ui/inventorybox.h b/engines/nancy/ui/inventorybox.h
index 1d0c3ee03e..663b47dea3 100644
--- a/engines/nancy/ui/inventorybox.h
+++ b/engines/nancy/ui/inventorybox.h
@@ -37,11 +37,16 @@ namespace Nancy {
 class NancyEngine;
 struct NancyInput;
 
+namespace State {
+class Scene;
+}
+
 namespace UI {
 
 class InventoryBox : public RenderObject {
 	friend class InventoryScrollbar;
     friend class Shades;
+    friend class Nancy::State::Scene;
 
 public:
     struct ItemDescription {
@@ -64,8 +69,8 @@ public:
     void handleInput(NancyInput &input);
 
     // To be called from Scene
-    void addItem(uint itemID);
-    void removeItem(uint itemID);
+    void addItem(int16 itemID);
+    void removeItem(int16 itemID);
 
     ItemDescription getItemDescription(uint id) { return _itemDescriptions[id]; }
 
@@ -97,7 +102,9 @@ private:
         Shades(RenderObject &redrawFrom, InventoryBox *parent) :
             RenderObject(redrawFrom),
             _parent(parent),
-            _soundTriggered(false) {}
+            _soundTriggered(false),
+            _areOpen(false),
+            _curFrame(0) {}
         virtual ~Shades() =default;
 
         virtual void init() override;
@@ -132,7 +139,7 @@ private:
 
     float _scrollbarPos;
 
-    Common::Array<uint> _order;
+    Common::Array<int16> _order;
     ItemHotspot _itemHotspots[4]; 
 
     // INV contents


Commit: 20f432f8d382795edfa4b6cf0fb012d3335d01e7
    https://github.com/scummvm/scummvm/commit/20f432f8d382795edfa4b6cf0fb012d3335d01e7
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Engine refactor

Made all states (Scene, Credits, Help, etc.) singletons, as well as subclasses of a common State parent with a proper state switching API. Moved the OnPause virtual function from RenderObject to ActionRecord and hooked it into the state switching code. Also got rid of the NancyEngine pointer that got passed around between most classes, and replaced it with several convenience macros. As a result of these changes action records that render to the viewport no longer disappear after opening the menu, and state changes feel snappier.

Changed paths:
  A engines/nancy/state/state.h
    engines/nancy/action/actionmanager.cpp
    engines/nancy/action/actionmanager.h
    engines/nancy/action/actionrecord.h
    engines/nancy/action/arfactory_v1.cpp
    engines/nancy/action/leverpuzzle.cpp
    engines/nancy/action/leverpuzzle.h
    engines/nancy/action/orderingpuzzle.cpp
    engines/nancy/action/orderingpuzzle.h
    engines/nancy/action/passwordpuzzle.cpp
    engines/nancy/action/passwordpuzzle.h
    engines/nancy/action/primaryvideo.cpp
    engines/nancy/action/primaryvideo.h
    engines/nancy/action/recordtypes.cpp
    engines/nancy/action/recordtypes.h
    engines/nancy/action/rotatinglockpuzzle.cpp
    engines/nancy/action/rotatinglockpuzzle.h
    engines/nancy/action/secondarymovie.cpp
    engines/nancy/action/secondarymovie.h
    engines/nancy/action/secondaryvideo.cpp
    engines/nancy/action/secondaryvideo.h
    engines/nancy/action/sliderpuzzle.cpp
    engines/nancy/action/sliderpuzzle.h
    engines/nancy/action/staticbitmapanim.cpp
    engines/nancy/action/staticbitmapanim.h
    engines/nancy/action/telephone.cpp
    engines/nancy/action/telephone.h
    engines/nancy/cheat.cpp
    engines/nancy/cheat.h
    engines/nancy/commontypes.cpp
    engines/nancy/commontypes.h
    engines/nancy/console.cpp
    engines/nancy/console.h
    engines/nancy/cursor.cpp
    engines/nancy/cursor.h
    engines/nancy/font.cpp
    engines/nancy/font.h
    engines/nancy/graphics.cpp
    engines/nancy/graphics.h
    engines/nancy/iff.cpp
    engines/nancy/iff.h
    engines/nancy/input.cpp
    engines/nancy/input.h
    engines/nancy/nancy.cpp
    engines/nancy/nancy.h
    engines/nancy/renderobject.cpp
    engines/nancy/renderobject.h
    engines/nancy/resource.cpp
    engines/nancy/resource.h
    engines/nancy/sound.cpp
    engines/nancy/sound.h
    engines/nancy/state/credits.cpp
    engines/nancy/state/credits.h
    engines/nancy/state/help.cpp
    engines/nancy/state/help.h
    engines/nancy/state/logo.cpp
    engines/nancy/state/logo.h
    engines/nancy/state/map.cpp
    engines/nancy/state/map.h
    engines/nancy/state/scene.cpp
    engines/nancy/state/scene.h
    engines/nancy/ui/button.cpp
    engines/nancy/ui/fullscreenimage.cpp
    engines/nancy/ui/fullscreenimage.h
    engines/nancy/ui/inventorybox.cpp
    engines/nancy/ui/scrollbar.cpp
    engines/nancy/ui/textbox.cpp
    engines/nancy/ui/viewport.cpp
    engines/nancy/ui/viewport.h


diff --git a/engines/nancy/action/actionmanager.cpp b/engines/nancy/action/actionmanager.cpp
index a33942dfd8..9707e584d4 100644
--- a/engines/nancy/action/actionmanager.cpp
+++ b/engines/nancy/action/actionmanager.cpp
@@ -46,14 +46,14 @@ void ActionManager::handleInput(NancyInput &input) {
             rec->handleInput(input);
         }
 
-        if (rec->isActive && rec->hasHotspot && _engine->scene->getViewport().convertViewportToScreen(rec->hotspot).contains(input.mousePos)) {
-            _engine->cursorManager->setCursorType(rec->getHoverCursor());
+        if (rec->isActive && rec->hasHotspot && NancySceneState.getViewport().convertViewportToScreen(rec->hotspot).contains(input.mousePos)) {
+            NanEngine.cursorManager->setCursorType(rec->getHoverCursor());
 
             if (input.input & NancyInput::kLeftMouseButtonUp) {
                 input.input &= ~NancyInput::kLeftMouseButtonUp;
 
                 bool shouldTrigger = false;
-                int16 heldItem = _engine->scene->getHeldItem();
+                int16 heldItem = NancySceneState.getHeldItem();
                 if (rec->itemRequired != -1) {
                     if (heldItem == -1 && rec->itemRequired == -2) {
                         shouldTrigger = true;
@@ -69,7 +69,7 @@ void ActionManager::handleInput(NancyInput &input) {
                     }
 
                     if (!shouldTrigger) {
-                        _engine->sound->playSound(17); // Hardcoded by original engine
+                        NanEngine.sound->playSound(17); // Hardcoded by original engine
                     }
                 } else {
                     shouldTrigger = true;
@@ -83,11 +83,11 @@ void ActionManager::handleInput(NancyInput &input) {
 
                     // Re-add the object to the inventory unless it's marked as a one-time use
                     if (rec->itemRequired == heldItem && rec->itemRequired != -1) {
-                        if (_engine->scene->getInventoryBox().getItemDescription(heldItem).oneTimeUse != 0) {
-                            _engine->scene->getInventoryBox().addItem(heldItem);
+                        if (NancySceneState.getInventoryBox().getItemDescription(heldItem).oneTimeUse != 0) {
+                            NancySceneState.getInventoryBox().addItem(heldItem);
                         }
 
-                        _engine->scene->setHeldItem(-1);
+                        NancySceneState.setHeldItem(-1);
                     }
                 }
 
@@ -253,15 +253,15 @@ void ActionManager::processActionRecords() {
                         switch (dep.condition) {
                         case kFalse:
                             // Item not in possession or held
-                            if (_engine->scene->_flags.items[dep.label] == kFalse &&
-                                dep.label != _engine->scene->_flags.heldItem) {
+                            if (NancySceneState._flags.items[dep.label] == kFalse &&
+                                dep.label != NancySceneState._flags.heldItem) {
                                 dep.satisfied = true;
                             }
 
                             break;
                         case kTrue:
-                            if (_engine->scene->_flags.items[dep.label] == kTrue ||
-                                dep.label == _engine->scene->_flags.heldItem) {
+                            if (NancySceneState._flags.items[dep.label] == kTrue ||
+                                dep.label == NancySceneState._flags.heldItem) {
                                 dep.satisfied = true;
                             }
 
@@ -272,7 +272,7 @@ void ActionManager::processActionRecords() {
 
                         break;
                     case kEventFlag:
-                        if (_engine->scene->getEventFlag(dep.label, (NancyFlag)dep.condition)) {
+                        if (NancySceneState.getEventFlag(dep.label, (NancyFlag)dep.condition)) {
                             // nancy1 has code for some timer array that never gets used
                             // and is discarded from nancy2 onward
                             dep.satisfied = true;
@@ -280,9 +280,9 @@ void ActionManager::processActionRecords() {
 
                         break;
                     case kLogicCondition:
-                        if (_engine->scene->_flags.logicConditions[dep.label].flag == dep.condition) {
+                        if (NancySceneState._flags.logicConditions[dep.label].flag == dep.condition) {
                             // Wait for specified time before satisfying dependency condition
-                            Time elapsed = _engine->scene->_timers.lastTotalTime - _engine->scene->_flags.logicConditions[dep.label].timestamp;
+                            Time elapsed = NancySceneState._timers.lastTotalTime - NancySceneState._flags.logicConditions[dep.label].timestamp;
 
                             if (elapsed >= dep.timeData) {
                                 dep.satisfied = true;
@@ -291,20 +291,20 @@ void ActionManager::processActionRecords() {
 
                         break;
                     case kTotalTime:
-                        if (_engine->scene->_timers.lastTotalTime >= dep.timeData) {
+                        if (NancySceneState._timers.lastTotalTime >= dep.timeData) {
                             dep.satisfied = true;
                         }
 
                         break;
                     case kSceneTime:
-                        if (_engine->scene->_timers.sceneTime >= dep.timeData) {
+                        if (NancySceneState._timers.sceneTime >= dep.timeData) {
                             dep.satisfied = true;
                         }
 
                         break;
                     case kPlayerTime:
                         // TODO almost definitely wrong, as the original engine treats player time differently
-                        if (_engine->scene->_timers.playerTime >= dep.timeData) {
+                        if (NancySceneState._timers.playerTime >= dep.timeData) {
                             dep.satisfied = true;
                         }
 
@@ -320,19 +320,19 @@ void ActionManager::processActionRecords() {
                         // Also, I'm pretty sure it never gets used
                         switch (dep.milliseconds) {
                         case 1:
-                            if (dep.seconds < _engine->scene->_sceneState.sceneHitCount[dep.hours]) {
+                            if (dep.seconds < NancySceneState._sceneState.sceneHitCount[dep.hours]) {
                                 dep.satisfied = true;
                             }
 
                             break;
                         case 2:
-                            if (dep.seconds > _engine->scene->_sceneState.sceneHitCount[dep.hours]) {
+                            if (dep.seconds > NancySceneState._sceneState.sceneHitCount[dep.hours]) {
                                 dep.satisfied = true;
                             }
 
                             break;
                         case 3:
-                            if (dep.seconds == _engine->scene->_sceneState.sceneHitCount[dep.hours]) {
+                            if (dep.seconds == NancySceneState._sceneState.sceneHitCount[dep.hours]) {
                                 dep.satisfied = true;
                             }
 
@@ -342,13 +342,13 @@ void ActionManager::processActionRecords() {
                         break;
                     case kResetOnNewDay:
                         if (record->days == -1) {
-                            record->days = _engine->scene->_timers.playerTime.getDays();
+                            record->days = NancySceneState._timers.playerTime.getDays();
                             dep.satisfied = true;
                             break;
                         }
 
-                        if (record->days < _engine->scene->_timers.playerTime.getDays()) {
-                            record->days = _engine->scene->_timers.playerTime.getDays();
+                        if (record->days < NancySceneState._timers.playerTime.getDays()) {
+                            record->days = NancySceneState._timers.playerTime.getDays();
                             for (uint j = 0; j < record->dependencies.size(); ++j) {
                                 if (record->dependencies[j].type == kPlayerTime) {
                                     record->dependencies[j].satisfied = false;
@@ -379,25 +379,25 @@ void ActionManager::processActionRecords() {
                         break;
                     }
                     case kTimeOfDay:
-                        if (dep.label == (byte)_engine->scene->_timers.timeOfDay) {
+                        if (dep.label == (byte)NancySceneState._timers.timeOfDay) {
                             dep.satisfied = true;
                         }
 
                         break;
                     case kTimerNotDone:
-                        if (_engine->scene->_timers.timerTime <= dep.timeData) {
+                        if (NancySceneState._timers.timerTime <= dep.timeData) {
                             dep.satisfied = true;
                         }
 
                         break;
                     case kTimerDone:
-                        if (_engine->scene->_timers.timerTime > dep.timeData) {
+                        if (NancySceneState._timers.timerTime > dep.timeData) {
                             dep.satisfied = true;
                         }
 
                         break;
                     case kDifficultyLevel:
-                        if (dep.condition == _engine->scene->_difficulty) {
+                        if (dep.condition == NancySceneState._difficulty) {
                             dep.satisfied = true;
                         }
 
@@ -432,7 +432,7 @@ void ActionManager::processActionRecords() {
         }
 
         if (record->isActive) {
-            record->execute(_engine);
+            record->execute();
         }
     }
 }
@@ -444,6 +444,14 @@ void ActionManager::clearActionRecords() {
     _records.clear();
 }
 
+void ActionManager::onPause(bool pause) {
+    for (auto &r : _records) {
+        if (r->isActive && !r->isDone) {
+            r->onPause(pause);
+        }
+    }
+}
+
 void ActionManager::synchronize(Common::Serializer &ser) {
     // When loading, the records should already have been initialized by scene
     for (auto &rec : _records) {
diff --git a/engines/nancy/action/actionmanager.h b/engines/nancy/action/actionmanager.h
index dac22f0034..7101ffb6f1 100644
--- a/engines/nancy/action/actionmanager.h
+++ b/engines/nancy/action/actionmanager.h
@@ -50,8 +50,7 @@ class ActionManager {
     friend class Nancy::State::Scene;
 
 public:
-    ActionManager(Nancy::NancyEngine* engine) :
-        _engine(engine) {}
+    ActionManager() {}
     virtual ~ActionManager() {}
 
     void handleInput(NancyInput &input);
@@ -62,12 +61,13 @@ public:
     ActionRecord *getActionRecord(uint id) { if (id < _records.size()) return _records[id]; else return nullptr;}
     void clearActionRecords();
 
+    void onPause(bool pause);
+
     void synchronize(Common::Serializer &serializer);
 
 protected:
     virtual ActionRecord *createActionRecord(uint16 type);
 
-    Nancy::NancyEngine *_engine;
     Common::Array<ActionRecord *> _records;
 };
 
diff --git a/engines/nancy/action/actionrecord.h b/engines/nancy/action/actionrecord.h
index 2aaf40242e..3c88bb7a82 100644
--- a/engines/nancy/action/actionrecord.h
+++ b/engines/nancy/action/actionrecord.h
@@ -94,7 +94,8 @@ public:
     virtual ~ActionRecord() {}
 
     virtual uint16 readData(Common::SeekableReadStream &stream) =0;
-    virtual void execute(NancyEngine *engine) {};
+    virtual void execute() {}
+    virtual void onPause(bool pause) {}
 
     virtual CursorManager::CursorType getHoverCursor() const { return CursorManager::kHotspot; }
     virtual void handleInput(NancyInput &input) {}
diff --git a/engines/nancy/action/arfactory_v1.cpp b/engines/nancy/action/arfactory_v1.cpp
index 7ba3fa6a58..d5afa51d40 100644
--- a/engines/nancy/action/arfactory_v1.cpp
+++ b/engines/nancy/action/arfactory_v1.cpp
@@ -62,17 +62,17 @@ ActionRecord *ActionManager::createActionRecord(uint16 type) {
     case 0x15:
         return new StartStopPlayerScrolling(); // TODO
     case 0x28:
-        return new PlayPrimaryVideoChan0(_engine->scene->getViewport());
+        return new PlayPrimaryVideoChan0(NancySceneState.getViewport());
     case 0x29:
-        return new PlaySecondaryVideo('0', _engine->scene->getViewport());
+        return new PlaySecondaryVideo('0', NancySceneState.getViewport());
     case 0x2A:
-        return new PlaySecondaryVideo('1', _engine->scene->getViewport());
+        return new PlaySecondaryVideo('1', NancySceneState.getViewport());
     case 0x2B:
-        return new PlaySecondaryMovie(_engine->scene->getViewport());
+        return new PlaySecondaryMovie(NancySceneState.getViewport());
     case 0x2C:
-        return new PlayStaticBitmapAnimation(false, _engine->scene->getViewport()); // PlayStaticBitmapAnimation
+        return new PlayStaticBitmapAnimation(false, NancySceneState.getViewport()); // PlayStaticBitmapAnimation
     case 0x2D:
-        return new PlayStaticBitmapAnimation(true, _engine->scene->getViewport()); // PlayIntStaticBitmapAnimation
+        return new PlayStaticBitmapAnimation(true, NancySceneState.getViewport()); // PlayIntStaticBitmapAnimation
     case 0x32:
         return new MapCall();
     case 0x33:
@@ -108,7 +108,7 @@ ActionRecord *ActionManager::createActionRecord(uint16 type) {
     case 0x61:
         return new EventFlags();
     case 0x62:
-        return new OrderingPuzzle(_engine->scene->getViewport());
+        return new OrderingPuzzle(NancySceneState.getViewport());
     case 0x63:
         return new LoseGame();
     case 0x64:
@@ -120,21 +120,21 @@ ActionRecord *ActionManager::createActionRecord(uint16 type) {
     case 0x67:
         return new DifficultyLevel();
     case 0x68:
-        return new RotatingLockPuzzle(_engine->scene->getViewport());
+        return new RotatingLockPuzzle(NancySceneState.getViewport());
     case 0x69:
-        return new LeverPuzzle(_engine->scene->getViewport());
+        return new LeverPuzzle(NancySceneState.getViewport());
     case 0x6A:
-        return new Telephone(_engine->scene->getViewport());
+        return new Telephone(NancySceneState.getViewport());
     case 0x6B:
-        return new SliderPuzzle(_engine->scene->getViewport());
+        return new SliderPuzzle(NancySceneState.getViewport());
     case 0x6C:
-        return new PasswordPuzzle(_engine->scene->getViewport());
+        return new PasswordPuzzle(NancySceneState.getViewport());
     case 0x6E:
         return new AddInventoryNoHS();
     case 0x6F:
         return new RemoveInventoryNoHS();
     case 0x70:
-        return new ShowInventoryItem(_engine->scene->getViewport());
+        return new ShowInventoryItem(NancySceneState.getViewport());
     case 0x8C:
         return new PlayDigiSoundAndDie(); // TODO
     case 0x8D:
diff --git a/engines/nancy/action/leverpuzzle.cpp b/engines/nancy/action/leverpuzzle.cpp
index 8bc6e30c22..e2ac0197b4 100644
--- a/engines/nancy/action/leverpuzzle.cpp
+++ b/engines/nancy/action/leverpuzzle.cpp
@@ -37,7 +37,7 @@ void LeverPuzzle::init() {
     _drawSurface.clear(GraphicsManager::transColor);
 
     Graphics::Surface surf;
-    _engine->_res->loadImage("ciftree", imageName, surf);
+    NanEngine.resource->loadImage("ciftree", imageName, surf);
     image.create(surf.w, surf.h, surf.format);
     image.blitFrom(surf);
     surf.free();
@@ -93,13 +93,13 @@ uint16 LeverPuzzle::readData(Common::SeekableReadStream &stream) {
     return 0x192;
 }
 
-void LeverPuzzle::execute(Nancy::NancyEngine *engine) {
+void LeverPuzzle::execute() {
     switch (state) {
     case kBegin:
         init();
         registerGraphics();
-        engine->sound->loadSound(moveSound);
-        engine->sound->loadSound(noMoveSound);
+        NanEngine.sound->loadSound(moveSound);
+        NanEngine.sound->loadSound(noMoveSound);
 
         for (uint i = 0; i < 3; ++i) {
             drawLever(i);
@@ -116,22 +116,22 @@ void LeverPuzzle::execute(Nancy::NancyEngine *engine) {
                 }
             }
             
-            engine->scene->setEventFlag(flagOnSolve);
-            solveSoundPlayTime = _engine->getTotalPlayTime() + solveSoundDelay * 1000;
+            NancySceneState.setEventFlag(flagOnSolve);
+            solveSoundPlayTime = NanEngine.getTotalPlayTime() + solveSoundDelay * 1000;
             solveState = kPlaySound;
             break;
         case kPlaySound:
-            if (_engine->getTotalPlayTime() <= solveSoundPlayTime) {
+            if (NanEngine.getTotalPlayTime() <= solveSoundPlayTime) {
                 break;
             }
 
-            engine->sound->loadSound(solveSound);
-            _engine->sound->playSound(solveSound);
+            NanEngine.sound->loadSound(solveSound);
+            NanEngine.sound->playSound(solveSound);
             solveState = kWaitForSound;
             break;
         case kWaitForSound:
-            if (!_engine->sound->isSoundPlaying(solveSound)) {
-                _engine->sound->stopSound(solveSound);
+            if (!NanEngine.sound->isSoundPlaying(solveSound)) {
+                NanEngine.sound->stopSound(solveSound);
                 state = kActionTrigger;
             }
 
@@ -140,14 +140,14 @@ void LeverPuzzle::execute(Nancy::NancyEngine *engine) {
 
         break;
     case kActionTrigger:
-        _engine->sound->stopSound(moveSound);
-        _engine->sound->stopSound(noMoveSound);
+        NanEngine.sound->stopSound(moveSound);
+        NanEngine.sound->stopSound(noMoveSound);
         
         if (solveState == kNotSolved) {
-            _engine->scene->changeScene(exitScene);
-            _engine->scene->setEventFlag(flagOnExit);
+            NancySceneState.changeScene(exitScene);
+            NancySceneState.setEventFlag(flagOnExit);
         } else {
-            _engine->scene->changeScene(solveExitScene);
+            NancySceneState.changeScene(solveExitScene);
         }
 
         finishExecution();
@@ -159,8 +159,8 @@ void LeverPuzzle::handleInput(NancyInput &input) {
         return;
     }
 
-    if (_engine->scene->getViewport().convertViewportToScreen(exitHotspot).contains(input.mousePos)) {
-        _engine->cursorManager->setCursorType(CursorManager::kExitArrow);
+    if (NancySceneState.getViewport().convertViewportToScreen(exitHotspot).contains(input.mousePos)) {
+        NanEngine.cursorManager->setCursorType(CursorManager::kExitArrow);
 
         if (input.input & NancyInput::kLeftMouseButtonUp) {
             state = kActionTrigger;
@@ -169,8 +169,8 @@ void LeverPuzzle::handleInput(NancyInput &input) {
     }
 
     for (uint i = 0; i < 3; ++i) {
-        if (_engine->scene->getViewport().convertViewportToScreen(destRects[i]).contains(input.mousePos)) {
-            _engine->cursorManager->setCursorType(CursorManager::kHotspot);
+        if (NancySceneState.getViewport().convertViewportToScreen(destRects[i]).contains(input.mousePos)) {
+            NanEngine.cursorManager->setCursorType(CursorManager::kHotspot);
             
             if (input.input & NancyInput::kLeftMouseButtonUp) {
                 bool isMoving = false;
@@ -194,7 +194,7 @@ void LeverPuzzle::handleInput(NancyInput &input) {
                 }
 
                 if (isMoving) {
-                    _engine->sound->playSound(moveSound);
+                    NanEngine.sound->playSound(moveSound);
 
                     if (leverDirection[i]) {
                         // Moving down
@@ -216,7 +216,7 @@ void LeverPuzzle::handleInput(NancyInput &input) {
 
                     drawLever(i);
                 } else {
-                    _engine->sound->playSound(noMoveSound);
+                    NanEngine.sound->playSound(noMoveSound);
                     return;
                 }
             }
@@ -224,6 +224,12 @@ void LeverPuzzle::handleInput(NancyInput &input) {
     }
 }
 
+void LeverPuzzle::onPause(bool pause) {
+    if (pause) {
+        registerGraphics();
+    }
+}
+
 void LeverPuzzle::drawLever(uint id) {
     Common::Point destPoint(destRects[id].left - _screenPosition.left, destRects[id].top - _screenPosition.top);
     _drawSurface.blitFrom(image, srcRects[id][playerSequence[id]], destPoint);
diff --git a/engines/nancy/action/leverpuzzle.h b/engines/nancy/action/leverpuzzle.h
index ca6798dcd1..5b35d9b633 100644
--- a/engines/nancy/action/leverpuzzle.h
+++ b/engines/nancy/action/leverpuzzle.h
@@ -45,8 +45,9 @@ public:
     virtual void init() override;
     
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
-    virtual void execute(Nancy::NancyEngine *engine) override;
+    virtual void execute() override;
     virtual void handleInput(NancyInput &input) override;
+    virtual void onPause(bool pause) override;
 
     Common::String imageName; // 0x0
     Common::Array<Common::Array<Common::Rect>> srcRects; // 0xA, 0xC0 bytes
diff --git a/engines/nancy/action/orderingpuzzle.cpp b/engines/nancy/action/orderingpuzzle.cpp
index 1351351217..e669066a01 100644
--- a/engines/nancy/action/orderingpuzzle.cpp
+++ b/engines/nancy/action/orderingpuzzle.cpp
@@ -44,7 +44,7 @@ void OrderingPuzzle::init() {
     clearAllElements();
 
     Graphics::Surface surf;
-    _engine->_res->loadImage("ciftree", imageName, surf);
+    NanEngine.resource->loadImage("ciftree", imageName, surf);
     image.create(surf.w, surf.h, surf.format);
     image.blitFrom(surf);
     surf.free();
@@ -105,13 +105,13 @@ uint16 OrderingPuzzle::readData(Common::SeekableReadStream &stream) {
     return 0x26D;
 }
 
-void OrderingPuzzle::execute(Nancy::NancyEngine *engine) {
+void OrderingPuzzle::execute() {
     switch (state) {
     case kBegin:
         init();
         registerGraphics();
-        _engine->sound->loadSound(clickSound);
-        _engine->sound->loadSound(solveSound);
+        NanEngine.sound->loadSound(clickSound);
+        NanEngine.sound->loadSound(solveSound);
         state = kRun;
         // fall through
     case kRun:
@@ -127,20 +127,20 @@ void OrderingPuzzle::execute(Nancy::NancyEngine *engine) {
                 }
             }
 
-            _engine->scene->setEventFlag(flagOnSolve);
-            solveSoundPlayTime = _engine->getTotalPlayTime() + solveSoundDelay * 1000;
+            NancySceneState.setEventFlag(flagOnSolve);
+            solveSoundPlayTime = NanEngine.getTotalPlayTime() + solveSoundDelay * 1000;
             solveState = kPlaySound;
             // fall through
         case kPlaySound:
-            if (_engine->getTotalPlayTime() <= solveSoundPlayTime) {
+            if (NanEngine.getTotalPlayTime() <= solveSoundPlayTime) {
                 break;
             }
 
-            _engine->sound->playSound(solveSound);
+            NanEngine.sound->playSound(solveSound);
             solveState = kWaitForSound;
             break;
         case kWaitForSound:
-            if (!_engine->sound->isSoundPlaying(solveSound)) {
+            if (!NanEngine.sound->isSoundPlaying(solveSound)) {
                 state = kActionTrigger;
             }
 
@@ -148,14 +148,14 @@ void OrderingPuzzle::execute(Nancy::NancyEngine *engine) {
         }
         break;
     case kActionTrigger:
-        _engine->sound->stopSound(clickSound);
-        _engine->sound->stopSound(solveSound);
+        NanEngine.sound->stopSound(clickSound);
+        NanEngine.sound->stopSound(solveSound);
 
         if (solveState == kNotSolved) {
-            _engine->scene->changeScene(exitScene);
-            _engine->scene->setEventFlag(flagOnExit);
+            NancySceneState.changeScene(exitScene);
+            NancySceneState.setEventFlag(flagOnExit);
         } else {
-            _engine->scene->changeScene(solveExitScene);
+            NancySceneState.changeScene(solveExitScene);
         }
 
         finishExecution();
@@ -168,8 +168,8 @@ void OrderingPuzzle::handleInput(NancyInput &input) {
         return;
     }
 
-    if (_engine->scene->getViewport().convertViewportToScreen(exitHotspot).contains(input.mousePos)) {
-        _engine->cursorManager->setCursorType(CursorManager::kExitArrow);
+    if (NancySceneState.getViewport().convertViewportToScreen(exitHotspot).contains(input.mousePos)) {
+        NanEngine.cursorManager->setCursorType(CursorManager::kExitArrow);
 
         if (input.input & NancyInput::kLeftMouseButtonUp) {
             state = kActionTrigger;
@@ -178,11 +178,11 @@ void OrderingPuzzle::handleInput(NancyInput &input) {
     }
 
     for (int i = 0; i < (int)destRects.size(); ++i) {
-        if (_engine->scene->getViewport().convertViewportToScreen(destRects[i]).contains(input.mousePos)) {
-            _engine->cursorManager->setCursorType(CursorManager::kHotspot);
+        if (NancySceneState.getViewport().convertViewportToScreen(destRects[i]).contains(input.mousePos)) {
+            NanEngine.cursorManager->setCursorType(CursorManager::kHotspot);
 
             if (input.input & NancyInput::kLeftMouseButtonUp) {
-                _engine->sound->playSound(clickSound);
+                NanEngine.sound->playSound(clickSound);
                 
                 for (uint j = 0; j < clickedSequence.size(); ++j) {
                     if (clickedSequence[j] == i && drawnElements[i] == true) {
@@ -208,6 +208,12 @@ void OrderingPuzzle::handleInput(NancyInput &input) {
     }
 }
 
+void OrderingPuzzle::onPause(bool pause) {
+    if (pause) {
+        registerGraphics();
+    }
+}
+
 void OrderingPuzzle::drawElement(uint id) {
     drawnElements[id] = true;
     Common::Point destPoint(destRects[id].left - _screenPosition.left, destRects[id].top - _screenPosition.top);
@@ -225,7 +231,7 @@ void OrderingPuzzle::undrawElement(uint id) {
 }
 
 void OrderingPuzzle::clearAllElements() {
-    _drawSurface.clear(_engine->graphicsManager->transColor);
+    _drawSurface.clear(NanEngine.graphicsManager->transColor);
     setVisible(false);
     clickedSequence.clear();
     return;
diff --git a/engines/nancy/action/orderingpuzzle.h b/engines/nancy/action/orderingpuzzle.h
index 4c819632a8..40d90a8a7a 100644
--- a/engines/nancy/action/orderingpuzzle.h
+++ b/engines/nancy/action/orderingpuzzle.h
@@ -46,8 +46,9 @@ public:
     virtual void init() override;
     
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
-    virtual void execute(Nancy::NancyEngine *engine) override;
+    virtual void execute() override;
     virtual void handleInput(NancyInput &input) override;
+    virtual void onPause(bool pause) override;
 
     Common::String imageName; // 0x00
     Common::Array<Common::Rect> srcRects; // 0xC, 15
diff --git a/engines/nancy/action/passwordpuzzle.cpp b/engines/nancy/action/passwordpuzzle.cpp
index 64eea71e90..0e3ef1ad79 100644
--- a/engines/nancy/action/passwordpuzzle.cpp
+++ b/engines/nancy/action/passwordpuzzle.cpp
@@ -72,12 +72,12 @@ uint16 PasswordPuzzle::readData(Common::SeekableReadStream &stream) {
     return 0xD7;
 }
 
-void PasswordPuzzle::execute(Nancy::NancyEngine *engine) {
+void PasswordPuzzle::execute() {
     switch (state) {
     case kBegin:
         init();
         registerGraphics();
-        nextBlinkTime = engine->getTotalPlayTime() + cursorBlinkTime;
+        nextBlinkTime = NanEngine.getTotalPlayTime() + cursorBlinkTime;
         state = kRun;
         // fall through
     case kRun:
@@ -85,7 +85,7 @@ void PasswordPuzzle::execute(Nancy::NancyEngine *engine) {
         case kNotSolved: {
             Common::String &activeField = passwordFieldIsActive ? playerPasswordInput : playerNameInput;
             Common::String &correctField = passwordFieldIsActive ? password : name;
-            Time currentTime = engine->getTotalPlayTime();
+            Time currentTime = NanEngine.getTotalPlayTime();
 
             if (playerHasHitReturn) {
                 playerHasHitReturn = false;
@@ -99,13 +99,13 @@ void PasswordPuzzle::execute(Nancy::NancyEngine *engine) {
                     if (!passwordFieldIsActive) {
                         passwordFieldIsActive = true;
                     } else {
-                        engine->sound->loadSound(solveSound);
-                        engine->sound->playSound(solveSound);
+                        NanEngine.sound->loadSound(solveSound);
+                        NanEngine.sound->playSound(solveSound);
                         solveState = kSolved;
                     }
                 } else {
-                    engine->sound->loadSound(failSound);
-                    engine->sound->playSound(failSound);
+                    NanEngine.sound->loadSound(failSound);
+                    NanEngine.sound->playSound(failSound);
                     solveState = kFailed;
                 }
                 
@@ -125,15 +125,15 @@ void PasswordPuzzle::execute(Nancy::NancyEngine *engine) {
             break;
         }
         case kFailed:
-            if (!engine->sound->isSoundPlaying(failSound)) {
-                engine->sound->stopSound(failSound);
+            if (!NanEngine.sound->isSoundPlaying(failSound)) {
+                NanEngine.sound->stopSound(failSound);
                 state = kActionTrigger;
             }
 
             break;
         case kSolved:
-            if (!engine->sound->isSoundPlaying(solveSound)) {
-                engine->sound->stopSound(solveSound);
+            if (!NanEngine.sound->isSoundPlaying(solveSound)) {
+                NanEngine.sound->stopSound(solveSound);
                 state = kActionTrigger;
             }
 
@@ -144,16 +144,16 @@ void PasswordPuzzle::execute(Nancy::NancyEngine *engine) {
     case kActionTrigger:
         switch (solveState) {
         case kNotSolved:
-            engine->scene->changeScene(exitScene);
-            engine->scene->setEventFlag(flagOnExit);
+            NancySceneState.changeScene(exitScene);
+            NancySceneState.setEventFlag(flagOnExit);
             break;
         case kFailed:
-            engine->scene->changeScene(failExitScene);
-            engine->scene->setEventFlag(flagOnFail.label);
+            NancySceneState.changeScene(failExitScene);
+            NancySceneState.setEventFlag(flagOnFail.label);
             break;
         case kSolved:
-            engine->scene->changeScene(solveExitScene);
-            engine->scene->setEventFlag(flagOnSolve.label);
+            NancySceneState.changeScene(solveExitScene);
+            NancySceneState.setEventFlag(flagOnSolve.label);
             break;
         }
 
@@ -166,8 +166,8 @@ void PasswordPuzzle::handleInput(NancyInput &input) {
         return;
     }
 
-    if (_engine->scene->getViewport().convertViewportToScreen(exitHotspot).contains(input.mousePos)) {
-        _engine->cursorManager->setCursorType(CursorManager::kExitArrow);
+    if (NancySceneState.getViewport().convertViewportToScreen(exitHotspot).contains(input.mousePos)) {
+        NanEngine.cursorManager->setCursorType(CursorManager::kExitArrow);
 
         if (input.input & NancyInput::kLeftMouseButtonUp) {
             state = kActionTrigger;
@@ -210,18 +210,24 @@ void PasswordPuzzle::handleInput(NancyInput &input) {
     }
 }
 
+void PasswordPuzzle::onPause(bool pause) {
+    if (pause) {
+        registerGraphics();
+    }
+}
+
 void PasswordPuzzle::drawText() {
     _drawSurface.clear(GraphicsManager::transColor);
-    Graphics::Font *font = _engine->graphicsManager->getFont(fontID);
+    Graphics::Font *font = NanEngine.graphicsManager->getFont(fontID);
 
     Common::Rect bounds = nameBounds;
-    bounds = _engine->scene->getViewport().convertViewportToScreen(bounds);
+    bounds = NancySceneState.getViewport().convertViewportToScreen(bounds);
     bounds = convertToLocal(bounds);
     Common::Point destPoint(bounds.left, bounds.bottom + 1 - font->getFontHeight());
     font->drawString(&_drawSurface, playerNameInput, destPoint.x, destPoint.y, bounds.width(), 0);
 
     bounds = passwordBounds;
-    bounds = _engine->scene->getViewport().convertViewportToScreen(bounds);
+    bounds = NancySceneState.getViewport().convertViewportToScreen(bounds);
     bounds = convertToLocal(bounds);
     destPoint.x = bounds.left;
     destPoint.y = bounds.bottom + 1 - font->getFontHeight();
diff --git a/engines/nancy/action/passwordpuzzle.h b/engines/nancy/action/passwordpuzzle.h
index 4901e58a44..8ed0132cbc 100644
--- a/engines/nancy/action/passwordpuzzle.h
+++ b/engines/nancy/action/passwordpuzzle.h
@@ -48,8 +48,9 @@ public:
     virtual void init() override;
     
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
-    virtual void execute(Nancy::NancyEngine *engine) override;
+    virtual void execute() override;
     virtual void handleInput(NancyInput &input) override;
+    virtual void onPause(bool pause) override;
 
     uint16 fontID; // 0x00
     Time cursorBlinkTime; // 0x2
diff --git a/engines/nancy/action/primaryvideo.cpp b/engines/nancy/action/primaryvideo.cpp
index fa79ce8226..58b8a6836f 100644
--- a/engines/nancy/action/primaryvideo.cpp
+++ b/engines/nancy/action/primaryvideo.cpp
@@ -46,27 +46,27 @@ void PlayPrimaryVideoChan0::ConditionFlag::read(Common::SeekableReadStream &stre
     orFlag = stream.readByte();
 }
 
-bool PlayPrimaryVideoChan0::ConditionFlag::isSatisfied(NancyEngine *engine) const {
+bool PlayPrimaryVideoChan0::ConditionFlag::isSatisfied() const {
     switch (type) {
     case ConditionFlag::kEventFlags:
-        return engine->scene->getEventFlag(flag);
+        return NancySceneState.getEventFlag(flag);
     case ConditionFlag::kInventory:
-        return engine->scene->hasItem(flag.label) == flag.flag;
+        return NancySceneState.hasItem(flag.label) == flag.flag;
     default:
         return false;
     }
 }
 
-void PlayPrimaryVideoChan0::ConditionFlag::set(NancyEngine *engine) const {
+void PlayPrimaryVideoChan0::ConditionFlag::set() const {
     switch (type) {
     case ConditionFlag::kEventFlags:
-        engine->scene->setEventFlag(flag);
+        NancySceneState.setEventFlag(flag);
         break;
     case ConditionFlag::kInventory:
         if (flag.flag == kTrue) {
-            engine->scene->addItemToInventory(flag.label);
+            NancySceneState.addItemToInventory(flag.label);
         } else {
-            engine->scene->removeItemFromInventory(flag.label);
+            NancySceneState.removeItemFromInventory(flag.label);
         }
 
         break;
@@ -84,13 +84,13 @@ void PlayPrimaryVideoChan0::ConditionFlags::read(Common::SeekableReadStream &str
     }
 }
 
-bool PlayPrimaryVideoChan0::ConditionFlags::isSatisfied(NancyEngine *engine) const {
+bool PlayPrimaryVideoChan0::ConditionFlags::isSatisfied() const {
     bool orFlag = false;
 
     for (uint i = 0; i < conditionFlags.size(); ++i) {
         const ConditionFlag &cur = conditionFlags[i];
         
-        if (!cur.isSatisfied(engine)) {
+        if (!cur.isSatisfied()) {
             if (orFlag) {
                 return false;
             } else {
@@ -108,10 +108,13 @@ bool PlayPrimaryVideoChan0::ConditionFlags::isSatisfied(NancyEngine *engine) con
 
 PlayPrimaryVideoChan0::~PlayPrimaryVideoChan0() {
     _decoder.close();
+
 	if (activePrimaryVideo == this) {
 		activePrimaryVideo = nullptr;
 	}
-    _engine->scene->getTextbox().setVisible(false);
+    
+    NancySceneState.setShouldClearTextbox(true);
+    NancySceneState.getTextbox().setVisible(false);
 }
 
 void PlayPrimaryVideoChan0::init() {
@@ -119,6 +122,8 @@ void PlayPrimaryVideoChan0::init() {
     _drawSurface.create(src.width(), src.height(), _decoder.getPixelFormat());
 
     RenderObject::init();
+    
+    NancySceneState.setShouldClearTextbox(false);
 }
 
 void PlayPrimaryVideoChan0::updateGraphics() {
@@ -140,6 +145,10 @@ void PlayPrimaryVideoChan0::updateGraphics() {
 
 void PlayPrimaryVideoChan0::onPause(bool pause) {
     _decoder.pauseVideo(pause);
+
+    if (pause) {
+        registerGraphics();
+    }
 }
 
 uint16 PlayPrimaryVideoChan0::readData(Common::SeekableReadStream &stream) {
@@ -213,7 +222,7 @@ uint16 PlayPrimaryVideoChan0::readData(Common::SeekableReadStream &stream) {
     return bytesRead;
 }
 
-void PlayPrimaryVideoChan0::execute(NancyEngine *engine) {
+void PlayPrimaryVideoChan0::execute() {
 	if (activePrimaryVideo != this && activePrimaryVideo != nullptr) {
         return;
     }
@@ -222,37 +231,37 @@ void PlayPrimaryVideoChan0::execute(NancyEngine *engine) {
     case kBegin:
         init();
         registerGraphics();
-        engine->sound->loadSound(sound);
-        engine->sound->playSound(sound);
+        NanEngine.sound->loadSound(sound);
+        NanEngine.sound->playSound(sound);
         state = kRun;
         activePrimaryVideo = this;
         // fall through
     case kRun:
         if (!hasDrawnTextbox) {
             hasDrawnTextbox = true;
-            engine->scene->getTextbox().clear();
-            engine->scene->getTextbox().addTextLine(text);
+            NancySceneState.getTextbox().clear();
+            NancySceneState.getTextbox().addTextLine(text);
 
             // Add responses when conditions have been satisfied
             if (conditionalResponseCharacterID != 10) {
-                addConditionalResponses(engine);
+                addConditionalResponses();
             }
 
             if (goodbyeResponseCharacterID != 10) {
-                addGoodbye(engine);
+                addGoodbye();
             }
 
             for (uint i = 0; i < responses.size(); ++i) {
                 auto &res = responses[i];
 
-                if (res.conditionFlags.isSatisfied(engine)) {
-                    engine->scene->getTextbox().addTextLine(res.text);
+                if (res.conditionFlags.isSatisfied()) {
+                    NancySceneState.getTextbox().addTextLine(res.text);
                 }
             }
         }
 
-        if (!engine->sound->isSoundPlaying(sound)) {
-            engine->sound->stopSound(sound);
+        if (!NanEngine.sound->isSoundPlaying(sound)) {
+            NanEngine.sound->stopSound(sound);
             
             if (responses.size() == 0) {
                 // NPC has finished talking with no responses available, auto-advance to next scene
@@ -260,7 +269,7 @@ void PlayPrimaryVideoChan0::execute(NancyEngine *engine) {
             } else {
                 // NPC has finished talking, we have responses
                 for (uint i = 0; i < 30; ++i) {
-                    if (engine->scene->getLogicCondition(i, kTrue)) {
+                    if (NancySceneState.getLogicCondition(i, kTrue)) {
                         pickedResponse = i;
                         break;
                     }
@@ -270,8 +279,8 @@ void PlayPrimaryVideoChan0::execute(NancyEngine *engine) {
                     // Player has picked response, play sound file and change state
                     responseGenericSound.name = responses[pickedResponse].soundName;
                     // TODO this is probably not correct
-                    engine->sound->loadSound(responseGenericSound);
-                    engine->sound->playSound(responseGenericSound);
+                    NanEngine.sound->loadSound(responseGenericSound);
+                    NanEngine.sound->playSound(responseGenericSound);
                     state = kActionTrigger;
                 }
             }
@@ -280,29 +289,29 @@ void PlayPrimaryVideoChan0::execute(NancyEngine *engine) {
     case kActionTrigger:
         // process flags structs
         for (auto flags : flagsStructs) {
-            if (flags.conditions.isSatisfied(engine)) {
-                flags.flagToSet.set(engine);
+            if (flags.conditions.isSatisfied()) {
+                flags.flagToSet.set();
             }
         }
         
         if (pickedResponse != -1) {
             // Set response's event flag, if any
-            engine->scene->setEventFlag(responses[pickedResponse].flagDesc);
+            NancySceneState.setEventFlag(responses[pickedResponse].flagDesc);
         }
 
-        if (!engine->sound->isSoundPlaying(responseGenericSound)) {
-            engine->sound->stopSound(responseGenericSound);
+        if (!NanEngine.sound->isSoundPlaying(responseGenericSound)) {
+            NanEngine.sound->stopSound(responseGenericSound);
             
             if (pickedResponse != -1) {
-                engine->scene->changeScene(responses[pickedResponse].sceneChange);
+                NancySceneState.changeScene(responses[pickedResponse].sceneChange);
             } else {
                 // Evaluate scene branch structs here
 
                 if (isDialogueExitScene == kFalse) {
-                    engine->scene->changeScene(sceneChange);
+                    NancySceneState.changeScene(sceneChange);
                 } else if (doNotPop == kFalse) {
                     // Exit dialogue
-                    engine->scene->popScene();
+                    NancySceneState.popScene();
                 }
             }
             
@@ -313,7 +322,7 @@ void PlayPrimaryVideoChan0::execute(NancyEngine *engine) {
     }
 }
 
-void PlayPrimaryVideoChan0::addConditionalResponses(NancyEngine *engine) {
+void PlayPrimaryVideoChan0::addConditionalResponses() {
     for (auto &res : nancy1ConditionalResponses) {
         if (res.characterID == conditionalResponseCharacterID) {
             bool isSatisfied = true;
@@ -322,7 +331,7 @@ void PlayPrimaryVideoChan0::addConditionalResponses(NancyEngine *engine) {
                     break;
                 }
 
-                if (!engine->scene->getEventFlag(cond.label, cond.flag)) {
+                if (!NancySceneState.getEventFlag(cond.label, cond.flag)) {
                     isSatisfied = false;
                     break;
                 }
@@ -349,7 +358,7 @@ void PlayPrimaryVideoChan0::addConditionalResponses(NancyEngine *engine) {
     }
 }
 
-void PlayPrimaryVideoChan0::addGoodbye(NancyEngine *engine) {
+void PlayPrimaryVideoChan0::addGoodbye() {
     for (auto &res : nancy1Goodbyes) {
         if (res.characterID == goodbyeResponseCharacterID) {
             Common::File file;
@@ -364,7 +373,7 @@ void PlayPrimaryVideoChan0::addGoodbye(NancyEngine *engine) {
             newResponse.soundName = snd;
             newResponse.text = file.readString();
             // response is picked randomly
-            newResponse.sceneChange.sceneID = res.sceneIDs[engine->_rnd->getRandomNumber(3)];
+            newResponse.sceneChange.sceneID = res.sceneIDs[NanEngine._rnd->getRandomNumber(3)];
             newResponse.sceneChange.doNotStartSound = true;
 
             file.close();
diff --git a/engines/nancy/action/primaryvideo.h b/engines/nancy/action/primaryvideo.h
index aa75611ab3..3bf8be6c91 100644
--- a/engines/nancy/action/primaryvideo.h
+++ b/engines/nancy/action/primaryvideo.h
@@ -49,15 +49,15 @@ enum ConditionType : byte { kNone = 0, kEventFlags = 1, kInventory = 2 };
     bool orFlag;
 
     void read(Common::SeekableReadStream &stream);
-    bool isSatisfied(NancyEngine *engine) const;
-    void set(NancyEngine *engine) const;
+    bool isSatisfied() const;
+    void set() const;
 };
 
 struct ConditionFlags {
     Common::Array<ConditionFlag> conditionFlags;
 
     void read(Common::SeekableReadStream &stream);
-    bool isSatisfied(NancyEngine *engine) const;
+    bool isSatisfied() const;
 };
 
 struct ResponseStruct {
@@ -82,11 +82,11 @@ public:
     virtual void onPause(bool pause) override;
 
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
-    virtual void execute(NancyEngine *engine) override;
+    virtual void execute() override;
     
     // Functions for handling the built-in dialogue responses found in the executable
-    void addConditionalResponses(NancyEngine *engine);
-    void addGoodbye(NancyEngine *engine);
+    void addConditionalResponses();
+    void addGoodbye();
 
     Common::String videoName; // 0x00
     Common::Rect src; // 0x1D
diff --git a/engines/nancy/action/recordtypes.cpp b/engines/nancy/action/recordtypes.cpp
index 31cda8241d..9719d49df6 100644
--- a/engines/nancy/action/recordtypes.cpp
+++ b/engines/nancy/action/recordtypes.cpp
@@ -45,8 +45,8 @@ uint16 SceneChange::readData(Common::SeekableReadStream &stream) {
     return 8;
 }
 
-void SceneChange::execute(NancyEngine *engine) {
-    engine->scene->changeScene(sceneChange);
+void SceneChange::execute() {
+    NancySceneState.changeScene(sceneChange);
     isDone = true;
 }
 
@@ -63,7 +63,7 @@ uint16 HotMultiframeSceneChange::readData(Common::SeekableReadStream &stream) {
     return ret + (numHotspots * 0x12) + 2;
 }
 
-void HotMultiframeSceneChange::execute(NancyEngine *engine) {
+void HotMultiframeSceneChange::execute() {
     switch (state) {
     case kBegin:
         // turn main rendering on
@@ -72,14 +72,14 @@ void HotMultiframeSceneChange::execute(NancyEngine *engine) {
     case kRun:
         hasHotspot = false;
         for (uint i = 0; i < hotspots.size(); ++i) {
-            if (hotspots[i].frameID == engine->scene->getSceneInfo().frameID) {
+            if (hotspots[i].frameID == NancySceneState.getSceneInfo().frameID) {
                 hasHotspot = true;
                 hotspot = hotspots[i].coords;
             }
         }
         break;
     case kActionTrigger:
-        SceneChange::execute(engine);
+        SceneChange::execute();
         break;
     }
 }
@@ -90,21 +90,21 @@ uint16 Hot1FrSceneChange::readData(Common::SeekableReadStream &stream) {
     return 0x1A;
 }
 
-void Hot1FrSceneChange::execute(NancyEngine *engine) {
+void Hot1FrSceneChange::execute() {
     switch (state) {
     case kBegin:
         hotspot = hotspotDesc.coords;
         state = kRun;
         // fall through
     case kRun:
-        if (hotspotDesc.frameID == engine->scene->getSceneInfo().frameID) {
+        if (hotspotDesc.frameID == NancySceneState.getSceneInfo().frameID) {
             hasHotspot = true;
         } else {
             hasHotspot = false;
         }
         break;
     case kActionTrigger:
-        SceneChange::execute(engine);
+        SceneChange::execute();
         break;
     }
 }
@@ -131,9 +131,9 @@ uint16 MapCall::readData(Common::SeekableReadStream &stream) {
     return 1;
 }
 
-void MapCall::execute(NancyEngine *engine) {
+void MapCall::execute() {
     execType = kRepeating;
-    engine->scene->requestStateChange(NancyEngine::kMap);
+    NancySceneState.requestStateChange(NancyEngine::kMap);
     finishExecution();
 }
 
@@ -142,19 +142,19 @@ uint16 MapCallHot1Fr::readData(Common::SeekableReadStream &stream) {
     return 0x12;
 }
 
-void MapCallHot1Fr::execute(NancyEngine *engine) {
+void MapCallHot1Fr::execute() {
     switch (state) {
     case kBegin:
         hotspot = hotspotDesc.coords;
         state = kRun;
         // fall through
     case kRun:
-        if (hotspotDesc.frameID == engine->scene->getSceneInfo().frameID) {
+        if (hotspotDesc.frameID == NancySceneState.getSceneInfo().frameID) {
             hasHotspot = true;
         }
         break;
     case kActionTrigger:
-        MapCall::execute(engine);
+        MapCall::execute();
         break;
     }
 }
@@ -169,7 +169,7 @@ uint16 MapCallHotMultiframe::readData(Common::SeekableReadStream &stream) {
     return 2 + numDescs * 0x12;
 }
 
-void MapCallHotMultiframe::execute(NancyEngine *engine) {
+void MapCallHotMultiframe::execute() {
     switch (state) {
     case kBegin:
         state = kRun;
@@ -177,14 +177,14 @@ void MapCallHotMultiframe::execute(NancyEngine *engine) {
     case kRun:
         hasHotspot = false;
         for (uint i = 0; i < hotspots.size(); ++i) {
-            if (hotspots[i].frameID == engine->scene->getSceneInfo().frameID) {
+            if (hotspots[i].frameID == NancySceneState.getSceneInfo().frameID) {
                 hasHotspot = true;
                 hotspot = hotspots[i].coords;
             }
         }
         break;
     case kActionTrigger:
-        MapCall::execute(engine);
+        MapCall::execute();
         break;  
     }
 }
@@ -246,8 +246,8 @@ uint16 ResetAndStartTimer::readData(Common::SeekableReadStream &stream) {
     return 1;
 }
 
-void ResetAndStartTimer::execute(NancyEngine *engine) {
-    engine->scene->resetAndStartTimer();
+void ResetAndStartTimer::execute() {
+    NancySceneState.resetAndStartTimer();
     isDone = true;
 }
 
@@ -256,8 +256,8 @@ uint16 StopTimer::readData(Common::SeekableReadStream &stream) {
     return 1;
 }
 
-void StopTimer::execute(NancyEngine *engine) {
-    engine->scene->stopTimer();
+void StopTimer::execute() {
+    NancySceneState.stopTimer();
     isDone = true;
 }
 
@@ -266,8 +266,8 @@ uint16 EventFlags::readData(Common::SeekableReadStream &stream) {
     return 0x28;
 }
 
-void EventFlags::execute(NancyEngine *engine) {
-    flags.execute(engine);
+void EventFlags::execute() {
+    flags.execute();
     isDone = true;
 }
 
@@ -286,7 +286,7 @@ uint16 EventFlagsMultiHS::readData(Common::SeekableReadStream &stream) {
     return returnSize;
 }
 
-void EventFlagsMultiHS::execute(NancyEngine *engine) {
+void EventFlagsMultiHS::execute() {
     switch (state) {
     case kBegin:
         // turn main rendering on
@@ -296,7 +296,7 @@ void EventFlagsMultiHS::execute(NancyEngine *engine) {
         hasHotspot = false;
 
         for (uint i = 0; i < hotspots.size(); ++i) {
-            if (hotspots[i].frameID == engine->scene->getSceneInfo().frameID) {
+            if (hotspots[i].frameID == NancySceneState.getSceneInfo().frameID) {
                 hasHotspot = true;
                 hotspot = hotspots[i].coords;
             }
@@ -305,7 +305,7 @@ void EventFlagsMultiHS::execute(NancyEngine *engine) {
         break;
     case kActionTrigger:
         hasHotspot = false;
-        EventFlags::execute(engine);
+        EventFlags::execute();
         finishExecution();
         break;
     }
@@ -316,10 +316,10 @@ uint16 LoseGame::readData(Common::SeekableReadStream &stream) {
     return 1;
 }
 
-void LoseGame::execute(NancyEngine *engine) {
-    engine->stopAndUnloadSpecificSounds();
-    engine->setState(NancyEngine::kMainMenu);
-    engine->scene->resetStateToInit();
+void LoseGame::execute() {
+    NanEngine.stopAndUnloadSpecificSounds();
+    NanEngine.setState(NancyEngine::kMainMenu);
+    NancySceneState.resetStateToInit();
     isDone = true;
 }
 
@@ -338,10 +338,12 @@ uint16 WinGame::readData(Common::SeekableReadStream &stream) {
     return 1;
 }
 
-void WinGame::execute(NancyEngine *engine) {
-    engine->stopAndUnloadSpecificSounds();
-    engine->setState(NancyEngine::kCredits, NancyEngine::kMainMenu);
-    engine->scene->resetStateToInit();
+void WinGame::execute() {
+    NanEngine.stopAndUnloadSpecificSounds();
+    NanEngine.setState(NancyEngine::kCredits, NancyEngine::kMainMenu);
+    
+    // TODO replace with destroy()?
+    NancySceneState.resetStateToInit();
     isDone = true;
 }
 
@@ -350,9 +352,9 @@ uint16 AddInventoryNoHS::readData(Common::SeekableReadStream &stream) {
     return 2;
 }
 
-void AddInventoryNoHS::execute(Nancy::NancyEngine *engine) {
-    if (engine->scene->hasItem(itemID) == kFalse) {
-        engine->scene->addItemToInventory(itemID);
+void AddInventoryNoHS::execute() {
+    if (NancySceneState.hasItem(itemID) == kFalse) {
+        NancySceneState.addItemToInventory(itemID);
     }
 
     isDone = true;
@@ -369,15 +371,15 @@ uint16 DifficultyLevel::readData(Common::SeekableReadStream &stream) {
     return 6;
 }
 
-void DifficultyLevel::execute(NancyEngine *engine) {
-    engine->scene->setDifficulty(difficulty);
-    engine->scene->setEventFlag(flag);
+void DifficultyLevel::execute() {
+    NancySceneState.setDifficulty(difficulty);
+    NancySceneState.setEventFlag(flag);
     isDone = true;
 }
 
 void ShowInventoryItem::init() {
     Graphics::Surface srcSurf;
-    _engine->_res->loadImage("ciftree", imageName, srcSurf);
+    NanEngine.resource->loadImage("ciftree", imageName, srcSurf);
     _fullSurface.create(srcSurf.w, srcSurf.h, srcSurf.format);
     _fullSurface.blitFrom(srcSurf);
     srcSurf.free();
@@ -403,7 +405,7 @@ uint16 ShowInventoryItem::readData(Common::SeekableReadStream &stream) {
     return 0xE + 0x22 * numFrames;
 }
 
-void ShowInventoryItem::execute(NancyEngine *engine) {
+void ShowInventoryItem::execute() {
     switch (state) {
     case kBegin:
         init();
@@ -414,7 +416,7 @@ void ShowInventoryItem::execute(NancyEngine *engine) {
         int newFrame = -1;
 
         for (uint i = 0; i < bitmaps.size(); ++i) {
-            if (bitmaps[i].frameID == engine->scene->getSceneInfo().frameID) {
+            if (bitmaps[i].frameID == NancySceneState.getSceneInfo().frameID) {
                 newFrame = i;
                 break;
             }
@@ -438,8 +440,8 @@ void ShowInventoryItem::execute(NancyEngine *engine) {
         break;
     }
     case kActionTrigger:
-        engine->sound->playSound(24); // Hardcoded by original engine
-        engine->scene->addItemToInventory(objectID);
+        NanEngine.sound->playSound(24); // Hardcoded by original engine
+        NancySceneState.addItemToInventory(objectID);
         setVisible(false);
         hasHotspot = false;
         finishExecution();
@@ -447,6 +449,12 @@ void ShowInventoryItem::execute(NancyEngine *engine) {
     }
 }
 
+void ShowInventoryItem::onPause(bool pause) {
+    if (pause) {
+        registerGraphics();
+    }
+}
+
 uint16 PlayDigiSoundAndDie::readData(Common::SeekableReadStream &stream) {
     sound.read(stream, SoundDescription::kDIGI);
     sceneChange.readData(stream);
@@ -456,26 +464,26 @@ uint16 PlayDigiSoundAndDie::readData(Common::SeekableReadStream &stream) {
     return 0x2B;
 }
 
-void PlayDigiSoundAndDie::execute(NancyEngine *engine) {
+void PlayDigiSoundAndDie::execute() {
     switch (state) {
     case kBegin:
-        engine->sound->loadSound(sound);
-        engine->sound->playSound(sound);
+        NanEngine.sound->loadSound(sound);
+        NanEngine.sound->playSound(sound);
         state = kRun;
         break;
     case kRun:
-        if (!engine->sound->isSoundPlaying(sound)) {
+        if (!NanEngine.sound->isSoundPlaying(sound)) {
             state = kActionTrigger;
         }
 
         break;
     case kActionTrigger:
         if (sceneChange.sceneID != 9999) {
-            engine->scene->changeScene(sceneChange);
+            NancySceneState.changeScene(sceneChange);
         }
         
-        engine->scene->setEventFlag(flagOnTrigger);
-        engine->sound->stopSound(sound);
+        NancySceneState.setEventFlag(flagOnTrigger);
+        NanEngine.sound->stopSound(sound);
 
         finishExecution();
         break;
@@ -503,14 +511,14 @@ uint16 PlaySoundMultiHS::readData(Common::SeekableReadStream &stream) {
     return 0x31 + numHotspots * 0x12;
 }
 
-void PlaySoundMultiHS::execute(Nancy::NancyEngine *engine) {
+void PlaySoundMultiHS::execute() {
     switch (state) {
     case kBegin:
         state = kRun;
         // fall through
     case kRun: {
         hasHotspot = false;
-        uint currentFrame = engine->scene->getSceneInfo().frameID;
+        uint currentFrame = NancySceneState.getSceneInfo().frameID;
 
         for (uint i = 0; i < hotspots.size(); ++i) {
             if (hotspots[i].frameID == currentFrame) {
@@ -523,10 +531,10 @@ void PlaySoundMultiHS::execute(Nancy::NancyEngine *engine) {
         break;
     }
     case kActionTrigger:
-        engine->sound->loadSound(sound);
-        engine->sound->playSound(sound);
-        engine->scene->changeScene(sceneChange);
-        engine->scene->setEventFlag(flag);
+        NanEngine.sound->loadSound(sound);
+        NanEngine.sound->playSound(sound);
+        NancySceneState.changeScene(sceneChange);
+        NancySceneState.setEventFlag(flag);
         finishExecution();
         break;
     }
@@ -538,25 +546,25 @@ uint16 HintSystem::readData(Common::SeekableReadStream &stream) {
     return 0x23;
 }
 
-void HintSystem::execute(Nancy::NancyEngine *engine) {
+void HintSystem::execute() {
     switch (state) {
     case kBegin:
-        if (engine->scene->getHintsRemaining() > 0) {
-            selectHint(engine);
+        if (NancySceneState.getHintsRemaining() > 0) {
+            selectHint();
         } else {
-            getHint(0, engine->scene->getDifficulty());
+            getHint(0, NancySceneState.getDifficulty());
         }
 
-        engine->scene->getTextbox().clear();
-        engine->scene->getTextbox().addTextLine(text);
+        NancySceneState.getTextbox().clear();
+        NancySceneState.getTextbox().addTextLine(text);
 
-        engine->sound->loadSound(genericSound);
-        engine->sound->playSound(genericSound);
+        NanEngine.sound->loadSound(genericSound);
+        NanEngine.sound->playSound(genericSound);
         state = kRun;
         break;
     case kRun:
-        if (!engine->sound->isSoundPlaying(genericSound)) {
-            engine->sound->stopSound(genericSound);
+        if (!NanEngine.sound->isSoundPlaying(genericSound)) {
+            NanEngine.sound->stopSound(genericSound);
             state = kActionTrigger;
         } else {
             break;
@@ -564,17 +572,17 @@ void HintSystem::execute(Nancy::NancyEngine *engine) {
 
         // fall through
     case kActionTrigger:
-        engine->scene->useHint(hintID, hintWeight);
-        engine->scene->getTextbox().clear();
+        NancySceneState.useHint(hintID, hintWeight);
+        NancySceneState.getTextbox().clear();
 
-        engine->scene->changeScene(sceneChange);
+        NancySceneState.changeScene(sceneChange);
 
         isDone = true;
         break;
     }
 }
 
-void HintSystem::selectHint(Nancy::NancyEngine *engine) {
+void HintSystem::selectHint() {
     for (auto &hint : nancy1Hints) {
         if (hint.characterID != characterID) {
             continue;
@@ -587,7 +595,7 @@ void HintSystem::selectHint(Nancy::NancyEngine *engine) {
                 break;
             }
             
-            if (!engine->scene->getEventFlag(flag.label, flag.flag)) {
+            if (!NancySceneState.getEventFlag(flag.label, flag.flag)) {
                 satisfied = false;
                 break;
             }
@@ -598,14 +606,14 @@ void HintSystem::selectHint(Nancy::NancyEngine *engine) {
                 break;
             }
 
-            if (engine->scene->hasItem(inv.label) != inv.flag) {
+            if (NancySceneState.hasItem(inv.label) != inv.flag) {
                 satisfied = false;
                 break;
             }
         }
 
         if (satisfied) {
-            getHint(hint.hintID, engine->scene->getDifficulty());
+            getHint(hint.hintID, NancySceneState.getDifficulty());
             break;
         }
     }
diff --git a/engines/nancy/action/recordtypes.h b/engines/nancy/action/recordtypes.h
index d85eaa1e9a..8b8bff60ed 100644
--- a/engines/nancy/action/recordtypes.h
+++ b/engines/nancy/action/recordtypes.h
@@ -42,7 +42,7 @@ namespace Action {
 class SceneChange : public ActionRecord {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
-    virtual void execute(Nancy::NancyEngine *engine) override;
+    virtual void execute() override;
 
     SceneChangeDescription sceneChange;
 
@@ -53,7 +53,7 @@ protected:
 class HotMultiframeSceneChange : public SceneChange {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
-    virtual void execute(Nancy::NancyEngine *engine) override;
+    virtual void execute() override;
 
     Common::Array<HotspotDescription> hotspots;
 
@@ -64,7 +64,7 @@ protected:
 class Hot1FrSceneChange : public SceneChange {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
-    virtual void execute(Nancy::NancyEngine *engine) override;
+    virtual void execute() override;
 
     HotspotDescription hotspotDesc;
 
@@ -109,7 +109,7 @@ protected:
 class MapCall : public ActionRecord {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
-    virtual void execute(Nancy::NancyEngine *engine) override;
+    virtual void execute() override;
 
     virtual CursorManager::CursorType getHoverCursor() const override { return CursorManager::kExitArrow; }
 
@@ -120,7 +120,7 @@ protected:
 class MapCallHot1Fr : public MapCall {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
-    virtual void execute(Nancy::NancyEngine *engine) override;
+    virtual void execute() override;
 
     HotspotDescription hotspotDesc;
     
@@ -131,7 +131,7 @@ protected:
 class MapCallHotMultiframe : public MapCall {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
-    virtual void execute(Nancy::NancyEngine *engine) override;
+    virtual void execute() override;
 
     Common::Array<HotspotDescription> hotspots;
     
@@ -232,7 +232,7 @@ protected:
 class ResetAndStartTimer : public ActionRecord {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
-    virtual void execute(Nancy::NancyEngine *engine) override;
+    virtual void execute() override;
     
 protected:
     virtual Common::String getRecordTypeName() const override { return "ResetAndStartTimer"; }
@@ -241,7 +241,7 @@ protected:
 class StopTimer : public ActionRecord {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
-    virtual void execute(Nancy::NancyEngine *engine) override;
+    virtual void execute() override;
     
 protected:
     virtual Common::String getRecordTypeName() const override { return "StopTimer"; }
@@ -250,7 +250,7 @@ protected:
 class EventFlags : public ActionRecord {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
-    virtual void execute(Nancy::NancyEngine *engine) override;
+    virtual void execute() override;
 
     MultiEventFlagDescription flags;
     
@@ -261,7 +261,7 @@ protected:
 class EventFlagsMultiHS : public EventFlags {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
-    virtual void execute(Nancy::NancyEngine *engine) override;
+    virtual void execute() override;
 
     Common::Array<HotspotDescription> hotspots;
     
@@ -272,7 +272,7 @@ protected:
 class LoseGame : public ActionRecord {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
-    virtual void execute(Nancy::NancyEngine *engine) override;
+    virtual void execute() override;
     
 protected:
     virtual Common::String getRecordTypeName() const override { return "LoseGame"; }
@@ -301,7 +301,7 @@ protected:
 class WinGame : public ActionRecord {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
-    virtual void execute(Nancy::NancyEngine *engine) override;
+    virtual void execute() override;
     
 protected:
     virtual Common::String getRecordTypeName() const override { return "WinGame"; }
@@ -310,7 +310,7 @@ protected:
 class AddInventoryNoHS : public ActionRecord {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
-    virtual void execute(Nancy::NancyEngine *engine) override;
+    virtual void execute() override;
     
     uint itemID;
     
@@ -329,7 +329,7 @@ protected:
 class DifficultyLevel : public ActionRecord {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
-    virtual void execute(Nancy::NancyEngine *engine) override;
+    virtual void execute() override;
 
     uint16 difficulty = 0;
     EventFlagDescription flag;
@@ -341,12 +341,13 @@ protected:
 class ShowInventoryItem : public ActionRecord, public RenderObject {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
-    virtual void execute(Nancy::NancyEngine *engine) override;
+    virtual void execute() override;
 
     ShowInventoryItem(RenderObject &redrawFrom) : RenderObject(redrawFrom) {}
     virtual ~ShowInventoryItem() { _fullSurface.free(); }
 
-    virtual void init()override;
+    virtual void init() override;
+    virtual void onPause(bool pause) override;
  
     uint16 objectID = 0;
     Common::String imageName;
@@ -366,7 +367,7 @@ protected:
 class PlayDigiSoundAndDie : public ActionRecord {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
-    virtual void execute(Nancy::NancyEngine *engine) override;
+    virtual void execute() override;
     // TODO subclass into Play and Stop (?)
 
     SoundDescription sound;
@@ -388,7 +389,7 @@ protected:
 class PlaySoundMultiHS : public ActionRecord {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
-    virtual void execute(Nancy::NancyEngine *engine) override;
+    virtual void execute() override;
 
     SoundDescription sound; // 0x0
     SceneChangeDescription sceneChange; // 0x22
@@ -402,7 +403,7 @@ protected:
 class HintSystem : public ActionRecord {
 public:
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
-    virtual void execute(Nancy::NancyEngine *engine) override;
+    virtual void execute() override;
 
     byte characterID; // 0x00
     SoundDescription genericSound; // 0x01
@@ -412,7 +413,7 @@ public:
     uint16 hintID;
     int16 hintWeight;
 
-    void selectHint(Nancy::NancyEngine *engine);
+    void selectHint();
     void getHint(uint hint, uint difficulty);
     
 protected:
diff --git a/engines/nancy/action/rotatinglockpuzzle.cpp b/engines/nancy/action/rotatinglockpuzzle.cpp
index 293b046b05..3c87b19f3f 100644
--- a/engines/nancy/action/rotatinglockpuzzle.cpp
+++ b/engines/nancy/action/rotatinglockpuzzle.cpp
@@ -39,7 +39,7 @@ void RotatingLockPuzzle::init() {
     _drawSurface.clear(GraphicsManager::transColor);
 
     Graphics::Surface surf;
-    _engine->_res->loadImage("ciftree", imageName, surf);
+    NanEngine.resource->loadImage("ciftree", imageName, surf);
     image.create(surf.w, surf.h, surf.format);
     image.blitFrom(surf);
     surf.free();
@@ -106,19 +106,19 @@ uint16 RotatingLockPuzzle::readData(Common::SeekableReadStream &stream) {
     return 0x2A4;
 }
 
-void RotatingLockPuzzle::execute(Nancy::NancyEngine *engine) {
+void RotatingLockPuzzle::execute() {
     switch (state) {
     case kBegin:
         init();
         registerGraphics();
 
         for (uint i = 0; i < correctSequence.size(); ++i) {
-            currentSequence.push_back(_engine->_rnd->getRandomNumber(9));
+            currentSequence.push_back(NanEngine._rnd->getRandomNumber(9));
             drawDial(i);
         }
 
-        _engine->sound->loadSound(clickSound);
-        _engine->sound->loadSound(solveSound);
+        NanEngine.sound->loadSound(clickSound);
+        NanEngine.sound->loadSound(solveSound);
         state = kRun;
         // fall through
     case kRun:
@@ -130,20 +130,20 @@ void RotatingLockPuzzle::execute(Nancy::NancyEngine *engine) {
                 }
             }
 
-            _engine->scene->setEventFlag(flagOnSolve);
-            solveSoundPlayTime = _engine->getTotalPlayTime() + solveSoundDelay * 1000;
+            NancySceneState.setEventFlag(flagOnSolve);
+            solveSoundPlayTime = NanEngine.getTotalPlayTime() + solveSoundDelay * 1000;
             solveState = kPlaySound;
             // fall through
         case kPlaySound:
-            if (_engine->getTotalPlayTime() <= solveSoundPlayTime) {
+            if (NanEngine.getTotalPlayTime() <= solveSoundPlayTime) {
                 break;
             }
 
-            _engine->sound->playSound(solveSound);
+            NanEngine.sound->playSound(solveSound);
             solveState = kWaitForSound;
             break;
         case kWaitForSound:
-            if (!_engine->sound->isSoundPlaying(solveSound)) {
+            if (!NanEngine.sound->isSoundPlaying(solveSound)) {
                 state = kActionTrigger;
             }
 
@@ -151,14 +151,14 @@ void RotatingLockPuzzle::execute(Nancy::NancyEngine *engine) {
         }
         break;
     case kActionTrigger:
-        _engine->sound->stopSound(clickSound);
-        _engine->sound->stopSound(solveSound);
+        NanEngine.sound->stopSound(clickSound);
+        NanEngine.sound->stopSound(solveSound);
 
         if (solveState == kNotSolved) {
-            _engine->scene->changeScene(exitScene);
-            _engine->scene->setEventFlag(flagOnExit);
+            NancySceneState.changeScene(exitScene);
+            NancySceneState.setEventFlag(flagOnExit);
         } else {
-            _engine->scene->changeScene(solveExitScene);
+            NancySceneState.changeScene(solveExitScene);
         }
 
         finishExecution();
@@ -170,8 +170,8 @@ void RotatingLockPuzzle::handleInput(NancyInput &input) {
         return;
     }
 
-    if (_engine->scene->getViewport().convertViewportToScreen(exitHotspot).contains(input.mousePos)) {
-        _engine->cursorManager->setCursorType(CursorManager::kExitArrow);
+    if (NancySceneState.getViewport().convertViewportToScreen(exitHotspot).contains(input.mousePos)) {
+        NanEngine.cursorManager->setCursorType(CursorManager::kExitArrow);
 
         if (input.input & NancyInput::kLeftMouseButtonUp) {
             state = kActionTrigger;
@@ -181,11 +181,11 @@ void RotatingLockPuzzle::handleInput(NancyInput &input) {
     }
 
     for (uint i = 0; i < upHotspots.size(); ++i) {
-        if (_engine->scene->getViewport().convertViewportToScreen(upHotspots[i]).contains(input.mousePos)) {
-            _engine->cursorManager->setCursorType(CursorManager::kHotspot);
+        if (NancySceneState.getViewport().convertViewportToScreen(upHotspots[i]).contains(input.mousePos)) {
+            NanEngine.cursorManager->setCursorType(CursorManager::kHotspot);
 
             if (input.input & NancyInput::kLeftMouseButtonUp) {
-                _engine->sound->playSound(clickSound);
+                NanEngine.sound->playSound(clickSound);
                 
                 currentSequence[i] = ++currentSequence[i] > 9 ? 0 : currentSequence[i];
                 drawDial(i);
@@ -196,11 +196,11 @@ void RotatingLockPuzzle::handleInput(NancyInput &input) {
     }
 
     for (uint i = 0; i < downHotspots.size(); ++i) {
-        if (_engine->scene->getViewport().convertViewportToScreen(downHotspots[i]).contains(input.mousePos)) {
-            _engine->cursorManager->setCursorType(CursorManager::kHotspot);
+        if (NancySceneState.getViewport().convertViewportToScreen(downHotspots[i]).contains(input.mousePos)) {
+            NanEngine.cursorManager->setCursorType(CursorManager::kHotspot);
 
             if (input.input & NancyInput::kLeftMouseButtonUp) {
-                _engine->sound->playSound(clickSound);
+                NanEngine.sound->playSound(clickSound);
 
                 int8 n = currentSequence[i];
                 n = --n < 0 ? 9 : n;
@@ -213,6 +213,12 @@ void RotatingLockPuzzle::handleInput(NancyInput &input) {
     }
 }
 
+void RotatingLockPuzzle::onPause(bool pause) {
+    if (pause) {
+        registerGraphics();
+    }
+}
+
 void RotatingLockPuzzle::drawDial(uint id) {
     Common::Point destPoint(destRects[id].left - _screenPosition.left, destRects[id].top - _screenPosition.top);
     _drawSurface.blitFrom(image, srcRects[currentSequence[id]], destPoint);
diff --git a/engines/nancy/action/rotatinglockpuzzle.h b/engines/nancy/action/rotatinglockpuzzle.h
index 4ca0ec5bbb..0633cc8b0b 100644
--- a/engines/nancy/action/rotatinglockpuzzle.h
+++ b/engines/nancy/action/rotatinglockpuzzle.h
@@ -45,8 +45,9 @@ public:
     virtual void init() override;
     
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
-    virtual void execute(Nancy::NancyEngine *engine) override;
+    virtual void execute() override;
     virtual void handleInput(NancyInput &input) override;
+    virtual void onPause(bool pause) override;
 
     Common::String imageName; // 0x00
     // 0xA numDials
diff --git a/engines/nancy/action/secondarymovie.cpp b/engines/nancy/action/secondarymovie.cpp
index 86834d89c7..db77ca809a 100644
--- a/engines/nancy/action/secondarymovie.cpp
+++ b/engines/nancy/action/secondarymovie.cpp
@@ -33,7 +33,7 @@ PlaySecondaryMovie::~PlaySecondaryMovie() {
     _decoder.close();
     
     if (hideMouse == kTrue && unknown == 5) {
-        _engine->setMouseEnabled(true);
+        NanEngine.setMouseEnabled(true);
     }
 }
 
@@ -110,15 +110,15 @@ void PlaySecondaryMovie::updateGraphics() {
         
         for (auto f : frameFlags) {
             if (_decoder.getCurFrame() == f.frameID) {
-                _engine->scene->setEventFlag(f.flagDesc);
+                NancySceneState.setEventFlag(f.flagDesc);
             }
         }
     }
 
 	if ((_decoder.getCurFrame() == lastFrame && isReverse == kFalse) ||
 	    (_decoder.getCurFrame() == firstFrame && isReverse == kTrue)) {
-		if (!_engine->sound->isSoundPlaying(sound)) {
-			_engine->sound->stopSound(sound);
+		if (!NanEngine.sound->isSoundPlaying(sound)) {
+			NanEngine.sound->stopSound(sound);
 			_decoder.stop();
             isFinished = true;
 			state = kActionTrigger;
@@ -130,24 +130,28 @@ void PlaySecondaryMovie::updateGraphics() {
 
 void PlaySecondaryMovie::onPause(bool pause) {
     _decoder.pauseVideo(pause);
+
+    if (pause) {
+        registerGraphics();
+    }
 }
 
-void PlaySecondaryMovie::execute(NancyEngine *engine) {
+void PlaySecondaryMovie::execute() {
     switch (state) {
     case kBegin:
         init();
         registerGraphics();
-        engine->sound->loadSound(sound);
-        engine->sound->playSound(sound);
+        NanEngine.sound->loadSound(sound);
+        NanEngine.sound->playSound(sound);
 
         if (hideMouse == kTrue) {
-            engine->setMouseEnabled(false);
+            NanEngine.setMouseEnabled(false);
         }
 
         state = kRun;
         // fall through
     case kRun: {
-        int newFrame = _engine->scene->getSceneInfo().frameID;
+        int newFrame = NancySceneState.getSceneInfo().frameID;
 
         if (newFrame != _curViewportFrame) {
             _curViewportFrame = newFrame;
@@ -170,13 +174,13 @@ void PlaySecondaryMovie::execute(NancyEngine *engine) {
         break;
     }
     case kActionTrigger:
-        triggerFlags.execute(engine);
+        triggerFlags.execute();
         if (unknown == 5) {
-            engine->scene->changeScene(sceneChange);
+            NancySceneState.changeScene(sceneChange);
         } else {
             // Not changing the scene so enable the mouse now
             if (hideMouse == kTrue) {
-                engine->setMouseEnabled(true);
+                NanEngine.setMouseEnabled(true);
             }
         }
 
diff --git a/engines/nancy/action/secondarymovie.h b/engines/nancy/action/secondarymovie.h
index ded20ea50b..8155e17a2c 100644
--- a/engines/nancy/action/secondarymovie.h
+++ b/engines/nancy/action/secondarymovie.h
@@ -53,7 +53,7 @@ public:
     virtual void onPause(bool pause) override;
 
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
-    virtual void execute(NancyEngine *engine) override;
+    virtual void execute() override;
 
     Common::String videoName; // 0x00
 
diff --git a/engines/nancy/action/secondaryvideo.cpp b/engines/nancy/action/secondaryvideo.cpp
index 596a7b87bf..ddee547465 100644
--- a/engines/nancy/action/secondaryvideo.cpp
+++ b/engines/nancy/action/secondaryvideo.cpp
@@ -126,10 +126,14 @@ void PlaySecondaryVideo::updateGraphics() {
 
 void PlaySecondaryVideo::onPause(bool pause) {
     _decoder.pauseVideo(pause);
+
+    if (pause) {
+        registerGraphics();
+    }
 }
 
 void PlaySecondaryVideo::handleInput(NancyInput &input) {
-    if (hasHotspot && _engine->scene->getViewport().convertViewportToScreen(hotspot).contains(input.mousePos)) {
+    if (hasHotspot && NancySceneState.getViewport().convertViewportToScreen(hotspot).contains(input.mousePos)) {
         _isHovered = true;
     } else {
         _isHovered = false;
@@ -159,7 +163,7 @@ uint16 PlaySecondaryVideo::readData(Common::SeekableReadStream &stream) {
     return 0x35 + (numVideoDescs * 0x42);
 }
 
-void PlaySecondaryVideo::execute(NancyEngine *engine) {
+void PlaySecondaryVideo::execute() {
     switch (state) {
     case kBegin:
         init();
@@ -168,8 +172,8 @@ void PlaySecondaryVideo::execute(NancyEngine *engine) {
         // fall through
     case kRun: {
         // Set correct position according to viewport frame
-        if (_currentViewportFrame != engine->scene->getSceneInfo().frameID) {
-            _currentViewportFrame = engine->scene->getSceneInfo().frameID;
+        if (_currentViewportFrame != NancySceneState.getSceneInfo().frameID) {
+            _currentViewportFrame = NancySceneState.getSceneInfo().frameID;
 
             int activeFrame = -1;
 
@@ -198,8 +202,8 @@ void PlaySecondaryVideo::execute(NancyEngine *engine) {
         break;
     }
     case kActionTrigger:
-        engine->scene->pushScene();
-        engine->scene->changeScene(sceneChange);
+        NancySceneState.pushScene();
+        NancySceneState.changeScene(sceneChange);
         finishExecution();
         break;
     }
diff --git a/engines/nancy/action/secondaryvideo.h b/engines/nancy/action/secondaryvideo.h
index f20f250419..690634d1fe 100644
--- a/engines/nancy/action/secondaryvideo.h
+++ b/engines/nancy/action/secondaryvideo.h
@@ -50,7 +50,7 @@ public:
     virtual void handleInput(NancyInput &input) override;
 
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
-    virtual void execute(NancyEngine *engine) override;
+    virtual void execute() override;
 
     Common::String filename;
     //...
diff --git a/engines/nancy/action/sliderpuzzle.cpp b/engines/nancy/action/sliderpuzzle.cpp
index 1c60682cf9..4184501aee 100644
--- a/engines/nancy/action/sliderpuzzle.cpp
+++ b/engines/nancy/action/sliderpuzzle.cpp
@@ -42,7 +42,7 @@ void SliderPuzzle::init() {
     _drawSurface.clear(GraphicsManager::transColor);
 
     Graphics::Surface surf;
-    _engine->_res->loadImage("ciftree", imageName, surf);
+    NanEngine.resource->loadImage("ciftree", imageName, surf);
     image.create(surf.w, surf.h, surf.format);
     image.blitFrom(surf);
     surf.free();
@@ -109,15 +109,15 @@ uint16 SliderPuzzle::readData(Common::SeekableReadStream &stream) {
     return 0x544;
 }
 
-void SliderPuzzle::execute(Nancy::NancyEngine *engine) {
+void SliderPuzzle::execute() {
     switch (state) {
     case kBegin:
         init();
         registerGraphics();
         if (!playerHasTriedPuzzle) {
-            Common::SeekableReadStream *spuz = engine->getBootChunkStream("SPUZ");
+            Common::SeekableReadStream *spuz = NanEngine.getBootChunkStream("SPUZ");
             playerTileOrder.clear();
-            spuz->seek(engine->scene->getDifficulty() * 0x48);
+            spuz->seek(NancySceneState.getDifficulty() * 0x48);
             for (uint y = 0; y < height; ++y) {
                 playerTileOrder.push_back(Common::Array<int16>());
 
@@ -137,7 +137,7 @@ void SliderPuzzle::execute(Nancy::NancyEngine *engine) {
             }
         }
 
-        engine->sound->loadSound(clickSound);
+        NanEngine.sound->loadSound(clickSound);
         state = kRun;
         // fall through
     case kRun:
@@ -151,13 +151,13 @@ void SliderPuzzle::execute(Nancy::NancyEngine *engine) {
                 }
             }
 
-            engine->sound->loadSound(solveSound);
-            engine->sound->playSound(solveSound);
+            NanEngine.sound->loadSound(solveSound);
+            NanEngine.sound->playSound(solveSound);
             solveState = kWaitForSound;
             break;
         case kWaitForSound:
-            if (!engine->sound->isSoundPlaying(solveSound)) {
-                engine->sound->stopSound(solveSound);
+            if (!NanEngine.sound->isSoundPlaying(solveSound)) {
+                NanEngine.sound->stopSound(solveSound);
                 state = kActionTrigger;
             }
 
@@ -168,17 +168,17 @@ void SliderPuzzle::execute(Nancy::NancyEngine *engine) {
     case kActionTrigger:
         switch (solveState) {
         case kNotSolved:
-            engine->scene->changeScene(exitScene);
-            engine->scene->setEventFlag(flagOnExit);
+            NancySceneState.changeScene(exitScene);
+            NancySceneState.setEventFlag(flagOnExit);
             break;
         case kWaitForSound:
-            engine->scene->changeScene(solveExitScene);
-            engine->scene->setEventFlag(flagOnSolve);
+            NancySceneState.changeScene(solveExitScene);
+            NancySceneState.setEventFlag(flagOnSolve);
             playerHasTriedPuzzle = false;
             break;
         }
 
-        engine->sound->stopSound(clickSound);
+        NanEngine.sound->stopSound(clickSound);
         finishExecution();
     }
 }
@@ -188,8 +188,8 @@ void SliderPuzzle::handleInput(NancyInput &input) {
         return;
     }
 
-    if (_engine->scene->getViewport().convertViewportToScreen(exitHotspot).contains(input.mousePos)) {
-        _engine->cursorManager->setCursorType(CursorManager::kExitArrow);
+    if (NancySceneState.getViewport().convertViewportToScreen(exitHotspot).contains(input.mousePos)) {
+        NanEngine.cursorManager->setCursorType(CursorManager::kExitArrow);
 
         if (input.input & NancyInput::kLeftMouseButtonUp) {
             state = kActionTrigger;
@@ -205,7 +205,7 @@ void SliderPuzzle::handleInput(NancyInput &input) {
         bool shouldBreak = false;
         for (uint x = 0; x < width; ++x) {
             if (x > 0 && playerTileOrder[y][x - 1] < 0) {
-                if (_engine->scene->getViewport().convertViewportToScreen(destRects[y][x]).contains(input.mousePos)) {
+                if (NancySceneState.getViewport().convertViewportToScreen(destRects[y][x]).contains(input.mousePos)) {
                     currentTileX = x;
                     currentTileY = y;
                     direction = kLeft;
@@ -213,7 +213,7 @@ void SliderPuzzle::handleInput(NancyInput &input) {
                     break;
                 }
             } else if ((int)x < width - 1 && playerTileOrder[y][x + 1] < 0) {
-                if (_engine->scene->getViewport().convertViewportToScreen(destRects[y][x]).contains(input.mousePos)) {
+                if (NancySceneState.getViewport().convertViewportToScreen(destRects[y][x]).contains(input.mousePos)) {
                     currentTileX = x;
                     currentTileY = y;
                     direction = kRight;
@@ -221,7 +221,7 @@ void SliderPuzzle::handleInput(NancyInput &input) {
                     break;
                 }
             } else if (y > 0 && playerTileOrder[y - 1][x] < 0) {
-                if (_engine->scene->getViewport().convertViewportToScreen(destRects[y][x]).contains(input.mousePos)) {
+                if (NancySceneState.getViewport().convertViewportToScreen(destRects[y][x]).contains(input.mousePos)) {
                     currentTileX = x;
                     currentTileY = y;
                     direction = kUp;
@@ -229,7 +229,7 @@ void SliderPuzzle::handleInput(NancyInput &input) {
                     break;
                 }
             } else if ((int)y < height - 1 && playerTileOrder[y + 1][x] < 0) {
-                if (_engine->scene->getViewport().convertViewportToScreen(destRects[y][x]).contains(input.mousePos)) {
+                if (NancySceneState.getViewport().convertViewportToScreen(destRects[y][x]).contains(input.mousePos)) {
                     currentTileX = x;
                     currentTileY = y;
                     direction = kDown;
@@ -245,10 +245,10 @@ void SliderPuzzle::handleInput(NancyInput &input) {
     }
 
     if (currentTileX != -1) {
-        _engine->cursorManager->setCursorType(CursorManager::kHotspot);
+        NanEngine.cursorManager->setCursorType(CursorManager::kHotspot);
 
         if (input.input & NancyInput::kLeftMouseButtonUp) {
-            _engine->sound->playSound(clickSound);
+            NanEngine.sound->playSound(clickSound);
             switch (direction) {
             case kUp: {
                 uint curTileID = playerTileOrder[currentTileY][currentTileX];
@@ -287,6 +287,12 @@ void SliderPuzzle::handleInput(NancyInput &input) {
     }
 }
 
+void SliderPuzzle::onPause(bool pause) {
+    if (pause) {
+        registerGraphics();
+    }
+}
+
 void SliderPuzzle::synchronize(Common::Serializer &ser) {
     ser.syncAsByte(playerHasTriedPuzzle);
 
diff --git a/engines/nancy/action/sliderpuzzle.h b/engines/nancy/action/sliderpuzzle.h
index 520f36daa3..0105fbc66e 100644
--- a/engines/nancy/action/sliderpuzzle.h
+++ b/engines/nancy/action/sliderpuzzle.h
@@ -50,8 +50,9 @@ public:
     virtual void init() override;
     
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
-    virtual void execute(Nancy::NancyEngine *engine) override;
+    virtual void execute() override;
     virtual void handleInput(NancyInput &input) override;
+    virtual void onPause(bool pause) override;
 
     static void synchronize(Common::Serializer &ser);
 
diff --git a/engines/nancy/action/staticbitmapanim.cpp b/engines/nancy/action/staticbitmapanim.cpp
index 49af328bcf..9d2434ffae 100644
--- a/engines/nancy/action/staticbitmapanim.cpp
+++ b/engines/nancy/action/staticbitmapanim.cpp
@@ -36,7 +36,7 @@ namespace Action {
 
 void PlayStaticBitmapAnimation::init() {
     Graphics::Surface surf;
-    _engine->_res->loadImage("ciftree", imageName, surf);
+    NanEngine.resource->loadImage("ciftree", imageName, surf);
 
     _fullSurface.create(surf.w, surf.h, surf.format);
     _fullSurface.blitFrom(surf);
@@ -91,24 +91,24 @@ uint16 PlayStaticBitmapAnimation::readData(Common::SeekableReadStream &stream) {
     return baseSize + numViewportFrames * 0x22 + (loopLastFrame - firstFrame + 1) * 16;
 }
 
-void PlayStaticBitmapAnimation::execute(NancyEngine *engine) {
-    uint32 currentFrameTime = engine->getTotalPlayTime();
+void PlayStaticBitmapAnimation::execute() {
+    uint32 currentFrameTime = NanEngine.getTotalPlayTime();
     switch (state) {
     case kBegin:
         init();
         registerGraphics();
-        _engine->sound->loadSound(sound);
-        _engine->sound->playSound(sound);
+        NanEngine.sound->loadSound(sound);
+        NanEngine.sound->playSound(sound);
         state = kRun;
         // fall through
     case kRun: {
         // Check the timer to see if we need to draw the next animation frame
         if (nextFrameTime <= currentFrameTime) {
             // World's worst if statement
-            if (engine->scene->getEventFlag(interruptCondition) ||
+            if (NancySceneState.getEventFlag(interruptCondition) ||
                 (   (((currentFrame == loopLastFrame) && (isReverse == kFalse) && (isLooping == kFalse)) ||
                     ((currentFrame == loopFirstFrame) && (isReverse == kTrue) && (isLooping == kFalse))) &&
-                        !engine->sound->isSoundPlaying(sound))   ) {
+                        !NanEngine.sound->isSoundPlaying(sound))   ) {
                 
                 state = kActionTrigger;
 
@@ -116,12 +116,12 @@ void PlayStaticBitmapAnimation::execute(NancyEngine *engine) {
                 // nancy1's safe lock light not turning off.
                 setVisible(false);
     
-                if (!engine->sound->isSoundPlaying(sound)) {
-                    engine->sound->stopSound(sound);
+                if (!NanEngine.sound->isSoundPlaying(sound)) {
+                    NanEngine.sound->stopSound(sound);
                 }
             } else {
                 // Check if we've moved the viewport
-                uint16 newFrame = engine->scene->getSceneInfo().frameID;
+                uint16 newFrame = NancySceneState.getSceneInfo().frameID;
 
                 if (currentViewportFrame != newFrame) {
                     currentViewportFrame = newFrame;
@@ -149,7 +149,7 @@ void PlayStaticBitmapAnimation::execute(NancyEngine *engine) {
             }                
         } else {
             // Check if we've moved the viewport
-            uint16 newFrame = engine->scene->getSceneInfo().frameID;
+            uint16 newFrame = NancySceneState.getSceneInfo().frameID;
 
             if (currentViewportFrame != newFrame) {
                 currentViewportFrame = newFrame;
@@ -166,15 +166,21 @@ void PlayStaticBitmapAnimation::execute(NancyEngine *engine) {
         break;
     }
     case kActionTrigger:
-        triggerFlags.execute(engine);
+        triggerFlags.execute();
         if (doNotChangeScene == kFalse) {
-            engine->scene->changeScene(sceneChange);
+            NancySceneState.changeScene(sceneChange);
             finishExecution();
         }
         break;
     }
 }
 
+void PlayStaticBitmapAnimation::onPause(bool pause) {
+    if (pause) {
+        registerGraphics();
+    }
+}
+
 void PlayStaticBitmapAnimation::setFrame(uint frame) {
     currentFrame = frame;
     _drawSurface.create(_fullSurface, srcRects[frame]);
diff --git a/engines/nancy/action/staticbitmapanim.h b/engines/nancy/action/staticbitmapanim.h
index ee8e60557e..ab222d65c9 100644
--- a/engines/nancy/action/staticbitmapanim.h
+++ b/engines/nancy/action/staticbitmapanim.h
@@ -46,7 +46,8 @@ public:
     virtual void init() override;
 
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
-    virtual void execute(NancyEngine *engine) override;
+    virtual void execute() override;
+    virtual void onPause(bool pause) override;
 
     Common::String imageName;
 
diff --git a/engines/nancy/action/telephone.cpp b/engines/nancy/action/telephone.cpp
index 06f6eb5ded..367589ff1b 100644
--- a/engines/nancy/action/telephone.cpp
+++ b/engines/nancy/action/telephone.cpp
@@ -41,10 +41,12 @@ void Telephone::init() {
     _drawSurface.clear(GraphicsManager::transColor);
 
     Graphics::Surface surf;
-    _engine->_res->loadImage("ciftree", imageName, surf);
+    NanEngine.resource->loadImage("ciftree", imageName, surf);
     image.create(surf.w, surf.h, surf.format);
     image.blitFrom(surf);
     surf.free();
+
+    NancySceneState.setShouldClearTextbox(false);
 }
 
 uint16 Telephone::readData(Common::SeekableReadStream &stream) {
@@ -118,15 +120,15 @@ uint16 Telephone::readData(Common::SeekableReadStream &stream) {
     return numCalls * 0xEB + 0x48C;
 }
 
-void Telephone::execute(NancyEngine *engine) {
+void Telephone::execute() {
     switch (state) {
     case kBegin:
         init();
         registerGraphics();
-        _engine->sound->loadSound(dialToneSound);
-        _engine->sound->playSound(dialToneSound);
-        _engine->scene->getTextbox().clear();
-        _engine->scene->getTextbox().addTextLine(addressBookString);
+        NanEngine.sound->loadSound(dialToneSound);
+        NanEngine.sound->playSound(dialToneSound);
+        NancySceneState.getTextbox().clear();
+        NancySceneState.getTextbox().addTextLine(addressBookString);
         state = kRun;
         // fall through
     case kRun:
@@ -134,25 +136,25 @@ void Telephone::execute(NancyEngine *engine) {
         case kWaiting:
             // Long phone numbers start with 1
             if (calledNumber.size() >= 11 || (calledNumber.size() >= 7 && (calledNumber[0] != 1))) {
-                _engine->scene->getTextbox().clear();
-                _engine->scene->getTextbox().addTextLine("ringing...<n><e>"); // Hardcoded in the original engine
-                _engine->sound->loadSound(ringSound);
-                _engine->sound->playSound(ringSound);
+                NancySceneState.getTextbox().clear();
+                NancySceneState.getTextbox().addTextLine("ringing...<n><e>"); // Hardcoded in the original engine
+                NanEngine.sound->loadSound(ringSound);
+                NanEngine.sound->playSound(ringSound);
                 callState = kRinging;
             }
 
             break;
         case kButtonPress:
-            if (!_engine->sound->isSoundPlaying(genericButtonSound)) {
-                _engine->sound->stopSound(genericButtonSound);
+            if (!NanEngine.sound->isSoundPlaying(genericButtonSound)) {
+                NanEngine.sound->stopSound(genericButtonSound);
                 undrawButton(selected);
                 callState = kWaiting;
             }
 
             break;
         case kRinging:
-            if (!_engine->sound->isSoundPlaying(ringSound)) {
-                _engine->sound->stopSound(ringSound);
+            if (!NanEngine.sound->isSoundPlaying(ringSound)) {
+                NanEngine.sound->stopSound(ringSound);
                 uint numberLength = calledNumber[0] == 1 ? 11 : 7;
 
                 for (uint i = 0; i < calls.size(); ++i) {
@@ -170,47 +172,47 @@ void Telephone::execute(NancyEngine *engine) {
                         continue;
                     }
 
-                    _engine->scene->getTextbox().clear();
-                    _engine->scene->getTextbox().addTextLine(calls[i].text);
+                    NancySceneState.getTextbox().clear();
+                    NancySceneState.getTextbox().addTextLine(calls[i].text);
 
                     genericDialogueSound.name = calls[i].soundName;
-                    _engine->sound->loadSound(genericDialogueSound);
-                    _engine->sound->playSound(genericDialogueSound);
+                    NanEngine.sound->loadSound(genericDialogueSound);
+                    NanEngine.sound->playSound(genericDialogueSound);
                     selected = i;
                     callState = kCall;
 
                     return;
                 }
                 
-                _engine->scene->getTextbox().clear();
-                _engine->scene->getTextbox().addTextLine(dialAgainString);
+                NancySceneState.getTextbox().clear();
+                NancySceneState.getTextbox().addTextLine(dialAgainString);
 
-                _engine->sound->loadSound(dialAgainSound);
-                _engine->sound->playSound(dialAgainSound);
+                NanEngine.sound->loadSound(dialAgainSound);
+                NanEngine.sound->playSound(dialAgainSound);
                 callState = kBadNumber;
                 return;
             }
 
             break;
         case kBadNumber:
-            if (!_engine->sound->isSoundPlaying(dialAgainSound)) {
-                _engine->sound->stopSound(dialAgainSound);
+            if (!NanEngine.sound->isSoundPlaying(dialAgainSound)) {
+                NanEngine.sound->stopSound(dialAgainSound);
 
                 state = kActionTrigger;
             }
 
             break;
         case kCall:
-            if (!_engine->sound->isSoundPlaying(genericDialogueSound)) {
-                _engine->sound->stopSound(genericDialogueSound);
+            if (!NanEngine.sound->isSoundPlaying(genericDialogueSound)) {
+                NanEngine.sound->stopSound(genericDialogueSound);
 
                 state = kActionTrigger;
             }
 
             break;
         case kHangUp:
-            if (!_engine->sound->isSoundPlaying(hangUpSound)) {
-                _engine->sound->stopSound(hangUpSound);
+            if (!NanEngine.sound->isSoundPlaying(hangUpSound)) {
+                NanEngine.sound->stopSound(hangUpSound);
 
                 state = kActionTrigger;
             }
@@ -222,23 +224,23 @@ void Telephone::execute(NancyEngine *engine) {
     case kActionTrigger:
         switch (callState) {
         case kBadNumber:
-            engine->scene->changeScene(reloadScene);
+            NancySceneState.changeScene(reloadScene);
             calledNumber.clear();
-            _engine->scene->setEventFlag(flagOnReload);
+            NancySceneState.setEventFlag(flagOnReload);
             state = kRun;
             callState = kWaiting;
 
             break;
         case kCall: {
             PhoneCall &call = calls[selected];
-            _engine->scene->changeScene(call.sceneChange);
-            _engine->scene->setEventFlag(call.flag);
+            NancySceneState.changeScene(call.sceneChange);
+            NancySceneState.setEventFlag(call.flag);
 
             break;
         }
         case kHangUp:
-            _engine->scene->changeScene(exitScene);
-            _engine->scene->setEventFlag(flagOnExit);
+            NancySceneState.changeScene(exitScene);
+            NancySceneState.setEventFlag(flagOnExit);
             
             break;
         default:
@@ -246,7 +248,8 @@ void Telephone::execute(NancyEngine *engine) {
         }
 
         finishExecution();
-        _engine->scene->getTextbox().clear();
+        NancySceneState.setShouldClearTextbox(true);
+        NancySceneState.getTextbox().clear();
     }
 }
 
@@ -254,8 +257,8 @@ void Telephone::handleInput(NancyInput &input) {
     int buttonNr = -1;
     // Cursor gets changed regardless of state
     for (uint i = 0; i < 12; ++i) {
-        if (_engine->scene->getViewport().convertViewportToScreen(destRects[i]).contains(input.mousePos)) {
-            _engine->cursorManager->setCursorType(CursorManager::kHotspot);
+        if (NancySceneState.getViewport().convertViewportToScreen(destRects[i]).contains(input.mousePos)) {
+            NanEngine.cursorManager->setCursorType(CursorManager::kHotspot);
             buttonNr = i;
             break;
         }
@@ -265,12 +268,12 @@ void Telephone::handleInput(NancyInput &input) {
         return;
     }
 
-    if (_engine->scene->getViewport().convertViewportToScreen(exitHotspot).contains(input.mousePos)) {
-        _engine->cursorManager->setCursorType(CursorManager::kExitArrow);
+    if (NancySceneState.getViewport().convertViewportToScreen(exitHotspot).contains(input.mousePos)) {
+        NanEngine.cursorManager->setCursorType(CursorManager::kExitArrow);
 
         if (input.input & NancyInput::kLeftMouseButtonUp) {
-            _engine->sound->loadSound(hangUpSound);
-            _engine->sound->playSound(hangUpSound);
+            NanEngine.sound->loadSound(hangUpSound);
+            NanEngine.sound->playSound(hangUpSound);
 
             callState = kHangUp;
         }
@@ -280,14 +283,14 @@ void Telephone::handleInput(NancyInput &input) {
 
     if (buttonNr != -1) {
         if (input.input & NancyInput::kLeftMouseButtonUp) {
-            if (_engine->sound->isSoundPlaying(dialToneSound)) {
-                _engine->sound->stopSound(dialToneSound);
+            if (NanEngine.sound->isSoundPlaying(dialToneSound)) {
+                NanEngine.sound->stopSound(dialToneSound);
             }
 
             calledNumber.push_back(buttonNr);
             genericButtonSound.name = buttonSoundNames[buttonNr];
-            _engine->sound->loadSound(genericButtonSound);
-            _engine->sound->playSound(genericButtonSound);
+            NanEngine.sound->loadSound(genericButtonSound);
+            NanEngine.sound->playSound(genericButtonSound);
 
             drawButton(buttonNr);
 
diff --git a/engines/nancy/action/telephone.h b/engines/nancy/action/telephone.h
index b8265e2c3e..a0223c3a85 100644
--- a/engines/nancy/action/telephone.h
+++ b/engines/nancy/action/telephone.h
@@ -59,7 +59,7 @@ public:
     virtual void init() override;
 
     virtual uint16 readData(Common::SeekableReadStream &stream) override;
-    virtual void execute(NancyEngine *engine) override;
+    virtual void execute() override;
     virtual void handleInput(NancyInput &input) override;
 
     Common::String imageName; // 0x00
diff --git a/engines/nancy/cheat.cpp b/engines/nancy/cheat.cpp
index 100bfed29a..dba55e96b8 100644
--- a/engines/nancy/cheat.cpp
+++ b/engines/nancy/cheat.cpp
@@ -35,16 +35,14 @@
 
 namespace Nancy {
 
-CheatDialog::CheatDialog(NancyEngine *engine) :
-        GUI::Dialog(20, 20, 600, 440),
-        _engine(engine) {
+CheatDialog::CheatDialog() : GUI::Dialog(20, 20, 600, 440) {
     _backgroundType = GUI::ThemeEngine::kDialogBackgroundSpecial;
     Common::WinResources *res = Common::WinResources::createFromEXE("game.exe");
     Common::Array<Common::WinResourceID> dialogIDs = res->getIDList(Common::kWinDialog);
-    State::SceneInfo scene = _engine->scene->getSceneInfo();
-    Time playerTime = _engine->scene->_timers.playerTime;
-    Time timerTime = _engine->scene->_timers.timerTime;
-    bool timerIsActive = _engine->scene->_timers.timerIsActive;
+    State::SceneInfo scene = NancySceneState.getSceneInfo();
+    Time playerTime = NancySceneState._timers.playerTime;
+    Time timerTime = NancySceneState._timers.timerTime;
+    bool timerIsActive = NancySceneState._timers.timerIsActive;
     if (!timerIsActive) {
         timerTime = 0;
     }
@@ -67,11 +65,11 @@ CheatDialog::CheatDialog(NancyEngine *engine) :
 
     new GUI::StaticTextWidget(_tabs, 30, 160, 150, 20, _("Hints Remaining"), Graphics::kTextAlignLeft);
     new GUI::StaticTextWidget(_tabs, 35, 185, 45, 20, _("Easy"), Graphics::kTextAlignLeft);
-    _hintsRemainingEasy = new GUI::EditTextWidget(_tabs, 35, 205, 45, 20, _(itoa(_engine->scene->_hintsRemaining[0], buf, 10)), _(""), kInputHintsEasy, kInputHintsEasy);
+    _hintsRemainingEasy = new GUI::EditTextWidget(_tabs, 35, 205, 45, 20, _(itoa(NancySceneState._hintsRemaining[0], buf, 10)), _(""), kInputHintsEasy, kInputHintsEasy);
     new GUI::StaticTextWidget(_tabs, 85, 185, 45, 20, _("Medium"), Graphics::kTextAlignLeft);
-    _hintsRemainingMedium = new GUI::EditTextWidget(_tabs, 85, 205, 45, 20, _(itoa(_engine->scene->_hintsRemaining[1], buf, 10)), _(""), kInputHintsMedium, kInputHintsMedium);
+    _hintsRemainingMedium = new GUI::EditTextWidget(_tabs, 85, 205, 45, 20, _(itoa(NancySceneState._hintsRemaining[1], buf, 10)), _(""), kInputHintsMedium, kInputHintsMedium);
     new GUI::StaticTextWidget(_tabs, 135, 185, 45, 20, _("Hard"), Graphics::kTextAlignLeft);
-    _hintsRemainingHard = new GUI::EditTextWidget(_tabs, 135, 205, 45, 20, _(itoa(_engine->scene->_hintsRemaining[2], buf, 10)), _(""), kInputHintsHard, kInputHintsHard);
+    _hintsRemainingHard = new GUI::EditTextWidget(_tabs, 135, 205, 45, 20, _(itoa(NancySceneState._hintsRemaining[2], buf, 10)), _(""), kInputHintsHard, kInputHintsHard);
     
     new GUI::StaticTextWidget(_tabs, 250, 20, 150, 20, _("Player Data"), Graphics::kTextAlignLeft);
     new GUI::StaticTextWidget(_tabs, 255, 50, 150, 20, _("Player Time:"), Graphics::kTextAlignLeft);
@@ -81,7 +79,7 @@ CheatDialog::CheatDialog(NancyEngine *engine) :
     new GUI::StaticTextWidget(_tabs, 375, 75, 40, 20, _("Hours"), Graphics::kTextAlignLeft);
     _playerTimeMinutes =new GUI::EditTextWidget(_tabs, 415, 75, 35, 20, _(itoa(playerTime.getMinutes(), buf, 10)), _(""), kInputPlayerTime, kInputPlayerTime);
     new GUI::StaticTextWidget(_tabs, 455, 75, 50, 20, _("Minutes"), Graphics::kTextAlignLeft);
-    _difficulty = new GUI::EditTextWidget(_tabs, 255, 105, 35, 20, _(itoa(_engine->scene->_difficulty, buf, 10)), _(""), kInputDifficulty, kInputDifficulty);
+    _difficulty = new GUI::EditTextWidget(_tabs, 255, 105, 35, 20, _(itoa(NancySceneState._difficulty, buf, 10)), _(""), kInputDifficulty, kInputDifficulty);
     new GUI::StaticTextWidget(_tabs, 295, 105, 150, 20, _("Player Difficulty Level"), Graphics::kTextAlignLeft);
 
     new GUI::StaticTextWidget(_tabs, 250, 140, 150, 20, _("Player Data"), Graphics::kTextAlignLeft);
@@ -135,7 +133,7 @@ CheatDialog::CheatDialog(NancyEngine *engine) :
                     }
                 }
                 GUI::CheckboxWidget *box = new GUI::CheckboxWidget(_tabs, 250 * (numItems / 10) + 20, (350 / 10) * (numItems % 10) + 15, 250, 250/10, _(itemLabel), Common::U32String());
-                box->setState(_engine->scene->hasItem(numItems) == kTrue);
+                box->setState(NancySceneState.hasItem(numItems) == kTrue);
                 _inventory.push_back(box);
 
                 ++numItems;
@@ -153,9 +151,9 @@ void CheatDialog::handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 d
     case GUI::kOKCmd: {
         if (_restartScene->getState()) {
             uint sceneID = atoi(Common::String(_scene->getEditString()).c_str());
-            IFF iff(_engine, Common::String::format("S%u", sceneID));
+            IFF iff(Common::String::format("S%u", sceneID));
             if (iff.load()) {
-                _engine->scene->changeScene(
+                NancySceneState.changeScene(
                     atoi(Common::String(_scene->getEditString()).c_str()),
                     atoi(Common::String(_frame->getEditString()).c_str()),
                     atoi(Common::String(_offset->getEditString()).c_str()),
@@ -167,32 +165,32 @@ void CheatDialog::handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 d
         }
 
         if (_timerOn->getState()) {
-            _engine->scene->_timers.timerIsActive = true;
-            Time &timer = _engine->scene->_timers.timerTime;
+            NancySceneState._timers.timerIsActive = true;
+            Time &timer = NancySceneState._timers.timerTime;
             timer = 0;
             timer += 1000 * atoi(Common::String(_timerSeconds->getEditString()).c_str());
             timer += 60000 * atoi(Common::String(_timerMinutes->getEditString()).c_str());
             timer += 3600000 * atoi(Common::String(_timerHours->getEditString()).c_str());
         } else {
-            _engine->scene->stopTimer();
+            NancySceneState.stopTimer();
         }
 
-        Time &playerTime = _engine->scene->_timers.timerTime;
+        Time &playerTime = NancySceneState._timers.timerTime;
         playerTime = 0;
         playerTime += 60000 * atoi(Common::String(_playerTimeMinutes->getEditString()).c_str());
         playerTime += 3600000 * atoi(Common::String(_playerTimeHours->getEditString()).c_str());
         playerTime += 86400000 * atoi(Common::String(_playerTimeMinutes->getEditString()).c_str());
 
-        _engine->scene->_difficulty = atoi(Common::String(_difficulty->getEditString()).c_str());
-        _engine->scene->_hintsRemaining[0] = atoi(Common::String(_hintsRemainingEasy->getEditString()).c_str());
-        _engine->scene->_hintsRemaining[1] = atoi(Common::String(_hintsRemainingMedium->getEditString()).c_str());
-        _engine->scene->_hintsRemaining[2] = atoi(Common::String(_hintsRemainingHard->getEditString()).c_str());
+        NancySceneState._difficulty = atoi(Common::String(_difficulty->getEditString()).c_str());
+        NancySceneState._hintsRemaining[0] = atoi(Common::String(_hintsRemainingEasy->getEditString()).c_str());
+        NancySceneState._hintsRemaining[1] = atoi(Common::String(_hintsRemainingMedium->getEditString()).c_str());
+        NancySceneState._hintsRemaining[2] = atoi(Common::String(_hintsRemainingHard->getEditString()).c_str());
 
         for (uint i = 0; i < _inventory.size(); ++i) {
-            if (_engine->scene->hasItem(i) == kTrue && !_inventory[i]->getState()) {
-                _engine->scene->removeItemFromInventory(i, false);
-            } else if (_engine->scene->hasItem(i) == kFalse && _inventory[i]->getState()) {
-                _engine->scene->addItemToInventory(i);
+            if (NancySceneState.hasItem(i) == kTrue && !_inventory[i]->getState()) {
+                NancySceneState.removeItemFromInventory(i, false);
+            } else if (NancySceneState.hasItem(i) == kFalse && _inventory[i]->getState()) {
+                NancySceneState.addItemToInventory(i);
             }
         }
         cmd = GUI::kCloseCmd;
@@ -258,9 +256,7 @@ void CheatDialog::sanitizeInput(GUI::EditTextWidget *textWidget, int maxValue) {
     textWidget->setCaretPos(str.size());
 }
 
-EventFlagDialog::EventFlagDialog(NancyEngine *engine) :
-        GUI::Dialog(20, 20, 600, 440),
-        _engine(engine) {
+EventFlagDialog::EventFlagDialog() : GUI::Dialog(20, 20, 600, 440) {
     _backgroundType = GUI::ThemeEngine::kDialogBackgroundSpecial;
     Common::WinResources *res = Common::WinResources::createFromEXE("game.exe");
     Common::Array<Common::WinResourceID> dialogIDs = res->getIDList(Common::kWinDialog);
@@ -324,7 +320,7 @@ EventFlagDialog::EventFlagDialog(NancyEngine *engine) :
                     uint32 command = atoi(num.c_str()) << 16 | 'ev';
 
                     GUI::CheckboxWidget *box = new GUI::CheckboxWidget(_tabs, 300 * (numFlags / 12) + 20, (350 / 12) * (numFlags % 12) + 15, 300, 350/12, _(flagLabel), Common::U32String(), command);
-                    box->setState(_engine->scene->getEventFlag(command >> 16));
+                    box->setState(NancySceneState.getEventFlag(command >> 16));
 
                     ++numFlags;
                 }
@@ -339,7 +335,7 @@ void EventFlagDialog::handleCommand(GUI::CommandSender *sender, uint32 cmd, uint
     Dialog::handleCommand(sender, cmd, data);
     if (cmd & 'ev') {
         cmd >>= 16;
-        _engine->scene->setEventFlag(cmd, data == 0 ? kFalse : kTrue);
+        NancySceneState.setEventFlag(cmd, data == 0 ? kFalse : kTrue);
     }
 }
 
diff --git a/engines/nancy/cheat.h b/engines/nancy/cheat.h
index ab29eef3d8..4ef098df86 100644
--- a/engines/nancy/cheat.h
+++ b/engines/nancy/cheat.h
@@ -43,7 +43,7 @@ class NancyEngine;
 
 class CheatDialog : public GUI::Dialog {
 public:
-    CheatDialog(NancyEngine *engine);
+    CheatDialog();
 
 protected:
     enum Commands {
@@ -60,8 +60,6 @@ protected:
 
     void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) override;
     static void sanitizeInput(GUI::EditTextWidget *textWidget, int maxValue = -1);
-    
-    NancyEngine *_engine;
 
     GUI::CheckboxWidget *_restartScene;
     GUI::EditTextWidget *_scene;
@@ -87,12 +85,10 @@ protected:
 
 class EventFlagDialog : public GUI::Dialog {
 public:
-    EventFlagDialog(NancyEngine *engine);
+    EventFlagDialog();
 
 protected:
     void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) override;
-    
-    NancyEngine *_engine;
 };
 
 } // End of namespace Nancy
diff --git a/engines/nancy/commontypes.cpp b/engines/nancy/commontypes.cpp
index a4f2b7ca78..0c4227af95 100644
--- a/engines/nancy/commontypes.cpp
+++ b/engines/nancy/commontypes.cpp
@@ -55,9 +55,9 @@ void MultiEventFlagDescription::readData(Common::SeekableReadStream &stream) {
     }
 }
 
-void MultiEventFlagDescription::execute(NancyEngine *engine) {
+void MultiEventFlagDescription::execute() {
     for (uint i = 0; i < 10; ++i) {
-        engine->scene->setEventFlag(descs[i]);
+        NancySceneState.setEventFlag(descs[i]);
     }
 }
 
diff --git a/engines/nancy/commontypes.h b/engines/nancy/commontypes.h
index 1113edc833..7ddc98e480 100644
--- a/engines/nancy/commontypes.h
+++ b/engines/nancy/commontypes.h
@@ -76,7 +76,7 @@ struct MultiEventFlagDescription {
     EventFlagDescription descs[10];
 
     void readData(Common::SeekableReadStream &stream);
-    void execute(Nancy::NancyEngine *engine);
+    void execute();
 };
 
 struct SecondaryVideoDescription {
diff --git a/engines/nancy/console.cpp b/engines/nancy/console.cpp
index 810c169934..426008885e 100644
--- a/engines/nancy/console.cpp
+++ b/engines/nancy/console.cpp
@@ -39,7 +39,7 @@
 
 namespace Nancy {
 
-NancyConsole::NancyConsole(NancyEngine *vm) : GUI::Debugger(), _vm(vm) {
+NancyConsole::NancyConsole() : GUI::Debugger() {
 	registerCmd("load_cal", WRAP_METHOD(NancyConsole, Cmd_loadCal));
 	registerCmd("cif_hexdump", WRAP_METHOD(NancyConsole, Cmd_cifHexDump));
 	registerCmd("cif_export", WRAP_METHOD(NancyConsole, Cmd_cifExport));
@@ -64,9 +64,9 @@ void NancyConsole::postEnter() {
 
 		if (dec->loadFile(_videoFile)) {
 			dec->start();
-			_vm->_system->fillScreen(0);
+			g_system->fillScreen(0);
 			Common::EventManager *ev = g_system->getEventManager();
-			while (!_vm->shouldQuit() && !dec->endOfVideo()) {
+			while (!NanEngine.shouldQuit() && !dec->endOfVideo()) {
 				Common::Event event;
 				if (ev->pollEvent(event)) {
 					if (event.type == Common::EVENT_CUSTOM_ENGINE_ACTION_END && event.customType == Nancy::InputManager::kNancyActionLeftClick) {
@@ -77,15 +77,15 @@ void NancyConsole::postEnter() {
 				if (dec->needsUpdate()) {
 					const Graphics::Surface *frame = dec->decodeNextFrame();
 					if (frame) {
-						_vm->_system->copyRectToScreen(frame->getPixels(), frame->pitch, 0, 0, frame->w, frame->h);
-						_vm->_system->updateScreen();
+						g_system->copyRectToScreen(frame->getPixels(), frame->pitch, 0, 0, frame->w, frame->h);
+						g_system->updateScreen();
 					}
 				}
 
-				_vm->_system->delayMillis(10);
+				g_system->delayMillis(10);
 			}
 
-			_vm->graphicsManager->redrawAll();
+			NanEngine.graphicsManager->redrawAll();
 		} else {
 			debugPrintf("Failed to load '%s'\n", _videoFile.c_str());
 		}
@@ -96,27 +96,27 @@ void NancyConsole::postEnter() {
 
 	if (!_imageFile.empty()) {
 		Graphics::Surface surf;
-		if (_vm->_res->loadImage(_imageCifTree, _imageFile, surf)) {
-			_vm->_system->fillScreen(0);
-			_vm->_system->copyRectToScreen(surf.getPixels(), surf.pitch, 0, 0, surf.w > 640 ? 640 : surf.w, surf.h > 480 ? 480 : surf.h);
-			_vm->_system->updateScreen();
+		if (NanEngine.resource->loadImage(_imageCifTree, _imageFile, surf)) {
+			g_system->fillScreen(0);
+			g_system->copyRectToScreen(surf.getPixels(), surf.pitch, 0, 0, surf.w > 640 ? 640 : surf.w, surf.h > 480 ? 480 : surf.h);
+			g_system->updateScreen();
 			surf.free();
 
 			Common::EventManager *ev = g_system->getEventManager();
-			while (!_vm->shouldQuit()) {
+			while (!NanEngine.shouldQuit()) {
 				Common::Event event;
 				if (ev->pollEvent(event)) {
 					if (event.type == Common::EVENT_CUSTOM_ENGINE_ACTION_END && event.customType == Nancy::InputManager::kNancyActionLeftClick) {
 						break;
 					}
 
-					_vm->_system->updateScreen();
+					g_system->updateScreen();
 				}
 
-				_vm->_system->delayMillis(10);
+				g_system->delayMillis(10);
 			}
 			
-			_vm->graphicsManager->redrawAll();
+			NanEngine.graphicsManager->redrawAll();
 		} else {
 			debugPrintf("Failed to load image '%s'\n", _imageFile.c_str());
 		}
@@ -127,7 +127,7 @@ void NancyConsole::postEnter() {
 
 	// After calling the console, action end events get sent to it and the input manager
 	// can still think a keyboard button is being held when it is not; clearing all inputs fixes that
-	_vm->input->forceCleanInput();
+	NanEngine.input->forceCleanInput();
 }
 
 bool NancyConsole::Cmd_cifHexDump(int argc, const char **argv) {
@@ -138,7 +138,7 @@ bool NancyConsole::Cmd_cifHexDump(int argc, const char **argv) {
 	}
 
 	uint size;
-	byte *buf = _vm->_res->loadCif((argc == 2 ? "ciftree" : argv[2]), argv[1], size);
+	byte *buf = NanEngine.resource->loadCif((argc == 2 ? "ciftree" : argv[2]), argv[1], size);
 	if (!buf) {
 		debugPrintf("Failed to load resource '%s'\n", argv[1]);
 		return true;
@@ -156,7 +156,7 @@ bool NancyConsole::Cmd_cifExport(int argc, const char **argv) {
 		return true;
 	}
 
-	if (!_vm->_res->exportCif((argc == 2 ? "ciftree" : argv[2]), argv[1]))
+	if (!NanEngine.resource->exportCif((argc == 2 ? "ciftree" : argv[2]), argv[1]))
 		debugPrintf("Failed to export '%s'\n", argv[1]);
 
 	return true;
@@ -171,7 +171,7 @@ bool NancyConsole::Cmd_cifList(int argc, const char **argv) {
 	}
 
 	Common::Array<Common::String> list;
-	_vm->_res->list((argc == 2 ? "ciftree" : argv[2]), list, atoi(argv[1]));
+	NanEngine.resource->list((argc == 2 ? "ciftree" : argv[2]), list, atoi(argv[1]));
 	for (uint i = 0; i < list.size(); i++) {
 		debugPrintf("%-38s", list[i].c_str());
 		if ((i % 2) == 1 && i + 1 != list.size())
@@ -190,7 +190,7 @@ bool NancyConsole::Cmd_cifInfo(int argc, const char **argv) {
 		return true;
 	}
 
-	debugPrintf("%s", _vm->_res->getCifDescription((argc == 2 ? "ciftree" : argv[2]), argv[1]).c_str());
+	debugPrintf("%s", NanEngine.resource->getCifDescription((argc == 2 ? "ciftree" : argv[2]), argv[1]).c_str());
 	return true;
 }
 
@@ -201,7 +201,7 @@ bool NancyConsole::Cmd_chunkHexDump(int argc, const char **argv) {
 		return true;
 	}
 
-	IFF iff(_vm, argv[1]);
+	IFF iff(argv[1]);
 	if (!iff.load()) {
 		debugPrintf("Failed to load IFF '%s'\n", argv[1]);
 		return true;
@@ -236,7 +236,7 @@ bool NancyConsole::Cmd_chunkList(int argc, const char **argv) {
 		return true;
 	}
 
-	IFF iff(_vm, argv[1]);
+	IFF iff(argv[1]);
 	if (!iff.load()) {
 		debugPrintf("Failed to load IFF '%s'\n", argv[1]);
 		return true;
@@ -274,7 +274,7 @@ bool NancyConsole::Cmd_loadCal(int argc, const char **argv) {
 		return true;
 	}
 
-	if (!_vm->_res->loadCifTree(argv[1], "cal"))
+	if (!NanEngine.resource->loadCifTree(argv[1], "cal"))
 		debugPrintf("Failed to load '%s.cal'\n", argv[1]);
 	return true;
 }
@@ -312,7 +312,7 @@ bool NancyConsole::Cmd_playAudio(int argc, const char **argv) {
 		return true;
 	}
 	Audio::SoundHandle handle;
-	_vm->_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &handle, stream);
+	g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &handle, stream);
 	return true;
 }
 
@@ -323,30 +323,30 @@ bool NancyConsole::Cmd_loadScene(int argc, const char **argv) {
 		return true;
 	}
 	
-	if (_vm->getPreviousState() != NancyEngine::GameState::kScene) {
+	if (NanEngine._gameFlow.previousState != &NancySceneState) {
 		debugPrintf("Not in the kScene state\n");
 		return true;
 	}
 
 	Common::String sceneName = Common::String::format("S%s", argv[1]);
-    IFF iff(_vm, sceneName);
+    IFF iff(sceneName);
 	if (!iff.load()) {
 		debugPrintf("Invalid scene S%s\n", argv[1]);
 		return true;
 	}
 
-	_vm->scene->changeScene((uint16)atoi(argv[1]), 0, 0, false);
-	_vm->scene->_state = State::Scene::kLoad;
+	NancySceneState.changeScene((uint16)atoi(argv[1]), 0, 0, false);
+	NancySceneState._state = State::Scene::kLoad;
 	return cmdExit(0, 0);
 }
 
 bool NancyConsole::Cmd_sceneID(int argc, const char **argv) {
-	if (_vm->getPreviousState() != NancyEngine::GameState::kScene) {
+	if (NanEngine._gameFlow.previousState != &NancySceneState) {
 		debugPrintf("Not in the kScene state\n");
 		return true;
 	}
 
-	debugPrintf("Scene: %u, Frame: %i \n", _vm->scene->getSceneInfo().sceneID, _vm->scene->getSceneInfo().frameID);
+	debugPrintf("Scene: %u, Frame: %i \n", NancySceneState.getSceneInfo().sceneID, NancySceneState.getSceneInfo().frameID);
 	return true;
 }
 
diff --git a/engines/nancy/console.h b/engines/nancy/console.h
index 9141523e88..ede680884e 100644
--- a/engines/nancy/console.h
+++ b/engines/nancy/console.h
@@ -33,13 +33,12 @@ class NancyEngine;
 
 class NancyConsole : public GUI::Debugger {
 public:
-	NancyConsole(NancyEngine *vm);
+	NancyConsole();
 	virtual ~NancyConsole(void);
 
 	void postEnter();
 
 private:
-	NancyEngine *_vm;
 	bool Cmd_loadCal(int argc, const char **argv);
 	bool Cmd_cifHexDump(int argc, const char **argv);
 	bool Cmd_cifExport(int argc, const char **argv);
diff --git a/engines/nancy/cursor.cpp b/engines/nancy/cursor.cpp
index 72b4a38bb3..55508115a2 100644
--- a/engines/nancy/cursor.cpp
+++ b/engines/nancy/cursor.cpp
@@ -33,11 +33,11 @@
 namespace Nancy {
 
 void CursorManager::init() {
-    Common::SeekableReadStream *chunk = _engine->getBootChunkStream("INV");
+    Common::SeekableReadStream *chunk = NanEngine.getBootChunkStream("INV");
     chunk->seek(0x1D2); // TODO
     Common::String inventoryCursorsImageName = chunk->readString();
 
-    chunk = _engine->getBootChunkStream("CURS");
+    chunk = NanEngine.getBootChunkStream("CURS");
     for (uint i = 0; i < 56; ++i) {
         _cursors.push_back(Cursor());
         chunk->seek(i * 16, SEEK_SET);
@@ -52,7 +52,7 @@ void CursorManager::init() {
     _primaryVideoInitialPos.y = chunk->readUint16LE();
 
     Graphics::Surface surf;
-    _engine->_res->loadImage("ciftree", inventoryCursorsImageName, surf);
+    NanEngine.resource->loadImage("ciftree", inventoryCursorsImageName, surf);
     _invCursorsSurface.create(surf.w, surf.h, surf.format);
     _invCursorsSurface.blitFrom(surf);
 
@@ -111,7 +111,7 @@ void CursorManager::setCursor(CursorType type, int16 itemID) {
         surf = &_invCursorsSurface;
         
     } else {
-        surf = &_engine->graphicsManager->object0;
+        surf = &NanEngine.graphicsManager->object0;
     }
 
     // TODO this is ridiculous, figure out why just calling
diff --git a/engines/nancy/cursor.h b/engines/nancy/cursor.h
index e15621b125..2da24c1a15 100644
--- a/engines/nancy/cursor.h
+++ b/engines/nancy/cursor.h
@@ -34,8 +34,7 @@ class CursorManager {
 public:
     enum CursorType { kNormal = 0, kHotspot = 1, kMove = 2, kNormalArrow, kHotspotArrow, kExitArrow };
 
-    CursorManager(NancyEngine *engine) :
-        _engine(engine),
+    CursorManager() :
         _isInitialized(false),
         _curItemID(-1),
         _curCursorType(kNormal) {}
@@ -53,8 +52,6 @@ private:
         Common::Point hotspot;
     };
 
-    NancyEngine *_engine;
-
     // CURS data
     Common::Array<Cursor> _cursors;
 
diff --git a/engines/nancy/font.cpp b/engines/nancy/font.cpp
index 87a53ca94d..ab2c68e2dd 100644
--- a/engines/nancy/font.cpp
+++ b/engines/nancy/font.cpp
@@ -29,8 +29,8 @@
 
 namespace Nancy {
 
-void Font::read(Common::SeekableReadStream &stream, NancyEngine *engine) {
-    _transColor = engine->graphicsManager->transColor;
+void Font::read(Common::SeekableReadStream &stream) {
+    _transColor = NanEngine.graphicsManager->transColor;
     _maxCharWidth = 0;
     _fontHeight = 0;
 
@@ -38,7 +38,7 @@ void Font::read(Common::SeekableReadStream &stream, NancyEngine *engine) {
     stream.read(name, 10);
     Common::String imageName = name;
 
-    engine->_res->loadImage("ciftree", name, _image);
+    NanEngine.resource->loadImage("ciftree", name, _image);
 
     char desc[0x20];
     stream.read(desc, 0x20);
diff --git a/engines/nancy/font.h b/engines/nancy/font.h
index a4dbf420ff..a0db640066 100644
--- a/engines/nancy/font.h
+++ b/engines/nancy/font.h
@@ -40,7 +40,7 @@ public:
 	Font() =default;
 	~Font() =default;
     
-    void read(Common::SeekableReadStream &stream, NancyEngine *engine);
+    void read(Common::SeekableReadStream &stream);
 
     int getFontHeight() const override { return _fontHeight; }
 	int getMaxCharWidth() const override { return _maxCharWidth; }
diff --git a/engines/nancy/graphics.cpp b/engines/nancy/graphics.cpp
index a9978bc999..5a641b0ab9 100644
--- a/engines/nancy/graphics.cpp
+++ b/engines/nancy/graphics.cpp
@@ -40,16 +40,12 @@ void GraphicsManager::init() {
     _screen.setTransparentColor(transColor); 
 
     Graphics::Surface surf;
-    _engine->_res->loadImage("ciftree", "OBJECT0", surf);
+    NanEngine.resource->loadImage("ciftree", "OBJECT0", surf);
     object0.create(surf.w, surf.h, surf.format);
     object0.blitFrom(surf, Common::Point(0, 0));
     surf.free();
 
-    Common::SeekableReadStream *fontChunk = _engine->getBootChunkStream("FONT");
-    while(fontChunk->pos() != fontChunk->size()) {
-        _fonts.push_back(Font());
-        _fonts.back().read(*fontChunk, _engine);
-    }
+    loadFonts();
 }
 
 void GraphicsManager::draw() {
@@ -95,12 +91,6 @@ void GraphicsManager::draw() {
     _screen.update();
 }
 
-void GraphicsManager::onPause(bool pause) {
-    for (auto &object : _objects) {
-        object->onPause(pause);
-    }
-}
-
 void GraphicsManager::addObject(RenderObject *object) {
     for (auto &r : _objects) {
         if (r == object) {
@@ -135,12 +125,12 @@ void GraphicsManager::redrawAll() {
 }
 
 void GraphicsManager::loadFonts() {
-    Common::SeekableReadStream *chunk = _engine->getBootChunkStream("FONT");
+    Common::SeekableReadStream *chunk = NanEngine.getBootChunkStream("FONT");
     
     chunk->seek(0);
     while (chunk->pos() < chunk->size() - 1) {
         _fonts.push_back(Font());
-        _fonts.back().read(*chunk, _engine);
+        _fonts.back().read(*chunk);
     }
 }
 
diff --git a/engines/nancy/graphics.h b/engines/nancy/graphics.h
index 34df53fe67..bd8a281bf7 100644
--- a/engines/nancy/graphics.h
+++ b/engines/nancy/graphics.h
@@ -37,13 +37,11 @@ class NancyEngine;
 // Graphics class that handles multilayered surface rendering with minimal redraw
 class GraphicsManager {
 public:
-    GraphicsManager(NancyEngine *engine) : _engine(engine), _objects(objectComparator) {}
+    GraphicsManager() : _objects(objectComparator) {}
 
     void init();
     void draw();
 
-    void onPause(bool pause);
-
     void addObject(RenderObject *object);
     void removeObject(RenderObject *object);
     void clearObjects();
@@ -63,7 +61,6 @@ private:
 
     static int objectComparator(const void *a, const void *b);
 
-    NancyEngine *_engine;
     Common::SortedArray<RenderObject *> _objects;
 
     Graphics::Screen _screen;
diff --git a/engines/nancy/iff.cpp b/engines/nancy/iff.cpp
index ffd86d9ec8..60540bfdc7 100644
--- a/engines/nancy/iff.cpp
+++ b/engines/nancy/iff.cpp
@@ -69,7 +69,7 @@ bool IFF::callback(Common::IFFChunk &c) {
 bool IFF::load() {
 	byte *data;
 	uint size;
-	data = _vm->_res->loadData("ciftree", _name, size);
+	data = NanEngine.resource->loadData("ciftree", _name, size);
 
 	if (!data)
 		return false;
diff --git a/engines/nancy/iff.h b/engines/nancy/iff.h
index c3d11981c1..3f26d4b3e7 100644
--- a/engines/nancy/iff.h
+++ b/engines/nancy/iff.h
@@ -40,7 +40,7 @@ class NancyEngine;
 
 class IFF {
 public:
-	IFF(NancyEngine *vm, const Common::String &name) : _name(name), _vm(vm) { };
+	IFF(const Common::String &name) : _name(name) { };
 	~IFF();
 
 	bool load();
@@ -64,7 +64,6 @@ private:
 
 	Common::Array<Chunk> _chunks;
 	const Common::String _name;
-	NancyEngine *_vm;
 };
 
 } // End of namespace Nancy
diff --git a/engines/nancy/input.cpp b/engines/nancy/input.cpp
index e0fd3e36d4..6fe190efb7 100644
--- a/engines/nancy/input.cpp
+++ b/engines/nancy/input.cpp
@@ -37,15 +37,15 @@ void InputManager::processEvents() {
     _inputs &= ~(NancyInput::kLeftMouseButtonDown | NancyInput::kLeftMouseButtonUp | NancyInput::kRightMouseButtonDown | NancyInput::kRightMouseButtonUp);
     _otherKbdInput.clear();
 
-    while (_engine->getEventManager()->pollEvent(event)) {
+    while (NanEngine.getEventManager()->pollEvent(event)) {
         switch (event.type) {
         case EVENT_KEYDOWN:
             if (event.kbd.keycode == KEYCODE_d && event.kbd.flags & Common::KBD_CTRL) {
                 // Launch debug console
-                _engine->launchConsole = true;
+                NanEngine.launchConsole = true;
             } else if (event.kbd.keycode == KEYCODE_q && event.kbd.flags & Common::KBD_CTRL) {
                 // Quit
-                _engine->quitGame();
+                NanEngine.quitGame();
             } else {
                 // Push all other keyboard events into an array and let getInput() callers handle them
                 _otherKbdInput.push_back(event.kbd);
@@ -77,10 +77,10 @@ void InputManager::processEvents() {
                 _inputs |= NancyInput::kMoveFastModifier;
                 break;
             case kNancyActionRequestCheatMenu:
-                _engine->callCheatMenu(false);
+                NanEngine.callCheatMenu(false);
                 break;
             case kNancyActionRequestEventMenu:
-                _engine->callCheatMenu(true);
+                NanEngine.callCheatMenu(true);
                 break;
             default:
                 break;
@@ -127,7 +127,7 @@ NancyInput InputManager::getInput() const {
     NancyInput ret;
     ret.input = _inputs;
     if (_mouseEnabled) {
-        ret.mousePos = _engine->getEventManager()->getMousePos();
+        ret.mousePos = NanEngine.getEventManager()->getMousePos();
     } else {
         ret.eatMouseInput();
     }
diff --git a/engines/nancy/input.h b/engines/nancy/input.h
index 6ba64bbd22..7e13869e10 100644
--- a/engines/nancy/input.h
+++ b/engines/nancy/input.h
@@ -87,8 +87,7 @@ enum NancyAction {
 };
 
 public:
-    InputManager(NancyEngine *engine) :
-        _engine(engine),
+    InputManager() :
         _inputs(0),
         _mouseEnabled(true) {}
 
@@ -101,7 +100,6 @@ public:
     static void initKeymaps(Common::KeymapArray &keymaps);
     
 private:
-    NancyEngine *_engine;
     uint16 _inputs;
     Common::Array<Common::KeyState> _otherKbdInput;
     bool _mouseEnabled;
diff --git a/engines/nancy/nancy.cpp b/engines/nancy/nancy.cpp
index 6d9a47709e..12e3b5e4d9 100644
--- a/engines/nancy/nancy.cpp
+++ b/engines/nancy/nancy.cpp
@@ -62,26 +62,19 @@
 
 namespace Nancy {
 
-NancyEngine *NancyEngine::s_Engine = nullptr;
-
 NancyEngine::NancyEngine(OSystem *syst, const NancyGameDescription *gd) : Engine(syst), _gameDescription(gd), _system(syst) {
 	DebugMan.addDebugChannel(kDebugEngine, "Engine", "Engine debug level");
 	DebugMan.addDebugChannel(kDebugActionRecord, "ActionRecord", "Action Record debug level");
 	DebugMan.addDebugChannel(kDebugScene, "Scene", "Scene debug level");
 
-	_console = new NancyConsole(this);
+	_console = new NancyConsole();
 	_rnd = new Common::RandomSource("Nancy");
 	_rnd->setSeed(_rnd->getSeed());
 
-	logo = new State::Logo(this);
-	scene = new State::Scene(this);
-	map = new State::Map(this);
-	credits = new State::Credits(this);
-	help = new State::Help(this);
-	input = new InputManager(this);
-	sound = new SoundManager(this);
-	graphicsManager = new GraphicsManager(this);
-	cursorManager = new CursorManager(this);
+	input = new InputManager();
+	sound = new SoundManager();
+	graphicsManager = new GraphicsManager();
+	cursorManager = new CursorManager();
 
 	launchConsole = false;
 }
@@ -91,9 +84,7 @@ NancyEngine::~NancyEngine() {
 	DebugMan.clearAllDebugChannels();
 	delete _console;
 	delete _rnd;
-	
-	delete scene;
-	delete map;
+
 	delete graphicsManager;
 	delete input;
 	delete sound;
@@ -120,10 +111,7 @@ Common::Platform NancyEngine::getPlatform() const {
 }
 
 Common::Error NancyEngine::run() {
-	s_Engine = this;
-
 	initGraphics(640, 480, &GraphicsManager::pixelFormat);
-	_console = new NancyConsole(this);
 
 	const Common::FSNode gameDataDir(ConfMan.get("path"));
 	SearchMan.addSubDirectoryMatching(gameDataDir, "game");
@@ -141,75 +129,24 @@ Common::Error NancyEngine::run() {
 	if (cab)
 		SearchMan.add("data1.hdr", cab);
 	
-	_res = new ResourceManager(this);
-	_res->initialize();
+	resource = new ResourceManager();
+	resource->initialize();
 
 	// Setup mixer
 	syncSoundSettings();
 
-	_gameFlow.minGameState = kBoot;
+	setState(kBoot);
 
 	while (!shouldQuit()) {
 		cursorManager->setCursorType(CursorManager::kNormalArrow);
 		input->processEvents();
 		
-		switch (_gameFlow.minGameState) {
-		case kBoot:
-			bootGameEngine();
-			graphicsManager->init();
-			cursorManager->init();
-			setState(kLogo);
-			break;
-		case kLogo:
-			logo->process();
-			break;
-		case kCredits:
-			credits->process();
-			break;
-		case kMainMenu: {
-			GameState prevState = getPreviousState();
-			// TODO until the game's own menus are implemented we simply open the GMM
-			openMainMenuDialog();
-			setState(prevState);
-			break;
-		}
-		case kHelp:
-			help->process();
-			break;
-		case kScene:
-			scene->process();
-			break;
-		case kMap:
-			map->process();
-			break;
-		case kCheat: {
-			if (_cheatTypeIsEventFlag) {
-				EventFlagDialog *dialog = new EventFlagDialog(this);
-				dialog->runModal();
-				delete dialog;
-			} else {
-				CheatDialog *dialog = new CheatDialog(this);
-				dialog->runModal();
-				delete dialog;
-			}
-			setState(getPreviousState());
-			input->forceCleanInput();
-			break;
-		}
-		case kNone:
-			break;
-		default:
-			break;
+		if (_gameFlow.currentState) {
+			_gameFlow.currentState->process();
 		}
 
 		graphicsManager->draw();
 
-		if (_gameFlow.justChanged) {
-			_gameFlow.justChanged = false;
-		} else {
-			_gameFlow.previousGameState = _gameFlow.minGameState;
-		}
-
 		if (launchConsole) {
 			_console->attach();
 			launchConsole = false;
@@ -225,7 +162,7 @@ Common::Error NancyEngine::run() {
 
 void NancyEngine::bootGameEngine() {
 	clearBootChunks();
-	IFF *boot = new IFF(this, "boot");
+	IFF *boot = new IFF("boot");
 	if (!boot->load())
 		error("Failed to load boot script");
 	preloadCals(*boot);
@@ -256,9 +193,41 @@ void NancyEngine::bootGameEngine() {
 	// TODO reset some vars
 	// TODO reset some more vars
 
+	// These originally get loaded inside Logo
+	SoundDescription desc;
+	desc.read(*NanEngine.getBootChunkStream("BUOK"), SoundDescription::kNormal);
+	NanEngine.sound->loadSound(desc);
+	desc.read(*NanEngine.getBootChunkStream("BUDE"), SoundDescription::kNormal);
+	NanEngine.sound->loadSound(desc);
+	desc.read(*NanEngine.getBootChunkStream("BULS"), SoundDescription::kNormal);
+	NanEngine.sound->loadSound(desc);
+	desc.read(*NanEngine.getBootChunkStream("GLOB"), SoundDescription::kNormal);
+	NanEngine.sound->loadSound(desc);
+	desc.read(*NanEngine.getBootChunkStream("CURT"), SoundDescription::kNormal);
+	NanEngine.sound->loadSound(desc);
+	desc.read(*NanEngine.getBootChunkStream("CANT"), SoundDescription::kNormal);
+	NanEngine.sound->loadSound(desc);
+
 	delete boot;
 }
 
+State::State *NancyEngine::getStateObject(GameState state) {
+	switch (state) {
+	case kLogo:
+		return &State::Logo::instance();
+	case kCredits:
+		return &State::Credits::instance();
+	case kMap:
+		return &State::Map::instance();
+	case kHelp:
+		return &State::Help::instance();
+	case kScene:
+		return &State::Scene::instance();
+	default:
+		return nullptr;
+	}
+}
+
 bool NancyEngine::addBootChunk(const Common::String &name, Common::SeekableReadStream *stream) {
 	if (!stream)
 		return false;
@@ -276,7 +245,7 @@ Common::SeekableReadStream *NancyEngine::getBootChunkStream(const Common::String
 void NancyEngine::stopAndUnloadSpecificSounds() {
 	// TODO missing if
 	
-	sound->stopSound(logo->MSNDchannelID);
+	sound->stopSound(NancyLogoState.MSNDchannelID);
 
 	for (uint i = 0; i < 10; ++i) {
 		sound->stopSound(i);
@@ -286,33 +255,9 @@ void NancyEngine::setMouseEnabled(bool enabled) {
 	cursorManager->showCursor(enabled); input->setMouseInputEnabled(enabled);
 }
 
-void NancyEngine::pauseEngineIntern(bool pause) {
-	if (pause) {
-		if (getState() == kScene) {
-			scene->requestStateChange(kPause);
-			scene->changeGameState(true);
-		} else {
-			setState(kPause, kNone, true);
-		}
-	} else {
-		setState(getPreviousState(), kNone, true);
-	}
-
-	graphicsManager->onPause(pause);
-
-	Engine::pauseEngineIntern(pause);
-}
-
 Common::Error NancyEngine::loadGameStream(Common::SeekableReadStream *stream) {
 	Common::Serializer ser(stream, nullptr);
-
-	auto ret = synchronize(ser);
-
-	if (ret.getCode() == Common::kNoError) {
-		scene->hasLoadedFromSavefile = true;
-	}
-
-	return ret;
+	return synchronize(ser);
 }
 
 bool NancyEngine::canSaveGameStateCurrently() {
@@ -351,8 +296,8 @@ Common::Error NancyEngine::synchronize(Common::Serializer &ser) {
 		ser.syncBytes(buf, 90);
 	}
 
-	scene->synchronize(ser);
-	scene->_actionManager.synchronize(ser);
+	NancySceneState.synchronize(ser);
+	NancySceneState._actionManager.synchronize(ser);
 	Action::SliderPuzzle::synchronize(ser);
 
 	return Common::kNoError;
@@ -375,7 +320,7 @@ void NancyEngine::preloadCals(const IFF &boot) {
 			stream.read(name, nameLen);
 			name[nameLen - 1] = 0;
 			debugC(1, kDebugEngine, "Preloading CAL '%s'", name);
-			if (!_res->loadCifTree(name, "cal"))
+			if (!resource->loadCifTree(name, "cal"))
 				error("Failed to preload CAL '%s'", name);
 		}
 
@@ -428,19 +373,78 @@ void NancyEngine::readImageList(const IFF &boot, const Common::String &prefix, I
 	}
 }
 
-void NancyEngine::setState(GameState state, GameState overridePrevious, bool keepGraphics) {
+void NancyEngine::setState(GameState state, GameState overridePrevious) {
+	// Handle special cases first
+	switch (state) {
+	case kBoot:
+		bootGameEngine();
+		graphicsManager->init();
+		cursorManager->init();
+		setState(kLogo);
+		return;
+	case kMainMenu:
+		if (_gameFlow.currentState) {
+			if (_gameFlow.currentState->onStateExit()) {
+				_gameFlow.currentState = nullptr;
+			}
+		}
+		
+		// TODO until the game's own menus are implemented we simply open the GMM
+		openMainMenuDialog();
+
+		if (shouldQuit()) {
+			return;
+		}
+
+		if (_gameFlow.currentState) {
+			_gameFlow.currentState->onStateEnter();
+		}
+
+		return;
+	case kCheat:
+		if (_cheatTypeIsEventFlag) {
+			EventFlagDialog *dialog = new EventFlagDialog();
+			runDialog(*dialog);
+			delete dialog;
+		} else {
+			CheatDialog *dialog = new CheatDialog();
+			runDialog(*dialog);
+			delete dialog;
+		}
+		input->forceCleanInput();
+		return;
+	default:
+		break;
+	}
+
+	graphicsManager->clearObjects();
+
+	_gameFlow.previousState = _gameFlow.currentState;
+	_gameFlow.currentState = getStateObject(state);
+
+	if (_gameFlow.previousState) {
+		_gameFlow.previousState->onStateExit();
+	}
+
+	if (_gameFlow.currentState) {
+		_gameFlow.currentState->onStateEnter();
+	}
+
 	if (overridePrevious != kNone) {
-		_gameFlow.previousGameState = overridePrevious;
-	} else {
-		_gameFlow.previousGameState = _gameFlow.minGameState;
+		_gameFlow.previousState = getStateObject(state);
+	}
+}
+
+void NancyEngine::setPreviousState() {
+	if (_gameFlow.currentState) {
+		_gameFlow.currentState->onStateExit();
 	}
-	
-	_gameFlow.minGameState = state;
-	_gameFlow.justChanged = true;
 
-	if (!keepGraphics) {
-		graphicsManager->clearObjects();
+	if (_gameFlow.previousState) {
+		_gameFlow.previousState->onStateEnter();
 	}
+
+	SWAP<Nancy::State::State *>(_gameFlow.currentState, _gameFlow.previousState);
 }
 
 class NancyEngine_v0 : public NancyEngine {
@@ -465,9 +469,10 @@ void NancyEngine_v0::readBootSummary(const IFF &boot) {
 	bsum->seek(0x1D1);
 	_fontSize = bsum->readSint32LE() * 1346;
 	bsum->seek(0x1ED);
-	scene->playerTimeMinuteLength = bsum->readSint16LE();
+	NancySceneState.playerTimeMinuteLength = bsum->readSint16LE();
 	bsum->seek(0x1F1);
     overrideMovementTimeDeltas = bsum->readByte();
+
     if (overrideMovementTimeDeltas) {
         slowMovementTimeDelta = bsum->readUint16LE();
         fastMovementTimeDelta = bsum->readUint16LE();
diff --git a/engines/nancy/nancy.h b/engines/nancy/nancy.h
index 1f8ade2ca8..d89f76b343 100644
--- a/engines/nancy/nancy.h
+++ b/engines/nancy/nancy.h
@@ -66,6 +66,7 @@ class CursorManager;
 class CheatDialog;
 
 namespace State {
+class State;
 class Logo;
 class Scene;
 class Map;
@@ -125,33 +126,24 @@ public:
 	// Used for state switching
 	void stopAndUnloadSpecificSounds();
 	
-	void setState(GameState state, GameState overridePrevious = kNone, bool keepGraphics = false);
-	GameState getState() const { return _gameFlow.minGameState; }
-	GameState getPreviousState() const { return _gameFlow.previousGameState; }
+	void setState(GameState state, GameState overridePrevious = kNone);
+	void setPreviousState();
+
 	void callCheatMenu(bool eventFlags) { setState(kCheat), _cheatTypeIsEventFlag = eventFlags; }
 
 	void setMouseEnabled(bool enabled);
 
-	virtual void pauseEngineIntern(bool pause) override;
-
 	virtual Common::Error loadGameStream(Common::SeekableReadStream *stream) override;
 	virtual Common::Error saveGameStream(Common::WriteStream *stream, bool isAutosave = false) override;
 	virtual bool canLoadGameStateCurrently() override { return canSaveGameStateCurrently(); };
 	virtual bool canSaveGameStateCurrently() override;
 
 	// Managers
-	ResourceManager *_res;
+	ResourceManager *resource;
 	GraphicsManager *graphicsManager;
 	CursorManager *cursorManager;
 	InputManager *input;
 	SoundManager *sound;
-
-	// States
-	State::Logo *logo;
-	State::Scene *scene;
-	State::Map *map;
-	State::Help *help;
-	State::Credits *credits;
 	
 	OSystem *_system;
 	Common::RandomSource *_rnd;
@@ -171,6 +163,8 @@ protected:
 
 	void bootGameEngine();
 
+	State::State *getStateObject(GameState state);
+
 	bool addBootChunk(const Common::String &name, Common::SeekableReadStream *stream);
 	void clearBootChunks();
 
@@ -187,9 +181,8 @@ protected:
 	};
 
 	struct GameFlow {
-		GameState minGameState;
-		GameState previousGameState;
-		bool justChanged = false;
+		State::State *currentState = nullptr;
+		State::State *previousState = nullptr;
 	};
 
 	typedef Common::Array<Image> ImageList;
@@ -209,8 +202,6 @@ protected:
 	virtual void readBootSummary(const IFF &boot) = 0;
 
 private:
-	static NancyEngine *s_Engine;
-
 	GameFlow _gameFlow;
 
 	NancyConsole *_console;
@@ -220,6 +211,8 @@ private:
 	Common::HashMap<Common::String, Common::SeekableReadStream *> _bootChunks;
 };
 
+#define NanEngine (*((NancyEngine *)(g_engine)))
+
 } // End of namespace Nancy
 
 #endif // NANCY_H
diff --git a/engines/nancy/renderobject.cpp b/engines/nancy/renderobject.cpp
index 311c32c9ec..bf9879663d 100644
--- a/engines/nancy/renderobject.cpp
+++ b/engines/nancy/renderobject.cpp
@@ -37,11 +37,11 @@ void RenderObject::init() {
 }
 
 void RenderObject::registerGraphics() {
-    _engine->graphicsManager->addObject(this);
+    NanEngine.graphicsManager->addObject(this);
 }
 
 RenderObject::~RenderObject() {
-    _engine->graphicsManager->removeObject(this);
+    NanEngine.graphicsManager->removeObject(this);
     if (_drawSurface.getPixels()) {
         _drawSurface.free();
     }
@@ -60,7 +60,7 @@ void RenderObject::setVisible(bool visible) {
 
 Common::Rect RenderObject::getScreenPosition() const {
     if (isViewportRelative()) {
-        return _engine->scene->getViewport().convertViewportToScreen(_screenPosition);
+        return NancySceneState.getViewport().convertViewportToScreen(_screenPosition);
     } else {
         return _screenPosition;
     }
@@ -68,7 +68,7 @@ Common::Rect RenderObject::getScreenPosition() const {
 
 Common::Rect RenderObject::getPreviousScreenPosition() const {
     if (isViewportRelative()) {
-        return _engine->scene->getViewport().convertViewportToScreen(_previousScreenPosition);
+        return NancySceneState.getViewport().convertViewportToScreen(_previousScreenPosition);
     } else {
         return _previousScreenPosition;
     }
@@ -80,10 +80,10 @@ Common::Rect RenderObject::convertToLocal(const Common::Rect &screen) const {
     Common::Point offset;
 
     if (isViewportRelative()) {
-        Common::Rect viewportScreenPos = _engine->scene->getViewport().getScreenPosition();
+        Common::Rect viewportScreenPos = NancySceneState.getViewport().getScreenPosition();
         offset.x -= viewportScreenPos.left;
         offset.y -= viewportScreenPos.top;
-        uint viewportScroll = _engine->scene->getViewport().getCurVerticalScroll();
+        uint viewportScroll = NancySceneState.getViewport().getCurVerticalScroll();
         offset.y += viewportScroll;
     }
 
@@ -101,10 +101,10 @@ Common::Rect RenderObject::convertToScreen(const Common::Rect &rect) const {
     Common::Point offset;
 
     if (isViewportRelative()) {
-        Common::Rect viewportScreenPos = _engine->scene->getViewport().getScreenPosition();
+        Common::Rect viewportScreenPos = NancySceneState.getViewport().getScreenPosition();
         offset.x += viewportScreenPos.left;
         offset.y += viewportScreenPos.top;
-        uint viewportScroll = _engine->scene->getViewport().getCurVerticalScroll();
+        uint viewportScroll = NancySceneState.getViewport().getCurVerticalScroll();
         offset.y -= viewportScroll;
     }
 
diff --git a/engines/nancy/renderobject.h b/engines/nancy/renderobject.h
index d1ed1fe5e9..74180ae171 100644
--- a/engines/nancy/renderobject.h
+++ b/engines/nancy/renderobject.h
@@ -43,14 +43,12 @@ class RenderObject {
 public:
     enum BlitType { kNoTrans, kTrans };
 
-    RenderObject(NancyEngine *engine) :
-        _engine(engine),
+    RenderObject() :
         _needsRedraw(true),
         _isVisible(true),
         _redrawFrom(nullptr) {}
 
     RenderObject(RenderObject &redrawFrom) :
-        _engine(redrawFrom._engine),
         _needsRedraw(true),
         _isVisible(true),
         _redrawFrom(&redrawFrom) {}
@@ -60,7 +58,6 @@ public:
     virtual void init(); // Does _not_ get called automatically
     virtual void registerGraphics();
     virtual void updateGraphics() {}
-    virtual void onPause(bool pause) {}
 
     void moveTo(Common::Point position);
     void setVisible(bool visible);
@@ -85,8 +82,6 @@ protected:
     // Needed for proper handling of objects inside the viewport
     virtual bool isViewportRelative() const { return false; }
 
-    NancyEngine *_engine;
-
     RenderObject *_redrawFrom;
 
     Graphics::ManagedSurface _drawSurface;
diff --git a/engines/nancy/resource.cpp b/engines/nancy/resource.cpp
index 3aa25c958c..bf81fa3aea 100644
--- a/engines/nancy/resource.cpp
+++ b/engines/nancy/resource.cpp
@@ -564,7 +564,7 @@ const CifExporter *CifExporter::create(uint32 version) {
 	return exp;
 }
 
-ResourceManager::ResourceManager(NancyEngine *vm) : _vm(vm) {
+ResourceManager::ResourceManager() {
 	_dec = new Decompressor;
 }
 
diff --git a/engines/nancy/resource.h b/engines/nancy/resource.h
index eb35c7e379..5b23ad2e6b 100644
--- a/engines/nancy/resource.h
+++ b/engines/nancy/resource.h
@@ -59,7 +59,7 @@ public:
 		uint32 compressedSize, size;
 	};
 
-	ResourceManager(NancyEngine *vm);
+	ResourceManager();
 	~ResourceManager();
 
 	void initialize();
@@ -74,7 +74,6 @@ public:
 	bool exportCif(const Common::String &treeName, const Common::String &name);
 	Common::String getCifDescription(const Common::String &treeName, const Common::String &name);
 private:
-	NancyEngine *_vm;
 	Decompressor *_dec;
 
 	byte *getCifData(const Common::String &treeName, const Common::String &name, CifInfo &info, uint *size = nullptr);
diff --git a/engines/nancy/sound.cpp b/engines/nancy/sound.cpp
index b1af23e158..dd8f1e0873 100644
--- a/engines/nancy/sound.cpp
+++ b/engines/nancy/sound.cpp
@@ -176,9 +176,8 @@ Audio::SeekableAudioStream *SoundManager::makeHISStream(Common::SeekableReadStre
 		return Audio::makeVorbisStream(subStream, DisposeAfterUse::YES);
 }
 
-SoundManager::SoundManager(NancyEngine *engine) :
-		_engine(engine) {
-	_mixer = _engine->_system->getMixer();
+SoundManager::SoundManager() {
+	_mixer = NanEngine._system->getMixer();
 
 	initSoundChannels();
 }
@@ -235,7 +234,7 @@ void SoundManager::pauseSound(uint16 channelID, bool pause) {
 		return;
 
 	if (isSoundPlaying(channelID)) {
-		_engine->_system->getMixer()->pauseHandle(_channels[channelID].handle, pause);
+		NanEngine._system->getMixer()->pauseHandle(_channels[channelID].handle, pause);
 	}
 }
 
diff --git a/engines/nancy/sound.h b/engines/nancy/sound.h
index 9eb5eece9d..ce53cfb25c 100644
--- a/engines/nancy/sound.h
+++ b/engines/nancy/sound.h
@@ -44,7 +44,7 @@ class NancyEngine;
 
 class SoundManager {
 public:
-    SoundManager(NancyEngine *engine);
+    SoundManager();
     ~SoundManager();
 
     // Load a sound into a channel without starting it
@@ -73,7 +73,6 @@ protected:
     };
 
     void initSoundChannels();
-    NancyEngine *_engine;
     Audio::Mixer *_mixer;
 
     Channel _channels[32];
diff --git a/engines/nancy/state/credits.cpp b/engines/nancy/state/credits.cpp
index 1b0e345c19..72e2a00e98 100644
--- a/engines/nancy/state/credits.cpp
+++ b/engines/nancy/state/credits.cpp
@@ -32,6 +32,10 @@
 
 #include "common/stream.h"
 
+namespace Common {
+DECLARE_SINGLETON(Nancy::State::Credits);
+}
+
 namespace Nancy {
 namespace State {
 
@@ -47,7 +51,7 @@ void Credits::process() {
 }
 
 void Credits::init() {
-    Common::SeekableReadStream *cred = _engine->getBootChunkStream("CRED");
+    Common::SeekableReadStream *cred = NanEngine.getBootChunkStream("CRED");
     cred->seek(0);
 
     char buf[10];
@@ -63,7 +67,7 @@ void Credits::init() {
     _sound.read(*cred, SoundDescription::kMenu);
 
     Graphics::Surface surf;
-    _engine->_res->loadImage("ciftree", buf, surf);
+    NanEngine.resource->loadImage("ciftree", buf, surf);
     _fullTextSurface.create(surf.w, surf.h + _text._screenPosition.height() * 2, GraphicsManager::pixelFormat);
     _fullTextSurface.clear(GraphicsManager::transColor);
     _fullTextSurface.blitFrom(surf, Common::Point(0, _text._screenPosition.height()));
@@ -74,35 +78,29 @@ void Credits::init() {
     _text._drawSurface.create(_fullTextSurface, src);
     _text.init();
 
-    _engine->sound->loadSound(_sound);
-    _engine->sound->playSound(_sound);
-
-    _returnToState = _engine->getPreviousState();
+    NanEngine.sound->loadSound(_sound);
+    NanEngine.sound->playSound(_sound);
 
     _background.registerGraphics();
     _text.registerGraphics();
 
-    _engine->cursorManager->showCursor(false);
+    NanEngine.cursorManager->showCursor(false);
 
     _state = kRun;
 }
 
 void Credits::run() {
-    NancyInput input = _engine->input->getInput();
+    NancyInput input = NanEngine.input->getInput();
 
     if (input.input & NancyInput::kLeftMouseButtonDown) {
         _state = kInit;
-        _engine->sound->stopSound(_sound);
-        if (_returnToState == NancyEngine::kMainMenu) {
-            _engine->setState((NancyEngine::GameState)_returnToState, NancyEngine::kScene);
-        } else {
-            _engine->setState((NancyEngine::GameState)_returnToState);
-        }
-        _engine->cursorManager->showCursor(true);
+        NanEngine.sound->stopSound(_sound);
+        NanEngine.setState(NancyEngine::kMainMenu);
+        NanEngine.cursorManager->showCursor(true);
         _fullTextSurface.free();
     }
 
-    Time currentTime = _engine->getTotalPlayTime();
+    Time currentTime = NanEngine.getTotalPlayTime();
     if (currentTime >= _nextUpdateTime) {
         _nextUpdateTime = currentTime + _updateTime;
 
diff --git a/engines/nancy/state/credits.h b/engines/nancy/state/credits.h
index bd8154db5b..996298cb2f 100644
--- a/engines/nancy/state/credits.h
+++ b/engines/nancy/state/credits.h
@@ -23,12 +23,15 @@
 #ifndef NANCY_STATE_CREDITS_H
 #define NANCY_STATE_CREDITS_H
 
+#include "engines/nancy/state/state.h"
+
 #include "engines/nancy/ui/fullscreenimage.h"
 
 #include "engines/nancy/time.h"
 #include "engines/nancy/commontypes.h"
 
 #include "common/rect.h"
+#include "common/singleton.h"
 
 #include "graphics/managed_surface.h"
 
@@ -38,12 +41,14 @@ class NancyEngine;
 
 namespace State {
 
-class Credits {
+class Credits : public State, public Common::Singleton<Credits> {
 public:
     enum State { kInit, kRun };
-    Credits(NancyEngine *engine) : _engine(engine), _state(kInit), _background(_engine), _text(_background) {}
+    Credits() : _state(kInit), _background(), _text(_background) {}
 
-    void process();
+    // State API
+    virtual void process() override;
+    virtual bool onStateExit() override { destroy(); return true; };
 
 protected:
     void init();
@@ -60,19 +65,19 @@ protected:
         virtual BlitType getBlitType() const override { return kTrans; }
     };
 
-    NancyEngine *_engine;
     State _state;
     UI::FullScreenImage _background;
     CreditsText _text;
     Time _nextUpdateTime;
     Graphics::ManagedSurface _fullTextSurface;
-    uint _returnToState;
 
     Time _updateTime; // 0x54
     uint16 _pixelsToScroll; // 0x56
     SoundDescription _sound; // 0x58, kMenu?
 };
 
+#define NancyCreditsState Nancy::State::Credits::instance()
+
 } // End of namespace State
 } // End of namespace Nancy
 
diff --git a/engines/nancy/state/help.cpp b/engines/nancy/state/help.cpp
index 7c506308f4..929e3a87d9 100644
--- a/engines/nancy/state/help.cpp
+++ b/engines/nancy/state/help.cpp
@@ -30,6 +30,10 @@
 
 #include "common/stream.h"
 
+namespace Common {
+DECLARE_SINGLETON(Nancy::State::Help);
+}
+
 namespace Nancy {
 namespace State {
 
@@ -51,7 +55,7 @@ void Help::process() {
 }
 
 void Help::init() {
-    Common::SeekableReadStream *chunk = _engine->getBootChunkStream("HELP");
+    Common::SeekableReadStream *chunk = NanEngine.getBootChunkStream("HELP");
 
     chunk->seek(0);
     char buf[10];
@@ -64,7 +68,7 @@ void Help::init() {
     _hotspot.right = chunk->readUint16LE();
     _hotspot.bottom = chunk->readUint16LE();
     
-    chunk = _engine->getBootChunkStream("MSND");
+    chunk = NanEngine.getBootChunkStream("MSND");
     chunk->seek(0);
 	_sound.read(*chunk, SoundDescription::kMenu);
 
@@ -72,33 +76,30 @@ void Help::init() {
 }
 
 void Help::begin() {
-	_engine->sound->loadSound(_sound);
-	_engine->sound->playSound(_sound);
+	NanEngine.sound->loadSound(_sound);
+	NanEngine.sound->playSound(_sound);
     
     _image.registerGraphics();
     _image.setVisible(true);
 
-    _engine->cursorManager->setCursorType(CursorManager::kNormalArrow);
-    _previousState = _engine->getPreviousState();
+    NanEngine.cursorManager->setCursorType(CursorManager::kNormalArrow);
     
     _state = kRun;
 }
 
 void Help::run() {
-    NancyInput input = _engine->input->getInput();
+    NancyInput input = NanEngine.input->getInput();
 
     if (_hotspot.contains(input.mousePos) && input.input & NancyInput::kLeftMouseButtonUp) {
-        _engine->sound->playSound(0x18); // Hardcoded by original engine
+        NanEngine.sound->playSound(0x18); // Hardcoded by original engine
         _state = kWaitForSound;
     }
 }
 
 void Help::waitForSound() {
-    if (!_engine->sound->isSoundPlaying(18)) {
-        _engine->setState((NancyEngine::GameState)_previousState);
-        
-	    _engine->sound->stopSound(_sound);
-        _state = kBegin;
+    if (!NanEngine.sound->isSoundPlaying(18)) {
+	    NanEngine.sound->stopSound(_sound);
+        NanEngine.setPreviousState();
     }
 }
 
diff --git a/engines/nancy/state/help.h b/engines/nancy/state/help.h
index 57d1e5209b..5add2d39f3 100644
--- a/engines/nancy/state/help.h
+++ b/engines/nancy/state/help.h
@@ -23,11 +23,14 @@
 #ifndef NANCY_STATE_HELP_H
 #define NANCY_STATE_HELP_H
 
+#include "engines/nancy/state/state.h"
+
 #include "engines/nancy/ui/fullscreenimage.h"
 
 #include "engines/nancy/commontypes.h"
 
 #include "common/rect.h"
+#include "common/singleton.h"
 
 namespace Nancy {
 
@@ -35,12 +38,14 @@ class NancyEngine;
 
 namespace State {
 
-class Help {
+class Help : public State, public Common::Singleton<Help> {
 public:
     enum State { kInit, kBegin, kRun, kWaitForSound };
-    Help(NancyEngine *engine) : _engine(engine), _state(kInit), _image(engine) {}
+    Help() : _state(kInit), _image() {}
 
-    void process();
+    // State API
+    virtual void process() override;
+    virtual bool onStateExit() override { destroy(); return true; };
 
 private:
     void init();
@@ -48,14 +53,14 @@ private:
     void run();
     void waitForSound();
 
-    NancyEngine *_engine;
     State _state;
     UI::FullScreenImage _image;
     Common::Rect _hotspot; // Can be an array, but isn't in nancy1
     SoundDescription _sound;
-    uint _previousState;
 };
 
+#define NancyHelpState Nancy::State::Help::instance()
+
 } // End of namespace State
 } // End of namespace Nancy
 
diff --git a/engines/nancy/state/logo.cpp b/engines/nancy/state/logo.cpp
index 0fea18279a..c9ef18fe9e 100644
--- a/engines/nancy/state/logo.cpp
+++ b/engines/nancy/state/logo.cpp
@@ -37,6 +37,10 @@
 
 #include "graphics/surface.h"
 
+namespace Common {
+DECLARE_SINGLETON(Nancy::State::Logo);
+}
+
 namespace Nancy {
 namespace State {
 
@@ -59,44 +63,32 @@ void Logo::process() {
 void Logo::init() {
 	_surf = new Graphics::Surface;
 
-	if (!_engine->_res->loadImage("ciftree", _engine->_logos[0].name, *_surf))
-		error("Failed to load %s", _engine->_logos[0].name.c_str());
+	if (!NanEngine.resource->loadImage("ciftree", NanEngine._logos[0].name, *_surf))
+		error("Failed to load %s", NanEngine._logos[0].name.c_str());
 
 	_state = kStartSound;
 }
 
 void Logo::startSound() {
 	SoundDescription desc;
-	desc.read(*_engine->getBootChunkStream("MSND"), SoundDescription::kMenu);
-	_engine->sound->loadSound(desc);
+	desc.read(*NanEngine.getBootChunkStream("MSND"), SoundDescription::kMenu);
+	NanEngine.sound->loadSound(desc);
 	MSNDchannelID = desc.channelID;
-	desc.read(*_engine->getBootChunkStream("BUOK"), SoundDescription::kNormal);
-	_engine->sound->loadSound(desc);
-	desc.read(*_engine->getBootChunkStream("BUDE"), SoundDescription::kNormal);
-	_engine->sound->loadSound(desc);
-	desc.read(*_engine->getBootChunkStream("BULS"), SoundDescription::kNormal);
-	_engine->sound->loadSound(desc);
-	desc.read(*_engine->getBootChunkStream("GLOB"), SoundDescription::kNormal);
-	_engine->sound->loadSound(desc);
-	desc.read(*_engine->getBootChunkStream("CURT"), SoundDescription::kNormal);
-	_engine->sound->loadSound(desc);
-	desc.read(*_engine->getBootChunkStream("CANT"), SoundDescription::kNormal);
-	_engine->sound->loadSound(desc);
-
-	_engine->sound->playSound(MSNDchannelID);
-
-	_startTicks = _engine->_system->getMillis();
+
+	NanEngine.sound->playSound(MSNDchannelID);
+
+	_startTicks = NanEngine._system->getMillis();
 	_state = kRun;
 }
 
 void Logo::run() {
 	switch (_runState) {
 	case kBlit:
-		_engine->_system->copyRectToScreen(_surf->getPixels(), _surf->pitch, 0, 0, _surf->w, _surf->h);
+		NanEngine._system->copyRectToScreen(_surf->getPixels(), _surf->pitch, 0, 0, _surf->w, _surf->h);
 		_runState = kWait;
 		break;
 	case kWait:
-		if (_engine->_system->getMillis() - _startTicks >= 7000 || (_engine->input->getInput().input & NancyInput::kLeftMouseButtonDown))
+		if (NanEngine._system->getMillis() - _startTicks >= 7000 || (NanEngine.input->getInput().input & NancyInput::kLeftMouseButtonDown))
 			_state = kStop;
 	}
 }
@@ -109,8 +101,10 @@ void Logo::stop() {
 	// For the N+C key combo it looks for some kind of cheat file
 	// to initialize the game state with.
 
-	_engine->setState(NancyEngine::kScene);
-	_engine->_system->fillScreen(0);
+	NanEngine.sound->stopSound(MSNDchannelID);
+
+	NanEngine.setState(NancyEngine::kScene);
+	NanEngine._system->fillScreen(0);
 }
 
 } // End of namespace State
diff --git a/engines/nancy/state/logo.h b/engines/nancy/state/logo.h
index fbc612839d..15339d99fa 100644
--- a/engines/nancy/state/logo.h
+++ b/engines/nancy/state/logo.h
@@ -23,7 +23,9 @@
 #ifndef NANCY_STATE_LOGO_H
 #define NANCY_STATE_LOGO_H
 
-#include "common/scummsys.h"
+#include "engines/nancy/state/state.h"
+
+#include "common/singleton.h"
 
 namespace Graphics {
 	struct Surface;
@@ -35,16 +37,17 @@ class NancyEngine;
 
 namespace State {
 
-class Logo {
+class Logo : public State, public Common::Singleton<Logo> {
 public:
-	Logo(NancyEngine *engine) :
-		_engine(engine),
+	Logo() :
 		_state(kInit),
 		_runState(kBlit),
 		_startTicks(0),
 		_surf(nullptr) { }
 		
-	void process();
+	// State API
+    virtual void process() override;
+    virtual bool onStateExit() override { destroy(); return true; };
 
 	uint MSNDchannelID; // This definitely shouldn't be here
 
@@ -67,13 +70,14 @@ private:
 		kWait
 	};
 
-	NancyEngine *_engine;
 	State _state;
 	RunState _runState;
 	uint _startTicks;
 	Graphics::Surface *_surf;
 };
 
+#define NancyLogoState Nancy::State::Logo::instance()
+
 } // end of namespace State
 } // End of namespace Nancy
 
diff --git a/engines/nancy/state/map.cpp b/engines/nancy/state/map.cpp
index 98e64f982d..f1f2efa913 100644
--- a/engines/nancy/state/map.cpp
+++ b/engines/nancy/state/map.cpp
@@ -35,6 +35,10 @@
 #include "common/stream.h"
 #include "common/str.h"
 
+namespace Common {
+DECLARE_SINGLETON(Nancy::State::Map);
+}
+
 namespace Nancy {
 namespace State {
 
@@ -46,21 +50,18 @@ void Map::process() {
     case kRun:
         run();
         break;
-    case kStop:
-        stop();
-        break;
     }
 }
 
 void Map::init() {
-    Common::SeekableReadStream *chunk = _engine->getBootChunkStream("MAP");
+    Common::SeekableReadStream *chunk = NanEngine.getBootChunkStream("MAP");
 
     _viewport.init();
     _label.init();
     _button.init();
 
-    if (_engine->scene->getEventFlag(40, kTrue) && // Has set up sting
-        _engine->scene->getEventFlag(95, kTrue)) { // Connie chickens
+    if (NancySceneState.getEventFlag(40, kTrue) && // Has set up sting
+        NancySceneState.getEventFlag(95, kTrue)) { // Connie chickens
         _mapID = 1;
     } else {
         _mapID = 0;
@@ -79,8 +80,8 @@ void Map::init() {
     chunk->seek(0x18 + _mapID * 0x20, SEEK_SET);
     SoundDescription sound;
     sound.read(*chunk, SoundDescription::kMenu);
-    _engine->sound->loadSound(sound);
-    _engine->sound->playSound(0x14);
+    NanEngine.sound->loadSound(sound);
+    NanEngine.sound->playSound(0x14);
 
     _locations.clear();
 
@@ -115,37 +116,37 @@ void Map::init() {
     }
 
     registerGraphics();
-    _engine->cursorManager->setCursorItemID(-1);
+    NanEngine.cursorManager->setCursorItemID(-1);
 
     _state = kRun;
 }
 
 void Map::run() {
-    if (!_engine->sound->isSoundPlaying(0x14) && !_engine->sound->isSoundPlaying(0x13)) {
-        _engine->sound->playSound(0x13);
+    if (!NanEngine.sound->isSoundPlaying(0x14) && !NanEngine.sound->isSoundPlaying(0x13)) {
+        NanEngine.sound->playSound(0x13);
     }
 
-    NancyInput input = _engine->input->getInput();
+    NancyInput input = NanEngine.input->getInput();
 
     _label.setLabel(-1);
 
     _button.handleInput(input);
 
     if (_mapButtonClicked) {
-        _state = kStop;
+        NanEngine.setState(NancyEngine::kScene);
         return;
     }
 
     for (uint i = 0; i < 4; ++i) {
         auto &loc = _locations[i];
         if (loc.isActive && _viewport.convertToScreen(loc.hotspot).contains(input.mousePos)) {
-            _engine->cursorManager->setCursorType(CursorManager::kHotspotArrow);
+            NanEngine.cursorManager->setCursorType(CursorManager::kHotspotArrow);
 
             _label.setLabel(i);
 
             if (input.input & NancyInput::kLeftMouseButtonUp) {
                 _pickedLocationID = i;
-                _state = kStop;
+                NanEngine.setState(NancyEngine::kScene);
             }
 
             return;
@@ -153,29 +154,30 @@ void Map::run() {
     }
 }
 
-void Map::stop() {
-    Common::SeekableReadStream *chunk = _engine->getBootChunkStream("MAP");
+bool Map::onStateExit() {
+    Common::SeekableReadStream *chunk = NanEngine.getBootChunkStream("MAP");
     SoundDescription sound;
     chunk->seek(0x18 + _mapID * 0x20, SEEK_SET);
     sound.read(*chunk, SoundDescription::kMenu);
-    _engine->sound->stopSound(sound);
+    NanEngine.sound->stopSound(sound);
     
-    _engine->setState(NancyEngine::kScene);
+    NanEngine.setState(NancyEngine::kScene);
 
     if (_pickedLocationID != -1) {
         auto &loc = _locations[_pickedLocationID];
-        _engine->scene->changeScene(loc.scenes[_mapID].sceneID, loc.scenes[_mapID].frameID, loc.scenes[_mapID].verticalOffset, false);
+        NancySceneState.changeScene(loc.scenes[_mapID].sceneID, loc.scenes[_mapID].frameID, loc.scenes[_mapID].verticalOffset, false);
         _pickedLocationID = -1;
         
-        _engine->sound->playSound(0x18);
+        NanEngine.sound->playSound(0x18);
     }
     
     // The two sounds play at the same time if a location was picked
-    _engine->sound->playSound(0x14);
+    NanEngine.sound->playSound(0x14);
 
     _mapButtonClicked = false;
 
-    _state = kInit;
+    destroy();
+    return true;
 }
 
 void Map::registerGraphics() {
@@ -195,18 +197,18 @@ void Map::MapLabel::setLabel(int labelID) {
         setVisible(false);
     } else {
         _screenPosition = _parent->_locations[labelID].labelDest;
-        _drawSurface.create(_engine->graphicsManager->object0, _parent->_locations[labelID].labelSrc);
+        _drawSurface.create(NanEngine.graphicsManager->object0, _parent->_locations[labelID].labelSrc);
         setVisible(true);
     }
 }
 
 void Map::MapButton::init() {
-    Common::SeekableReadStream *map = _engine->getBootChunkStream("MAP");
+    Common::SeekableReadStream *map = NanEngine.getBootChunkStream("MAP");
 
     map->seek(0x7A, SEEK_SET);
     Common::Rect src;
     readRect(*map, src);
-    _drawSurface.create(_engine->graphicsManager->object0, src);
+    _drawSurface.create(NanEngine.graphicsManager->object0, src);
     readRect(*map, _screenPosition);
     setVisible(true);
 
diff --git a/engines/nancy/state/map.h b/engines/nancy/state/map.h
index 4478e32e73..63edbc14d3 100644
--- a/engines/nancy/state/map.h
+++ b/engines/nancy/state/map.h
@@ -23,6 +23,8 @@
 #ifndef NANCY_STATE_MAP_H
 #define NANCY_STATE_MAP_H
 
+#include "engines/nancy/state/state.h"
+
 #include "engines/nancy/ui/viewport.h"
 #include "engines/nancy/ui/button.h"
 
@@ -34,6 +36,7 @@
 #include "common/str.h"
 #include "common/array.h"
 #include "common/rect.h"
+#include "common/singleton.h"
 
 #include "graphics/surface.h"
 
@@ -43,22 +46,22 @@ class NancyEngine;
 
 namespace State {
 
-class Map {
+class Map : public State, public Common::Singleton<Map> {
     friend class MapLabel;
     friend class MapButton;
 public:
-    enum State { kInit, kRun, kStop };
-    Map(Nancy::NancyEngine *engine) :
-        _engine(engine),
-        _state(kInit),
-        _mapID(0),
-        _mapButtonClicked(false),
-        _pickedLocationID(-1),
-        _viewport(engine),
-        _label(engine->scene->getFrame(), this),
-        _button(engine->scene->getFrame(), this) {}
-
-    void process();
+    enum State { kInit, kRun };
+    Map() : _state(kInit),
+            _mapID(0),
+            _mapButtonClicked(false),
+            _pickedLocationID(-1),
+            _viewport(),
+            _label(NancySceneState.getFrame(), this),
+            _button(NancySceneState.getFrame(), this) {}
+
+    // State API
+    virtual void process() override;
+    virtual bool onStateExit() override;
 
 private:
     struct Location {
@@ -108,12 +111,9 @@ private:
 
     void init();
     void run();
-    void stop();
 
     void registerGraphics();
 
-    Nancy::NancyEngine *_engine;
-
     Nancy::UI::Viewport _viewport;
     MapLabel _label;
     MapButton _button;
@@ -125,6 +125,8 @@ private:
     Common::Array<Location> _locations;
 };
 
+#define NancyMapState Nancy::State::Map::instance()
+
 } // End of namespace State
 } // End of namespace Nancy
 
diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index 3932bb8076..e3198f1e2e 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -42,6 +42,10 @@
 
 #include "graphics/surface.h"
 
+namespace Common {
+DECLARE_SINGLETON(Nancy::State::Scene);
+}
+
 namespace Nancy {
 namespace State{
 
@@ -59,9 +63,9 @@ void Scene::process() {
     case kStartSound:
         _state = kRun;
         if (!_sceneState._doNotStartSound) {
-            _engine->stopAndUnloadSpecificSounds();
-            _engine->sound->loadSound(_sceneState.summary.sound);
-            _engine->sound->playSound(_sceneState.summary.sound);
+            NanEngine.stopAndUnloadSpecificSounds();
+            NanEngine.sound->loadSound(_sceneState.summary.sound);
+            NanEngine.sound->playSound(_sceneState.summary.sound);
         }
         // fall through
     case kRun:
@@ -70,6 +74,31 @@ void Scene::process() {
     }
 }
 
+void Scene::onStateEnter() {
+    if (_state != kInit) {
+        registerGraphics();
+        _actionManager.onPause(false);
+
+        NanEngine.graphicsManager->redrawAll();
+
+        // Run once to clear out the previous scene when coming from Map
+        process();
+
+        NanEngine.setTotalPlayTime((uint32)_timers.pushedPlayTime);
+
+        unpauseSceneSpecificSounds();
+    }
+}
+
+bool Scene::onStateExit() {
+    _timers.pushedPlayTime = NanEngine.getTotalPlayTime();
+    _actionManager.onPause(true);
+    pauseSceneSpecificSounds();
+    _gameStateRequested = NancyEngine::kNone;
+
+    return false;
+}
+
 void Scene::changeScene(uint16 id, uint16 frame, uint16 verticalOffset, bool noSound) {
     if (id == 9999) {
         return;
@@ -100,13 +129,13 @@ void Scene::pauseSceneSpecificSounds() {
     // TODO missing if, same condition as the one in NancyEngine::stopAndUnloadSpecificSounds
 
     for (uint i = 0; i < 10; ++i) {
-		_engine->sound->pauseSound(i, true);
+		NanEngine.sound->pauseSound(i, true);
 	}
 }
 
 void Scene::unpauseSceneSpecificSounds() {
     for (uint i = 0; i < 10; ++i) {
-		_engine->sound->pauseSound(i, false);
+		NanEngine.sound->pauseSound(i, false);
 	}
 }
 
@@ -136,10 +165,9 @@ void Scene::registerGraphics() {
     _inventoryBox.registerGraphics();
     _menuButton.registerGraphics();
 
-    _engine->graphicsManager->redrawAll();
-
-    // Used to clear the map label
-    _textbox.setVisible(false);
+    _textbox.setVisible(!_shouldClearTextbox);
+    _menuButton.setVisible(false);
+    _helpButton.setVisible(false);
 }
 
 void Scene::synchronize(Common::Serializer &ser) {
@@ -196,7 +224,7 @@ void Scene::synchronize(Common::Serializer &ser) {
 	// TODO hardcoded inventory size
 	ser.syncArray(_flags.items, 11, Common::Serializer::Byte);
     ser.syncAsSint16LE(_flags.heldItem);
-    _engine->cursorManager->setCursorItemID(_flags.heldItem);
+    NanEngine.cursorManager->setCursorItemID(_flags.heldItem);
 
 	ser.syncAsUint32LE(_timers.lastTotalTime);
 	ser.syncAsUint32LE(_timers.sceneTime);
@@ -206,7 +234,7 @@ void Scene::synchronize(Common::Serializer &ser) {
 	ser.syncAsByte(_timers.timerIsActive);
     ser.syncAsByte(_timers.timeOfDay);
 
-    _engine->setTotalPlayTime((uint32)_timers.lastTotalTime);
+    NanEngine.setTotalPlayTime((uint32)_timers.lastTotalTime);
 
 	// TODO hardcoded number of event flags
 	ser.syncArray(_flags.eventFlags, 168, Common::Serializer::Byte);
@@ -233,7 +261,7 @@ void Scene::init() {
     }
 
     _timers.lastTotalTime = 0;
-    _timers.playerTime = _engine->startTimeHours * 3600000;
+    _timers.playerTime = NanEngine.startTimeHours * 3600000;
     _timers.sceneTime = 0;
     _timers.timerTime = 0;
     _timers.timerIsActive = false;
@@ -241,9 +269,9 @@ void Scene::init() {
     _timers.pushedPlayTime = 0;
     _timers.timeOfDay = Timers::kDay;
 
-    _sceneState.nextScene.sceneID = _engine->firstSceneID;
+    _sceneState.nextScene.sceneID = NanEngine.firstSceneID;
 
-    Common::SeekableReadStream *chunk = _engine->getBootChunkStream("HINT");
+    Common::SeekableReadStream *chunk = NanEngine.getBootChunkStream("HINT");
     chunk->seek(0);
 
     _hintsRemaining.clear();
@@ -257,10 +285,12 @@ void Scene::init() {
     Action::SliderPuzzle::playerHasTriedPuzzle = false;
 
     _state = kInitStatic;
+
+    registerGraphics();
 }
 
 void Scene::initStatic() {
-    Common::SeekableReadStream *chunk = _engine->getBootChunkStream("MAP");
+    Common::SeekableReadStream *chunk = NanEngine.getBootChunkStream("MAP");
     chunk->seek(0x8A);
     readRect(*chunk, _mapHotspot);
 
@@ -281,7 +311,7 @@ void Scene::initStatic() {
     _inventoryBox.init();
     _menuButton.init();
     _helpButton.init();
-    _engine->cursorManager->showCursor(true);
+    NanEngine.cursorManager->showCursor(true);
 
     _state = kLoad;
 }
@@ -291,7 +321,7 @@ void Scene::load() {
 
     // Scene IDs are prefixed with S inside the cif tree; e.g 100 -> S100                                                                                    
     Common::String sceneName = Common::String::format("S%u", _sceneState.nextScene.sceneID);
-    IFF sceneIFF(_engine, sceneName);
+    IFF sceneIFF(sceneName);
 
 	if (!sceneIFF.load()) {
 		error("Faled to load IFF %s", sceneName.c_str());
@@ -370,27 +400,13 @@ void Scene::run() {
     isComingFromMenu = false;
 
 
-    if (changeGameState()) {
-        return;
-    }
-
-    // Do some work if we're coming from a different game state
-    if (_engine->getState() != _engine->getPreviousState()) {
-        // If the GMM was on we shouldn't reregister graphics
-        if (_engine->getPreviousState() != Nancy::NancyEngine::kPause) {
-            registerGraphics();
-        }
-
-        _engine->setTotalPlayTime((uint32)_timers.pushedPlayTime);
-
-        unpauseSceneSpecificSounds();
-        _menuButton.setVisible(false);
-        _helpButton.setVisible(false);
+    if (_gameStateRequested != NancyEngine::kNone) {
+        NanEngine.setState(_gameStateRequested);
 
         return;
     }
 
-    Time currentPlayTime = _engine->getTotalPlayTime();
+    Time currentPlayTime = NanEngine.getTotalPlayTime();
 
     Time deltaTime = currentPlayTime - _timers.lastTotalTime;
     _timers.lastTotalTime = currentPlayTime;
@@ -417,7 +433,7 @@ void Scene::run() {
     }
 
     // Update the UI elements and handle input
-    NancyInput input = _engine->input->getInput();
+    NancyInput input = NanEngine.input->getInput();
     _viewport.handleInput(input);
     _menuButton.handleInput(input);
     _helpButton.handleInput(input);
@@ -432,7 +448,7 @@ void Scene::run() {
     for (uint i = 0; i < _mapAccessSceneIDs.size(); ++i) {
         if (_sceneState.currentScene.sceneID == _mapAccessSceneIDs[i]) {
             if (_mapHotspot.contains(input.mousePos)) {
-                _engine->cursorManager->setCursorType(CursorManager::kHotspotArrow);
+                NanEngine.cursorManager->setCursorType(CursorManager::kHotspotArrow);
 
                 if (input.input & NancyInput::kLeftMouseButtonUp) {
                     requestStateChange(NancyEngine::kMap);
@@ -470,27 +486,14 @@ void Scene::readSceneSummary(Common::SeekableReadStream &stream) {
     _sceneState.summary.slowMoveTimeDelta = stream.readUint16LE();
     _sceneState.summary.fastMoveTimeDelta = stream.readUint16LE();
 
-    if (_engine->overrideMovementTimeDeltas) {
-        _sceneState.summary.slowMoveTimeDelta = _engine->slowMovementTimeDelta;
-        _sceneState.summary.fastMoveTimeDelta = _engine->fastMovementTimeDelta;
+    if (NanEngine.overrideMovementTimeDeltas) {
+        _sceneState.summary.slowMoveTimeDelta = NanEngine.slowMovementTimeDelta;
+        _sceneState.summary.fastMoveTimeDelta = NanEngine.fastMovementTimeDelta;
     }
 
     delete[] buf;
 }
 
-bool Scene::changeGameState(bool keepGraphics) {
-    if (_gameStateRequested != NancyEngine::kScene) {
-        _timers.pushedPlayTime = _engine->getTotalPlayTime();
-        _engine->setState(_gameStateRequested, NancyEngine::kNone, keepGraphics);
-        _gameStateRequested = NancyEngine::kScene;
-        pauseSceneSpecificSounds();
-
-        return true;
-    }
-
-    return false;
-}
-
 void Scene::clearSceneData() {
     // only clear select flags
     for (uint i = 44; i < 54; ++i) {
@@ -534,7 +537,7 @@ bool Scene::getEventFlag(EventFlagDescription eventFlag) const {
 void Scene::setLogicCondition(int16 label, NancyFlag flag) {
     if (label > -1) {
         _flags.logicConditions[label].flag = flag;
-        _flags.logicConditions[label].timestamp = _engine->getTotalPlayTime();
+        _flags.logicConditions[label].timestamp = NanEngine.getTotalPlayTime();
     }
 }
 
diff --git a/engines/nancy/state/scene.h b/engines/nancy/state/scene.h
index b54859da7f..d3bb9a284a 100644
--- a/engines/nancy/state/scene.h
+++ b/engines/nancy/state/scene.h
@@ -23,6 +23,8 @@
 #ifndef NANCY_STATE_SCENE_H
 #define NANCY_STATE_SCENE_H
 
+#include "engines/nancy/state/state.h"
+
 #include "engines/nancy/action/actionmanager.h"
 
 #include "engines/nancy/ui/fullscreenimage.h"
@@ -39,6 +41,7 @@
 #include "common/scummsys.h"
 #include "common/array.h"
 #include "common/str.h"
+#include "common/singleton.h"
 
 namespace Graphics {
 	struct Surface;
@@ -61,7 +64,7 @@ struct SceneInfo {
     uint16 verticalOffset = 0;
 };
 
-class Scene {
+class Scene : public State, public Common::Singleton<Scene> {
     friend class Nancy::Action::ActionRecord;
     friend class Nancy::Action::ActionManager;
     friend class Nancy::NancyConsole;
@@ -85,20 +88,22 @@ public:
         //
     };
 
-    Scene(Nancy::NancyEngine *engine) :
-        _engine (engine),
+    Scene() :
         _state (kInit),
-        _frame(engine),
+        _frame(),
         _lastHint(-1),
-        _gameStateRequested(NancyEngine::kScene),
-        _viewport(engine),
+        _gameStateRequested(NancyEngine::kNone),
+        _viewport(),
         _textbox(_frame),
         _inventoryBox(_frame),
         _menuButton(_frame),
         _helpButton(_frame),
-        _actionManager(engine) {}
+        _actionManager() {}
 
-    void process();
+    // State API
+    virtual void process() override;
+    virtual void onStateEnter() override;
+    virtual bool onStateExit() override;
 
     void changeScene(uint16 id, uint16 frame, uint16 verticalOffset, bool noSound);
     void changeScene(const SceneChangeDescription &sceneDescription);
@@ -108,10 +113,12 @@ public:
     void pauseSceneSpecificSounds();
     void unpauseSceneSpecificSounds();
 
+    void setShouldClearTextbox(bool shouldClear) { _shouldClearTextbox = shouldClear; }
+
     void addItemToInventory(uint16 id);
     void removeItemFromInventory(uint16 id, bool pickUp = true);
     int16 getHeldItem() const { return _flags.heldItem; }
-    void setHeldItem(int16 id) { _flags.heldItem = id; _engine->cursorManager->setCursorItemID(id); }
+    void setHeldItem(int16 id) { _flags.heldItem = id; NanEngine.cursorManager->setCursorItemID(id); }
     NancyFlag hasItem(int16 id) const { return _flags.items[id]; }
 
     void setEventFlag(int16 label, NancyFlag flag = kTrue);
@@ -159,8 +166,6 @@ private:
 
     void readSceneSummary(Common::SeekableReadStream &stream);
 
-    bool changeGameState(bool keepGraphics = false);
-
     void clearSceneData();
 
 public:
@@ -222,8 +227,6 @@ protected:
         int16 primaryVideoResponsePicked = -1;
     };
 
-    Nancy::NancyEngine *_engine;
-
     // RenderObjects
     UI::FullScreenImage _frame;
     UI::Viewport _viewport;
@@ -250,8 +253,11 @@ protected:
 
     bool isComingFromMenu = true;
     bool hasLoadedFromSavefile = false;
+    bool _shouldClearTextbox = true;
 };
 
+#define NancySceneState Nancy::State::Scene::instance()
+
 } // End of namespace State
 } // End of namespace Nancy
 
diff --git a/engines/nancy/state/state.h b/engines/nancy/state/state.h
new file mode 100644
index 0000000000..054c8389c2
--- /dev/null
+++ b/engines/nancy/state/state.h
@@ -0,0 +1,46 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef NANCY_STATE_STATE_H
+#define NANCY_STATE_STATE_H
+
+#include "common/types.h"
+
+namespace Nancy {
+namespace State {
+
+class State {
+public:
+    State() {}
+    virtual ~State() =default;
+
+    virtual void process() =0;
+    virtual void onStateEnter() {}
+
+    // Returns whether the object destroyed itself after exit
+    virtual bool onStateExit() { return true; }
+};
+
+} // End of namespace State
+} // End of namespace Nancy
+
+#endif // NANCY_STATE_STATE_H
diff --git a/engines/nancy/ui/button.cpp b/engines/nancy/ui/button.cpp
index d43ca2dec5..5dfd34d066 100644
--- a/engines/nancy/ui/button.cpp
+++ b/engines/nancy/ui/button.cpp
@@ -36,7 +36,7 @@ namespace UI {
 
 void Button::handleInput(NancyInput &input) {
     if (_screenPosition.contains(input.mousePos)) {
-        _engine->cursorManager->setCursorType(CursorManager::kHotspotArrow);
+        NanEngine.cursorManager->setCursorType(CursorManager::kHotspotArrow);
 
         if (input.input & NancyInput::kLeftMouseButtonUp) {
             onClick();
@@ -45,12 +45,12 @@ void Button::handleInput(NancyInput &input) {
 }
 
 void MenuButton::init() {
-    Common::SeekableReadStream *bsum = _engine->getBootChunkStream("BSUM");
+    Common::SeekableReadStream *bsum = NanEngine.getBootChunkStream("BSUM");
 
     bsum->seek(0x184, SEEK_SET);
     Common::Rect src;
     readRect(*bsum, src);
-    _drawSurface.create(_engine->graphicsManager->object0, src);
+    _drawSurface.create(NanEngine.graphicsManager->object0, src);
     bsum->skip(16);
     readRect(*bsum, _screenPosition);
     setVisible(false);
@@ -59,18 +59,18 @@ void MenuButton::init() {
 }
 
 void MenuButton::onClick() {
-    _engine->scene->requestStateChange(NancyEngine::kMainMenu);
-    _engine->sound->playSound(0x18);
+    NancySceneState.requestStateChange(NancyEngine::kMainMenu);
+    NanEngine.sound->playSound(0x18);
     setVisible(true);
 }
 
 void HelpButton::init() {
-    Common::SeekableReadStream *bsum = _engine->getBootChunkStream("BSUM");
+    Common::SeekableReadStream *bsum = NanEngine.getBootChunkStream("BSUM");
 
     bsum->seek(0x194, SEEK_SET);
     Common::Rect src;
     readRect(*bsum, src);
-    _drawSurface.create(_engine->graphicsManager->object0, src);
+    _drawSurface.create(NanEngine.graphicsManager->object0, src);
     bsum->skip(16);
     readRect(*bsum, _screenPosition);
     setVisible(false);
@@ -79,8 +79,8 @@ void HelpButton::init() {
 }
 
 void HelpButton::onClick() {
-    _engine->scene->requestStateChange(NancyEngine::kHelp);
-    _engine->sound->playSound(0x18);
+    NancySceneState.requestStateChange(NancyEngine::kHelp);
+    NanEngine.sound->playSound(0x18);
     setVisible(true);
 }
 
diff --git a/engines/nancy/ui/fullscreenimage.cpp b/engines/nancy/ui/fullscreenimage.cpp
index 183d8fe0b7..9cd1d50681 100644
--- a/engines/nancy/ui/fullscreenimage.cpp
+++ b/engines/nancy/ui/fullscreenimage.cpp
@@ -31,7 +31,7 @@ namespace UI {
 
 void FullScreenImage::init(Common::String imageName) {
     Graphics::Surface surf;
-    _engine->_res->loadImage("ciftree", imageName, surf);
+    NanEngine.resource->loadImage("ciftree", imageName, surf);
 
     Common::Rect srcBounds = Common::Rect(0,0, surf.w, surf.h);
     _screenPosition = srcBounds;
diff --git a/engines/nancy/ui/fullscreenimage.h b/engines/nancy/ui/fullscreenimage.h
index 506087d5aa..1e3c44d0b1 100644
--- a/engines/nancy/ui/fullscreenimage.h
+++ b/engines/nancy/ui/fullscreenimage.h
@@ -30,7 +30,7 @@ namespace UI {
 
 class FullScreenImage : public RenderObject {
 public:
-    FullScreenImage(NancyEngine *engine) : RenderObject(engine) {}
+    FullScreenImage() : RenderObject() {}
     virtual ~FullScreenImage() =default;
 
     void init(Common::String imageName);
diff --git a/engines/nancy/ui/inventorybox.cpp b/engines/nancy/ui/inventorybox.cpp
index fecf30b1a5..ef62f08b53 100644
--- a/engines/nancy/ui/inventorybox.cpp
+++ b/engines/nancy/ui/inventorybox.cpp
@@ -36,7 +36,7 @@ namespace Nancy {
 namespace UI {
 
 void InventoryBox::init() {
-    Common::SeekableReadStream &stream = *_engine->getBootChunkStream("INV");
+    Common::SeekableReadStream &stream = *NanEngine.getBootChunkStream("INV");
     stream.seek(0, SEEK_SET);
 
     _order.clear();
@@ -72,7 +72,7 @@ void InventoryBox::init() {
         readRect(stream, _itemDescriptions[i].sourceRect);
     }
 
-    _engine->_res->loadImage("ciftree", inventoryBoxIconsImageName, _iconsSurface);
+    NanEngine.resource->loadImage("ciftree", inventoryBoxIconsImageName, _iconsSurface);
     
     uint numItems = 11; // TODO
     _fullInventorySurface.create(_screenPosition.width(), _screenPosition.height() * ((numItems / 4) + 1), GraphicsManager::pixelFormat);
@@ -115,17 +115,17 @@ void InventoryBox::handleInput(NancyInput &input) {
     
     for (uint i = 0; i < 4; ++i) {
         if (_itemHotspots[i].hotspot.contains(input.mousePos)) {
-            if (_engine->scene->getHeldItem() != -1) {
-                _engine->cursorManager->setCursorType(CursorManager::kHotspotArrow);
+            if (NancySceneState.getHeldItem() != -1) {
+                NanEngine.cursorManager->setCursorType(CursorManager::kHotspotArrow);
                 if (input.input & NancyInput::kLeftMouseButtonUp) {
-                    _engine->scene->addItemToInventory(_engine->scene->getHeldItem());
-                    _engine->sound->playSound(0x16);
+                    NancySceneState.addItemToInventory(NancySceneState.getHeldItem());
+                    NanEngine.sound->playSound(0x16);
                 }                
             } else if (_itemHotspots[i].itemID != -1) {
-                _engine->cursorManager->setCursorType(CursorManager::kHotspotArrow);
+                NanEngine.cursorManager->setCursorType(CursorManager::kHotspotArrow);
                 if (input.input & NancyInput::kLeftMouseButtonUp) {
-                    _engine->scene->removeItemFromInventory(_itemHotspots[i].itemID);
-                    _engine->sound->playSound(0x18);
+                    NancySceneState.removeItemFromInventory(_itemHotspots[i].itemID);
+                    NanEngine.sound->playSound(0x18);
                 }
             }
             break;
@@ -209,7 +209,7 @@ void InventoryBox::InventoryScrollbar::init() {
     Common::Rect &srcBounds = _parent->_sliderSource;
     Common::Point &topPosition = _parent->_sliderDefaultDest;
 
-    _drawSurface.create(_engine->graphicsManager->object0, srcBounds);
+    _drawSurface.create(NanEngine.graphicsManager->object0, srcBounds);
 
     _startPosition = topPosition;
     _startPosition.x -= srcBounds.width() / 2;
@@ -233,7 +233,7 @@ void InventoryBox::Shades::init() {
 }
 
 void InventoryBox::Shades::updateGraphics() {
-    Time time = _engine->getTotalPlayTime();
+    Time time = NanEngine.getTotalPlayTime();
     if (_areOpen) {
         if (_curFrame < 7 && time > _nextFrameTime) {
             setAnimationFrame(++_curFrame);
@@ -241,7 +241,7 @@ void InventoryBox::Shades::updateGraphics() {
 
             if (!_soundTriggered) {
                 _soundTriggered = true;
-                _engine->sound->playSound(0x12);
+                NanEngine.sound->playSound(0x12);
             }
         }
     } else {
@@ -251,7 +251,7 @@ void InventoryBox::Shades::updateGraphics() {
 
             if (!_soundTriggered) {
                 _soundTriggered = true;
-                _engine->sound->playSound(0x12);
+                NanEngine.sound->playSound(0x12);
             }
         }
     }
@@ -262,7 +262,7 @@ void InventoryBox::Shades::updateGraphics() {
 }
 
 void InventoryBox::Shades::setAnimationFrame(uint frame) {
-    Graphics::ManagedSurface &object0 = _engine->graphicsManager->object0;
+    Graphics::ManagedSurface &object0 = NanEngine.graphicsManager->object0;
     Common::Rect srcRect;
     Common::Point destPoint;
 
diff --git a/engines/nancy/ui/scrollbar.cpp b/engines/nancy/ui/scrollbar.cpp
index 8de7a7dfd8..b1149e5ab9 100644
--- a/engines/nancy/ui/scrollbar.cpp
+++ b/engines/nancy/ui/scrollbar.cpp
@@ -36,7 +36,7 @@ namespace UI {
 
 void Scrollbar::handleInput(NancyInput &input) {
     if (_screenPosition.contains(input.mousePos)) {
-        _engine->cursorManager->setCursorType(CursorManager::kHotspotArrow);
+        NanEngine.cursorManager->setCursorType(CursorManager::kHotspotArrow);
 
         if (input.input & NancyInput::kLeftMouseButtonDown && !_isClicked) {
             // Begin click and hold
diff --git a/engines/nancy/ui/textbox.cpp b/engines/nancy/ui/textbox.cpp
index e47315628e..0f741e1bd8 100644
--- a/engines/nancy/ui/textbox.cpp
+++ b/engines/nancy/ui/textbox.cpp
@@ -48,7 +48,7 @@ const char Textbox::tabToken[] = "<t>";
 const char Textbox::telephoneEndToken[] = "<e>";
 
 void Textbox::init() {    
-    Common::SeekableReadStream *chunk = _engine->getBootChunkStream("TBOX");
+    Common::SeekableReadStream *chunk = NanEngine.getBootChunkStream("TBOX");
     chunk->seek(0);
     readRect(*chunk, _scrollbarSourceBounds);
 
@@ -69,7 +69,7 @@ void Textbox::init() {
     chunk->seek(0x1FE, SEEK_SET);
     _fontID = chunk->readUint16LE();
 
-    chunk = _engine->getBootChunkStream("BSUM");
+    chunk = NanEngine.getBootChunkStream("BSUM");
     chunk->seek(0x164);
     readRect(*chunk, _screenPosition);
 
@@ -108,12 +108,12 @@ void Textbox::handleInput(NancyInput &input) {
         Common::Rect hotspot = _hotspots[i];
         hotspot.translate(0, -_drawSurface.getOffsetFromOwner().y);
         if (convertToScreen(hotspot).findIntersectingRect(_screenPosition).contains(input.mousePos)) {
-            _engine->cursorManager->setCursorType(CursorManager::kHotspotArrow);
+            NanEngine.cursorManager->setCursorType(CursorManager::kHotspotArrow);
 
             if (input.input & NancyInput::kLeftMouseButtonUp) {
                 input.input &= ~NancyInput::kLeftMouseButtonUp;
-                _engine->scene->clearLogicConditions();
-                _engine->scene->setLogicCondition(i);
+                NancySceneState.clearLogicConditions();
+                NancySceneState.setLogicCondition(i);
             }
             
             break;
@@ -126,7 +126,7 @@ void Textbox::drawTextbox() {
 
     _numLines = 0;
 
-    Font *font = _engine->graphicsManager->getFont(_fontID);
+    Font *font = NanEngine.graphicsManager->getFont(_fontID);
 
     uint maxWidth = _fullSurface.w - _borderWidth * 2;
     uint lineDist = _lineHeight + _lineHeight / 4;
@@ -289,7 +289,7 @@ void Textbox::TextboxScrollbar::init() {
     Common::Rect &srcBounds = _parent->_scrollbarSourceBounds;
     Common::Point &topPosition = _parent->_scrollbarDefaultDest;
 
-    _drawSurface.create(_engine->graphicsManager->object0, srcBounds);
+    _drawSurface.create(NanEngine.graphicsManager->object0, srcBounds);
 
     _startPosition = topPosition;
     _startPosition.x -= srcBounds.width() / 2;
diff --git a/engines/nancy/ui/viewport.cpp b/engines/nancy/ui/viewport.cpp
index 4be6d18cec..d030df439f 100644
--- a/engines/nancy/ui/viewport.cpp
+++ b/engines/nancy/ui/viewport.cpp
@@ -34,7 +34,7 @@ namespace UI {
 
 // does NOT put the object in a valid state until loadVideo is called
 void Viewport::init() {
-    Common::SeekableReadStream *viewChunk = _engine->getBootChunkStream("VIEW");
+    Common::SeekableReadStream *viewChunk = NanEngine.getBootChunkStream("VIEW");
     viewChunk->seek(0);
 
     Common::Rect dest;
@@ -57,11 +57,11 @@ void Viewport::init() {
 }
 
 void Viewport::handleInput(NancyInput &input) {
-    Time playTime = _engine->getTotalPlayTime();
+    Time playTime = NanEngine.getTotalPlayTime();
     byte direction = 0;
 
     if (_screenPosition.contains(input.mousePos)) {
-        _engine->cursorManager->setCursorType(CursorManager::kNormal);
+        NanEngine.cursorManager->setCursorType(CursorManager::kNormal);
     }
 
     // Do not handle hotspots marked as incative and ignore diagonals if intersecting hotspots are not active
@@ -118,7 +118,7 @@ void Viewport::handleInput(NancyInput &input) {
     }
 
     if (direction) {
-        _engine->cursorManager->setCursorType(CursorManager::kMove);
+        NanEngine.cursorManager->setCursorType(CursorManager::kMove);
 
         if (input.input & NancyInput::kRightMouseButton) {
             direction |= kMoveFast;
@@ -151,8 +151,8 @@ void Viewport::handleInput(NancyInput &input) {
 
     // Perform the movement
     if (direction) {
-        const Nancy::State::Scene::SceneSummary &summary = _engine->scene->getSceneSummary();
-        Time movementDelta = _engine->scene->getMovementTimeDelta(direction & kMoveFast);
+        const Nancy::State::Scene::SceneSummary &summary = NancySceneState.getSceneSummary();
+        Time movementDelta = NancySceneState.getMovementTimeDelta(direction & kMoveFast);
 
         if (playTime > _nextMovementTime) {
             if (direction & kLeft) {
diff --git a/engines/nancy/ui/viewport.h b/engines/nancy/ui/viewport.h
index 8fdbccd637..ee3e1cd33a 100644
--- a/engines/nancy/ui/viewport.h
+++ b/engines/nancy/ui/viewport.h
@@ -40,8 +40,8 @@ namespace UI {
 
 class Viewport : public Nancy::RenderObject {
 public:
-    Viewport(NancyEngine *engine) :
-        RenderObject(engine),
+    Viewport() :
+        RenderObject(),
         _movementLastFrame(0),
         _edgesMask(0) {}
     virtual ~Viewport() { _decoder.close(); _fullFrame.free(); }


Commit: 19135b354c8c6c8b7cfdf4cea9306ab60b65c61c
    https://github.com/scummvm/scummvm/commit/19135b354c8c6c8b7cfdf4cea9306ab60b65c61c
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add The Vampire Diaries detection entry

Added a detection entry and game type for The Vampire Diaries.

Changed paths:
    engines/nancy/detection.cpp
    engines/nancy/detection.h


diff --git a/engines/nancy/detection.cpp b/engines/nancy/detection.cpp
index 964e5fe41a..05036f5d04 100644
--- a/engines/nancy/detection.cpp
+++ b/engines/nancy/detection.cpp
@@ -39,6 +39,7 @@ const char *const directoryGlobs[] = {
 
 static const PlainGameDescriptor nancyGames[] = {
 	// Games
+	{"vampirediaries", "The Vampire Diaries"},
 	{"nancy1", "Nancy Drew: Secrets Can Kill"},
 	{"nancy2", "Nancy Drew: Stay Tuned for Danger"},
 	{"nancy3", "Nancy Drew: Message in a Haunted Mansion"},
@@ -49,7 +50,21 @@ static const PlainGameDescriptor nancyGames[] = {
 };
 
 static const Nancy::NancyGameDescription gameDescriptions[] = {
-
+	
+	{ // MD5 by fracturehill
+		{
+			"vampirediaries", 0,
+			{
+				{"vampire.exe", 0, "c6207f4bb7418b8a067ad75ed9f57bdf", 114688},
+				AD_LISTEND
+			},
+			Common::EN_ANY,
+			Common::kPlatformWindows,
+			ADGF_NO_FLAGS,
+			GUIO0()
+		},
+		Nancy::GameType::kGameTypeVampire
+	},
 	{ // MD5 by waltervn
 		{
 			"nancy1", 0,
diff --git a/engines/nancy/detection.h b/engines/nancy/detection.h
index 1d98c8ccba..e2d67f1e67 100644
--- a/engines/nancy/detection.h
+++ b/engines/nancy/detection.h
@@ -29,6 +29,7 @@ namespace Nancy {
 
 enum GameType {
 	kGameTypeNone = 0,
+	kGameTypeVampire,
 	kGameTypeNancy1,
 	kGameTypeNancy2,
 	kGameTypeNancy3


Commit: bde7db15aea6863a5ca0c4be9e4f8a38511a2ead
    https://github.com/scummvm/scummvm/commit/bde7db15aea6863a5ca0c4be9e4f8a38511a2ead
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Use serializer when reading boot summary

Changed the readBootSummary function so it uses a serializer with the game type enum as its version. With this change, we don't need separate subclasses just to read every different BSUM format, so all of the NancyEngine child classes have been removed.

Changed paths:
    engines/nancy/detection.h
    engines/nancy/graphics.cpp
    engines/nancy/nancy.cpp
    engines/nancy/nancy.h
    engines/nancy/state/logo.cpp
    engines/nancy/state/scene.cpp


diff --git a/engines/nancy/detection.h b/engines/nancy/detection.h
index e2d67f1e67..9509590337 100644
--- a/engines/nancy/detection.h
+++ b/engines/nancy/detection.h
@@ -29,10 +29,10 @@ namespace Nancy {
 
 enum GameType {
 	kGameTypeNone = 0,
-	kGameTypeVampire,
-	kGameTypeNancy1,
-	kGameTypeNancy2,
-	kGameTypeNancy3
+	kGameTypeVampire = 1,
+	kGameTypeNancy1 = 2,
+	kGameTypeNancy2 = 3,
+	kGameTypeNancy3 = 4
 };
 
 struct NancyGameDescription {
diff --git a/engines/nancy/graphics.cpp b/engines/nancy/graphics.cpp
index 5a641b0ab9..99630cfcc6 100644
--- a/engines/nancy/graphics.cpp
+++ b/engines/nancy/graphics.cpp
@@ -39,8 +39,11 @@ void GraphicsManager::init() {
     _screen.create(640, 480, pixelFormat);
     _screen.setTransparentColor(transColor); 
 
+    Common::SeekableReadStream *ob = NanEngine.getBootChunkStream("OB0");
+    ob->seek(0);
+
     Graphics::Surface surf;
-    NanEngine.resource->loadImage("ciftree", "OBJECT0", surf);
+    NanEngine.resource->loadImage(ob->readString(), surf);
     object0.create(surf.w, surf.h, surf.format);
     object0.blitFrom(surf, Common::Point(0, 0));
     surf.free();
diff --git a/engines/nancy/nancy.cpp b/engines/nancy/nancy.cpp
index 12e3b5e4d9..b4cf51d5b6 100644
--- a/engines/nancy/nancy.cpp
+++ b/engines/nancy/nancy.cpp
@@ -345,31 +345,12 @@ Common::String NancyEngine::readFilename(Common::ReadStream *stream) const {
 	return Common::String(buf);
 }
 
-void NancyEngine::readImageList(const IFF &boot, const Common::String &prefix, ImageList &list) {
-	Common::SeekableReadStream *bsum = getBootChunkStream("BSUM");
-	byte count = bsum->readByte();
-	debugC(1, kDebugEngine, "Found %i %s images", count, prefix.c_str());
-
-	for (int i = 0; i < count; ++i) {
-		Common::String chunkName = Common::String::format("%s%d", prefix.c_str(), i);
-		Common::SeekableReadStream *chunkStream = boot.getChunkStream(chunkName);
-
-		if (!chunkStream)
-			error("Failed to read BOOT %s", chunkName.c_str());
-
-		Image image;
-		image.name = readFilename(chunkStream);
-		chunkStream->skip(1);
-		image.width = chunkStream->readUint16LE();
-		image.height = chunkStream->readUint16LE();
-
-		if (chunkStream->err())
-			error("Error reading %s%d", prefix.c_str(), i);
-
-		debugC(1, kDebugEngine, "Adding %s (%dx%d)", image.name.c_str(), image.width, image.height);
-		list.push_back(image);
-
-		delete chunkStream;
+void NancyEngine::readChunkList(const IFF &boot, Common::Serializer &ser, const Common::String &prefix) {
+	byte numChunks;
+	ser.syncAsByte(numChunks);
+	for (byte i = 0; i < numChunks; ++ i) {
+		Common::String name = Common::String::format("%s%d", prefix.c_str(), i);
+		addBootChunk(name, boot.getChunkStream(name));
 	}
 }
 
@@ -456,70 +437,59 @@ private:
 	virtual void readBootSummary(const IFF &boot);
 };
 
-void NancyEngine_v0::readBootSummary(const IFF &boot) {
+void NancyEngine::readBootSummary(const IFF &boot) {
 	Common::SeekableReadStream *bsum = getBootChunkStream("BSUM");
-	bsum->seek(0xa3);
-	firstSceneID = bsum->readUint16LE();
-	bsum->skip(4);
-	startTimeHours = bsum->readUint16LE(); // this is a whole Time struct but we just take the hours for now
-	bsum->seek(0x151);
-	readImageList(boot, "FR", _frames);
-	readImageList(boot, "LG", _logos);
-	readImageList(boot, "OB", _objects);
-	bsum->seek(0x1D1);
-	_fontSize = bsum->readSint32LE() * 1346;
-	bsum->seek(0x1ED);
-	NancySceneState.playerTimeMinuteLength = bsum->readSint16LE();
-	bsum->seek(0x1F1);
-    overrideMovementTimeDeltas = bsum->readByte();
+	bsum->seek(0);
 
-    if (overrideMovementTimeDeltas) {
-        slowMovementTimeDelta = bsum->readUint16LE();
-        fastMovementTimeDelta = bsum->readUint16LE();
-    }
-}
+	// Use a serializer to handle several games' BSUMs in the same function
+	Common::Serializer ser(bsum, nullptr);
+	ser.setVersion(_gameDescription->gameType);
 
-class NancyEngine_v1 : public NancyEngine_v0 {
-public:
-	NancyEngine_v1(OSystem *syst, const NancyGameDescription *gd) : NancyEngine_v0(syst, gd) { }
+	ser.skip(0xA3, kGameTypeVampire, kGameTypeNancy2);
+	ser.syncAsUint16LE(firstSceneID);
+	ser.skip(4, kGameTypeVampire, kGameTypeNancy2);
+	ser.syncAsUint16LE(startTimeHours);
+	
+	ser.skip(0x80, kGameTypeVampire, kGameTypeVampire);
+	ser.skip(0xA6, kGameTypeNancy1, kGameTypeNancy1);
+	ser.skip(0xA0, kGameTypeNancy2, kGameTypeNancy2);
 
-private:
-	virtual void readBootSummary(const IFF &boot);
-};
+	// nancy3 has not been looked into, skip straight to images
+	ser.skip(0xA7, kGameTypeNancy3, kGameTypeNancy3);
 
-void NancyEngine_v1::readBootSummary(const IFF &boot) {
-	Common::SeekableReadStream *bsum = getBootChunkStream("BSUM");
-	bsum->seek(0xa3);
-	firstSceneID = bsum->readUint16LE();
-	bsum->seek(0x14b);
-	readImageList(boot, "FR", _frames);
-	readImageList(boot, "LG", _logos);
-}
+	bsum->seek(0x151);
+	readChunkList(boot, ser, "FR");
+	readChunkList(boot, ser, "LG");
 
-class NancyEngine_v2 : public NancyEngine_v1 {
-public:
-	NancyEngine_v2(OSystem *syst, const NancyGameDescription *gd) : NancyEngine_v1(syst, gd) { }
+	if (ser.getVersion() < kGameTypeNancy3) {
+		readChunkList(boot, ser, "OB");
+	}
 
-private:
-	virtual uint getFilenameLen() const { return 32; }
-	virtual void readBootSummary(const IFF &boot);
-};
+	ser.skip(0x99, kGameTypeNancy1, kGameTypeNancy1);
+	int16 time = 0;
+	ser.syncAsSint16LE(time, kGameTypeNancy1, kGameTypeNancy1);
+	NancySceneState.playerTimeMinuteLength = time;
+	ser.skip(2, kGameTypeNancy1, kGameTypeNancy1);
+    ser.syncAsByte(overrideMovementTimeDeltas, kGameTypeNancy1, kGameTypeNancy1);
 
-void NancyEngine_v2::readBootSummary(const IFF &boot) {
-	Common::SeekableReadStream *bsum = getBootChunkStream("BSUM");
-	bsum->seek(0xa7);
-	readImageList(boot, "FR", _frames);
-	readImageList(boot, "LG", _logos);
+    if (overrideMovementTimeDeltas) {
+		ser.syncAsSint16LE(time, kGameTypeNancy1, kGameTypeNancy1);
+		slowMovementTimeDelta = time;
+		ser.syncAsSint16LE(time, kGameTypeNancy1, kGameTypeNancy1);
+		fastMovementTimeDelta = time;
+    }
 }
 
 NancyEngine *NancyEngine::create(GameType type, OSystem *syst, const NancyGameDescription *gd) {
 	switch (type) {
+	case kGameTypeVampire:
+		return new NancyEngine(syst, gd);
 	case kGameTypeNancy1:
-		return new NancyEngine_v0(syst, gd);
+		return new NancyEngine(syst, gd);
 	case kGameTypeNancy2:
-		return new NancyEngine_v1(syst, gd);
+		return new NancyEngine(syst, gd);
 	case kGameTypeNancy3:
-		return new NancyEngine_v2(syst, gd);
+		return new NancyEngine(syst, gd);
 	default:
 		error("Unknown GameType");
 	}
diff --git a/engines/nancy/nancy.h b/engines/nancy/nancy.h
index d89f76b343..3ccf7723ed 100644
--- a/engines/nancy/nancy.h
+++ b/engines/nancy/nancy.h
@@ -185,21 +185,14 @@ protected:
 		State::State *previousState = nullptr;
 	};
 
-	typedef Common::Array<Image> ImageList;
-
-	ImageList _logos;
-	ImageList _frames;
-	ImageList _objects;
-	int32 _fontSize;
-
 	bool _cheatTypeIsEventFlag;
 
 	void preloadCals(const IFF &boot);
-	void readImageList(const IFF &boot, const Common::String &prefix, ImageList &list);
+	void readChunkList(const IFF &boot, Common::Serializer &ser, const Common::String &prefix);
 	Common::String readFilename(Common::ReadStream *stream) const;
 
-	virtual uint getFilenameLen() const = 0;
-	virtual void readBootSummary(const IFF &boot) = 0;
+	virtual uint getFilenameLen() const { return 9; };
+	virtual void readBootSummary(const IFF &boot);
 
 private:
 	GameFlow _gameFlow;
diff --git a/engines/nancy/state/logo.cpp b/engines/nancy/state/logo.cpp
index c9ef18fe9e..a35e7ef427 100644
--- a/engines/nancy/state/logo.cpp
+++ b/engines/nancy/state/logo.cpp
@@ -63,8 +63,11 @@ void Logo::process() {
 void Logo::init() {
 	_surf = new Graphics::Surface;
 
-	if (!NanEngine.resource->loadImage("ciftree", NanEngine._logos[0].name, *_surf))
-		error("Failed to load %s", NanEngine._logos[0].name.c_str());
+	Common::SeekableReadStream *lg = NanEngine.getBootChunkStream("LG0");
+	lg->seek(0);
+
+	if (!NanEngine.resource->loadImage(lg->readString(), *_surf))
+		error("Failed to load logo image");
 
 	_state = kStartSound;
 }
diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index e3198f1e2e..b0c2eddd2b 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -305,7 +305,10 @@ void Scene::initStatic() {
     _mapAccessSceneIDs.push_back(0x4E2);
     _mapAccessSceneIDs.push_back(0x682);
 
-    _frame.init("FRAME"); // TODO should be extracted from BSUM
+    Common::SeekableReadStream *fr = NanEngine.getBootChunkStream("FR0");
+    fr->seek(0);
+
+    _frame.init(fr->readString());
     _viewport.init();
     _textbox.init();
     _inventoryBox.init();


Commit: b3222b743916cd3fe5d1e566200ba56daa236379
    https://github.com/scummvm/scummvm/commit/b3222b743916cd3fe5d1e566200ba56daa236379
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Resource manager improvements

The resource manager now keeps track of its cif trees and calls to loadImage() and loadData() don't need to specify a cif tree. In order to support The Vampire Diaries, IFFs can now be loaded from their own dedicated .iff file, and the engine doesn't require a cif tree to be loaded on boot.

Changed paths:
    engines/nancy/action/leverpuzzle.cpp
    engines/nancy/action/orderingpuzzle.cpp
    engines/nancy/action/recordtypes.cpp
    engines/nancy/action/rotatinglockpuzzle.cpp
    engines/nancy/action/sliderpuzzle.cpp
    engines/nancy/action/staticbitmapanim.cpp
    engines/nancy/action/telephone.cpp
    engines/nancy/console.cpp
    engines/nancy/console.h
    engines/nancy/cursor.cpp
    engines/nancy/font.cpp
    engines/nancy/iff.cpp
    engines/nancy/nancy.cpp
    engines/nancy/resource.cpp
    engines/nancy/resource.h
    engines/nancy/state/credits.cpp
    engines/nancy/ui/fullscreenimage.cpp
    engines/nancy/ui/inventorybox.cpp


diff --git a/engines/nancy/action/leverpuzzle.cpp b/engines/nancy/action/leverpuzzle.cpp
index e2ac0197b4..3f549f9a04 100644
--- a/engines/nancy/action/leverpuzzle.cpp
+++ b/engines/nancy/action/leverpuzzle.cpp
@@ -37,7 +37,7 @@ void LeverPuzzle::init() {
     _drawSurface.clear(GraphicsManager::transColor);
 
     Graphics::Surface surf;
-    NanEngine.resource->loadImage("ciftree", imageName, surf);
+    NanEngine.resource->loadImage(imageName, surf);
     image.create(surf.w, surf.h, surf.format);
     image.blitFrom(surf);
     surf.free();
diff --git a/engines/nancy/action/orderingpuzzle.cpp b/engines/nancy/action/orderingpuzzle.cpp
index e669066a01..bca2da5b67 100644
--- a/engines/nancy/action/orderingpuzzle.cpp
+++ b/engines/nancy/action/orderingpuzzle.cpp
@@ -44,7 +44,7 @@ void OrderingPuzzle::init() {
     clearAllElements();
 
     Graphics::Surface surf;
-    NanEngine.resource->loadImage("ciftree", imageName, surf);
+    NanEngine.resource->loadImage(imageName, surf);
     image.create(surf.w, surf.h, surf.format);
     image.blitFrom(surf);
     surf.free();
diff --git a/engines/nancy/action/recordtypes.cpp b/engines/nancy/action/recordtypes.cpp
index 9719d49df6..e8a76fffe7 100644
--- a/engines/nancy/action/recordtypes.cpp
+++ b/engines/nancy/action/recordtypes.cpp
@@ -379,7 +379,7 @@ void DifficultyLevel::execute() {
 
 void ShowInventoryItem::init() {
     Graphics::Surface srcSurf;
-    NanEngine.resource->loadImage("ciftree", imageName, srcSurf);
+    NanEngine.resource->loadImage(imageName, srcSurf);
     _fullSurface.create(srcSurf.w, srcSurf.h, srcSurf.format);
     _fullSurface.blitFrom(srcSurf);
     srcSurf.free();
diff --git a/engines/nancy/action/rotatinglockpuzzle.cpp b/engines/nancy/action/rotatinglockpuzzle.cpp
index 3c87b19f3f..8b80842616 100644
--- a/engines/nancy/action/rotatinglockpuzzle.cpp
+++ b/engines/nancy/action/rotatinglockpuzzle.cpp
@@ -39,7 +39,7 @@ void RotatingLockPuzzle::init() {
     _drawSurface.clear(GraphicsManager::transColor);
 
     Graphics::Surface surf;
-    NanEngine.resource->loadImage("ciftree", imageName, surf);
+    NanEngine.resource->loadImage(imageName, surf);
     image.create(surf.w, surf.h, surf.format);
     image.blitFrom(surf);
     surf.free();
diff --git a/engines/nancy/action/sliderpuzzle.cpp b/engines/nancy/action/sliderpuzzle.cpp
index 4184501aee..1c940eb598 100644
--- a/engines/nancy/action/sliderpuzzle.cpp
+++ b/engines/nancy/action/sliderpuzzle.cpp
@@ -42,7 +42,7 @@ void SliderPuzzle::init() {
     _drawSurface.clear(GraphicsManager::transColor);
 
     Graphics::Surface surf;
-    NanEngine.resource->loadImage("ciftree", imageName, surf);
+    NanEngine.resource->loadImage(imageName, surf);
     image.create(surf.w, surf.h, surf.format);
     image.blitFrom(surf);
     surf.free();
diff --git a/engines/nancy/action/staticbitmapanim.cpp b/engines/nancy/action/staticbitmapanim.cpp
index 9d2434ffae..7e76da4091 100644
--- a/engines/nancy/action/staticbitmapanim.cpp
+++ b/engines/nancy/action/staticbitmapanim.cpp
@@ -36,7 +36,7 @@ namespace Action {
 
 void PlayStaticBitmapAnimation::init() {
     Graphics::Surface surf;
-    NanEngine.resource->loadImage("ciftree", imageName, surf);
+    NanEngine.resource->loadImage(imageName, surf);
 
     _fullSurface.create(surf.w, surf.h, surf.format);
     _fullSurface.blitFrom(surf);
diff --git a/engines/nancy/action/telephone.cpp b/engines/nancy/action/telephone.cpp
index 367589ff1b..61abd428e8 100644
--- a/engines/nancy/action/telephone.cpp
+++ b/engines/nancy/action/telephone.cpp
@@ -41,7 +41,7 @@ void Telephone::init() {
     _drawSurface.clear(GraphicsManager::transColor);
 
     Graphics::Surface surf;
-    NanEngine.resource->loadImage("ciftree", imageName, surf);
+    NanEngine.resource->loadImage(imageName, surf);
     image.create(surf.w, surf.h, surf.format);
     image.blitFrom(surf);
     surf.free();
diff --git a/engines/nancy/console.cpp b/engines/nancy/console.cpp
index 426008885e..43b96e1b47 100644
--- a/engines/nancy/console.cpp
+++ b/engines/nancy/console.cpp
@@ -96,7 +96,7 @@ void NancyConsole::postEnter() {
 
 	if (!_imageFile.empty()) {
 		Graphics::Surface surf;
-		if (NanEngine.resource->loadImage(_imageCifTree, _imageFile, surf)) {
+		if (NanEngine.resource->loadImage(_imageFile, surf)) {
 			g_system->fillScreen(0);
 			g_system->copyRectToScreen(surf.getPixels(), surf.pitch, 0, 0, surf.w > 640 ? 640 : surf.w, surf.h > 480 ? 480 : surf.h);
 			g_system->updateScreen();
@@ -122,7 +122,6 @@ void NancyConsole::postEnter() {
 		}
 		
 		_imageFile.clear();
-		_imageCifTree.clear();
 	}
 
 	// After calling the console, action end events get sent to it and the input manager
@@ -256,14 +255,13 @@ bool NancyConsole::Cmd_chunkList(int argc, const char **argv) {
 }
 
 bool NancyConsole::Cmd_showImage(int argc, const char **argv) {
-	if (argc < 2 || argc > 3) {
+	if (argc != 2) {
 		debugPrintf("Draws an image on the screen\n");
 		debugPrintf("Usage: %s name [cal]\n", argv[0]);
 		return true;
 	}
 
 	_imageFile = argv[1];
-	_imageCifTree = argc == 2 ? "ciftree" : argv[2];
 	return cmdExit(0, 0);
 }
 
diff --git a/engines/nancy/console.h b/engines/nancy/console.h
index ede680884e..38a35fadb4 100644
--- a/engines/nancy/console.h
+++ b/engines/nancy/console.h
@@ -54,7 +54,6 @@ private:
 
 	Common::String _videoFile;
 	Common::String _imageFile;
-	Common::String _imageCifTree;
 };
 
 } // End of namespace Nancy
diff --git a/engines/nancy/cursor.cpp b/engines/nancy/cursor.cpp
index 55508115a2..9b77df0a5b 100644
--- a/engines/nancy/cursor.cpp
+++ b/engines/nancy/cursor.cpp
@@ -52,7 +52,7 @@ void CursorManager::init() {
     _primaryVideoInitialPos.y = chunk->readUint16LE();
 
     Graphics::Surface surf;
-    NanEngine.resource->loadImage("ciftree", inventoryCursorsImageName, surf);
+    NanEngine.resource->loadImage(inventoryCursorsImageName, surf);
     _invCursorsSurface.create(surf.w, surf.h, surf.format);
     _invCursorsSurface.blitFrom(surf);
 
diff --git a/engines/nancy/font.cpp b/engines/nancy/font.cpp
index ab2c68e2dd..dc3c39ea99 100644
--- a/engines/nancy/font.cpp
+++ b/engines/nancy/font.cpp
@@ -38,7 +38,7 @@ void Font::read(Common::SeekableReadStream &stream) {
     stream.read(name, 10);
     Common::String imageName = name;
 
-    NanEngine.resource->loadImage("ciftree", name, _image);
+    NanEngine.resource->loadImage(name, _image);
 
     char desc[0x20];
     stream.read(desc, 0x20);
diff --git a/engines/nancy/iff.cpp b/engines/nancy/iff.cpp
index 60540bfdc7..df29201d67 100644
--- a/engines/nancy/iff.cpp
+++ b/engines/nancy/iff.cpp
@@ -69,10 +69,11 @@ bool IFF::callback(Common::IFFChunk &c) {
 bool IFF::load() {
 	byte *data;
 	uint size;
-	data = NanEngine.resource->loadData("ciftree", _name, size);
+	data = NanEngine.resource->loadData(_name, size);
 
-	if (!data)
+	if (!data) {
 		return false;
+	}
 
 	// Scan the file for DATA chunks, completely ignoring IFF structure
 	// Presumably the string "DATA" is not allowed inside of chunks...
@@ -80,16 +81,17 @@ bool IFF::load() {
 
 	while (offset < size - 3) {
 		uint32 id = READ_BE_UINT32(data + offset);
-		if (id == ID_DATA) {
+		if (id == ID_DATA || id == ID_FORM) {
 			// Replace 'DATA' with standard 'FORM' for the parser
 			WRITE_BE_UINT32(data + offset, ID_FORM);
 			Common::MemoryReadStream stream(data + offset, size - offset);
 			Common::IFFParser iff(&stream);
-			Common::Functor1Mem< Common::IFFChunk &, bool, IFF > c(this, &IFF::callback);
+			Common::Functor1Mem<Common::IFFChunk &, bool, IFF> c(this, &IFF::callback);
 			iff.parse(c);
 			offset += 16; // Original engine skips 16, while 12 seems more logical
-		} else
+		} else {
 			++offset;
+		}
 	}
 
 	delete[] data;
diff --git a/engines/nancy/nancy.cpp b/engines/nancy/nancy.cpp
index b4cf51d5b6..1f3f38f75b 100644
--- a/engines/nancy/nancy.cpp
+++ b/engines/nancy/nancy.cpp
@@ -120,14 +120,16 @@ Common::Error NancyEngine::run() {
 	SearchMan.addSubDirectoryMatching(gameDataDir, "cdsound");
 	SearchMan.addSubDirectoryMatching(gameDataDir, "hdvideo");
 	SearchMan.addSubDirectoryMatching(gameDataDir, "cdvideo");
+	SearchMan.addSubDirectoryMatching(gameDataDir, "iff");
 	
 	Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember("data1.cab");
-		if (!stream)
-			error("Failed to open data1.cab");
-
+	if (stream) {
 		Common::Archive *cab = Common::makeInstallShieldArchive(stream);
-	if (cab)
-		SearchMan.add("data1.hdr", cab);
+		
+		if (cab) {
+			SearchMan.add("data1.hdr", cab);
+		}
+	}
 	
 	resource = new ResourceManager();
 	resource->initialize();
@@ -176,8 +178,7 @@ void NancyEngine::bootGameEngine() {
 		"MAP", "CD", "TBOX", "CURS", "VIEW", "MSND",
 		"BUOK", "BUDE", "BULS", "GLOB", "SLID",
 		"SET", "CURT", "CANT", "TH1", "TH2",
-		"QUOT", "TMOD"
-		};
+		"QUOT", "TMOD" };
 
 	for (auto const &n : names) {
 		addBootChunk(n, boot->getChunkStream(n));
@@ -185,10 +186,6 @@ void NancyEngine::bootGameEngine() {
 
 	// The FR, LG and OB chunks get added here	
 
-	Common::SeekableReadStream *font = getBootChunkStream("FONT");
-	if (_fontSize != font->size()) {
-		error("Mismatch NumFonts and FONT memory... %i, %i", _fontSize, font->size());
-	}
 	
 	// TODO reset some vars
 	// TODO reset some more vars
@@ -457,7 +454,6 @@ void NancyEngine::readBootSummary(const IFF &boot) {
 	// nancy3 has not been looked into, skip straight to images
 	ser.skip(0xA7, kGameTypeNancy3, kGameTypeNancy3);
 
-	bsum->seek(0x151);
 	readChunkList(boot, ser, "FR");
 	readChunkList(boot, ser, "LG");
 
diff --git a/engines/nancy/resource.cpp b/engines/nancy/resource.cpp
index bf81fa3aea..4d84e4149a 100644
--- a/engines/nancy/resource.cpp
+++ b/engines/nancy/resource.cpp
@@ -301,6 +301,40 @@ byte *CifTree::getCifData(const Common::String &name, ResourceManager::CifInfo &
 	return buf;
 }
 
+byte *ResourceManager::getCifData(const Common::String &treeName, const Common::String &name, CifInfo &info, uint *size) {
+	const CifFile *cifFile = CifFile::load(name);
+	byte *buf;
+
+	if (cifFile) {
+		buf = cifFile->getCifData(info, size);
+		delete cifFile;
+	} else {
+		const CifTree *cifTree = findCifTree(treeName);
+		if (!cifTree)
+			return 0;
+
+		buf = cifTree->getCifData(name, info, size);
+	}
+
+	if (buf && info.comp == kResCompression) {
+		Common::MemoryReadStream input(buf, info.compressedSize);
+		byte *raw = new byte[info.size];
+		Common::MemoryWriteStream output(raw, info.size);
+		if (!_dec->decompress(input, output)) {
+			warning("Failed to decompress '%s'", name.c_str());
+			delete[] buf;
+			delete[] raw;
+			return 0;
+		}
+		delete[] buf;
+		if (size)
+			*size = output.size();
+		return raw;
+	}
+
+	return buf;
+}
+
 class CifTree20 : public CifTree {
 public:
 	CifTree20(const Common::String &name, const Common::String &ext) : CifTree(name, ext) { }
@@ -575,9 +609,6 @@ ResourceManager::~ResourceManager() {
 }
 
 bool ResourceManager::loadCifTree(const Common::String &name, const Common::String &ext) {
-	// NOTE: It seems likely that multiple CifTrees can be open at the same time
-	// For now, we just replace the current CifTree with the new one
-
 	const CifTree *cifTree = CifTree::load(name, ext);
 
 	if (!cifTree)
@@ -597,8 +628,17 @@ const CifTree *ResourceManager::findCifTree(const Common::String &name) const {
 }
 
 void ResourceManager::initialize() {
-	if (!loadCifTree("ciftree", "dat"))
-		error("Failed to read 'ciftree.dat'");
+	loadCifTree("ciftree", "dat");
+}
+
+bool ResourceManager::getCifInfo(const Common::String &name, CifInfo &info) {
+	for (auto &tree : _cifTrees) {
+		if (getCifInfo(tree->getName(), name, info)) {
+			return true;
+		}
+	}
+
+	return false;
 }
 
 bool ResourceManager::getCifInfo(const Common::String &treeName, const Common::String &name, CifInfo &info) {
@@ -618,19 +658,22 @@ bool ResourceManager::getCifInfo(const Common::String &treeName, const Common::S
 	return cifTree->getCifInfo(name, info);
 }
 
-byte *ResourceManager::getCifData(const Common::String &treeName, const Common::String &name, CifInfo &info, uint *size) {
+byte *ResourceManager::getCifData(const Common::String &name, CifInfo &info, uint *size) {
+	// Try to open name.cif
 	const CifFile *cifFile = CifFile::load(name);
-	byte *buf;
+	byte *buf = nullptr;
 
+	// Look for cif inside cif tree
 	if (cifFile) {
 		buf = cifFile->getCifData(info, size);
 		delete cifFile;
 	} else {
-		const CifTree *cifTree = findCifTree(treeName);
-		if (!cifTree)
-			return 0;
-
-		buf = cifTree->getCifData(name, info, size);
+		for (auto &tree : _cifTrees) {
+			buf = tree->getCifData(name, info, size);
+			if (buf) {
+				break;
+			}
+		}
 	}
 
 	if (buf && info.comp == kResCompression) {
@@ -660,7 +703,7 @@ byte *ResourceManager::loadCif(const Common::String &treeName, const Common::Str
 bool ResourceManager::exportCif(const Common::String &treeName, const Common::String &name) {
 	CifInfo info;
 	uint size;
-	byte *buf = getCifData(treeName, name, info, &size);
+	byte *buf = getCifData(name, info, &size);
 
 	if (!buf)
 		return false;
@@ -679,27 +722,35 @@ bool ResourceManager::exportCif(const Common::String &treeName, const Common::St
 	return retval;
 }
 
-byte *ResourceManager::loadData(const Common::String &treeName, const Common::String &name, uint &size) {
+byte *ResourceManager::loadData(const Common::String &name, uint &size) {
 	CifInfo info;
-
-	byte *buf = getCifData(treeName, name, info, &size);
-
-	if (!buf)
-		return 0;
-
-	if (info.type != kResTypeScript) {
+	byte *buf = getCifData(name, info, &size);
+	
+	if (!buf) {
+		// Data was not found inside a cif tree or a cif file, try to open an .iff file
+		// This is used by The Vampire Diaries
+		Common::File *f = new Common::File;
+		if (f->open(name + ".iff")) {
+			size = f->size();
+			buf = new byte[size];
+			f->read(buf, size);
+		} else {
+			delete f;
+			return nullptr;
+		}
+	} else if (info.type != kResTypeScript) {
 		warning("Resource '%s' is not a script", name.c_str());
 		delete[] buf;
-		return 0;
+		return nullptr;
 	}
 
 	return buf;
 }
 
-bool ResourceManager::loadImage(const Common::String &treeName, const Common::String &name, Graphics::Surface &surf) {
+bool ResourceManager::loadImage(const Common::String &name, Graphics::Surface &surf) {
 	CifInfo info;
 
-	byte *buf = getCifData(treeName, name, info);
+	byte *buf = getCifData(name, info);
 
 	if (!buf)
 		return false;
@@ -724,10 +775,6 @@ bool ResourceManager::loadImage(const Common::String &treeName, const Common::St
 	return true;
 }
 
-void ResourceManager::freeImage(Graphics::Surface &surf) {
-	delete[] (byte *)surf.getPixels();
-}
-
 void ResourceManager::list(const Common::String &treeName, Common::Array<Common::String> &nameList, uint type) {
 	const CifTree *cifTree = findCifTree(treeName);
 
diff --git a/engines/nancy/resource.h b/engines/nancy/resource.h
index 5b23ad2e6b..09d0f65ec7 100644
--- a/engines/nancy/resource.h
+++ b/engines/nancy/resource.h
@@ -64,9 +64,9 @@ public:
 
 	void initialize();
 	bool loadCifTree(const Common::String &name, const Common::String &ext);
-	bool loadImage(const Common::String &treeName, const Common::String &name, Graphics::Surface &surf);
+	bool loadImage(const Common::String &name, Graphics::Surface &surf);
 	void freeImage(Graphics::Surface &surf);
-	byte *loadData(const Common::String &treeName, const Common::String &name, uint &size);
+	byte *loadData(const Common::String &name, uint &size);
 
 	// Debugger functions
 	void list(const Common::String &treeName, Common::Array<Common::String> &nameList, uint type);
@@ -76,7 +76,9 @@ public:
 private:
 	Decompressor *_dec;
 
+	byte *getCifData(const Common::String &name, CifInfo &info, uint *size = nullptr);
 	byte *getCifData(const Common::String &treeName, const Common::String &name, CifInfo &info, uint *size = nullptr);
+	bool getCifInfo(const Common::String &name, CifInfo &info);
 	bool getCifInfo(const Common::String &treeName, const Common::String &name, CifInfo &info);
 	const CifTree *findCifTree(const Common::String &name) const;
 
diff --git a/engines/nancy/state/credits.cpp b/engines/nancy/state/credits.cpp
index 72e2a00e98..55e17fcb21 100644
--- a/engines/nancy/state/credits.cpp
+++ b/engines/nancy/state/credits.cpp
@@ -67,7 +67,7 @@ void Credits::init() {
     _sound.read(*cred, SoundDescription::kMenu);
 
     Graphics::Surface surf;
-    NanEngine.resource->loadImage("ciftree", buf, surf);
+    NanEngine.resource->loadImage(buf, surf);
     _fullTextSurface.create(surf.w, surf.h + _text._screenPosition.height() * 2, GraphicsManager::pixelFormat);
     _fullTextSurface.clear(GraphicsManager::transColor);
     _fullTextSurface.blitFrom(surf, Common::Point(0, _text._screenPosition.height()));
diff --git a/engines/nancy/ui/fullscreenimage.cpp b/engines/nancy/ui/fullscreenimage.cpp
index 9cd1d50681..15cf36e1c7 100644
--- a/engines/nancy/ui/fullscreenimage.cpp
+++ b/engines/nancy/ui/fullscreenimage.cpp
@@ -31,7 +31,7 @@ namespace UI {
 
 void FullScreenImage::init(Common::String imageName) {
     Graphics::Surface surf;
-    NanEngine.resource->loadImage("ciftree", imageName, surf);
+    NanEngine.resource->loadImage(imageName, surf);
 
     Common::Rect srcBounds = Common::Rect(0,0, surf.w, surf.h);
     _screenPosition = srcBounds;
diff --git a/engines/nancy/ui/inventorybox.cpp b/engines/nancy/ui/inventorybox.cpp
index ef62f08b53..6fc9d254e4 100644
--- a/engines/nancy/ui/inventorybox.cpp
+++ b/engines/nancy/ui/inventorybox.cpp
@@ -72,7 +72,7 @@ void InventoryBox::init() {
         readRect(stream, _itemDescriptions[i].sourceRect);
     }
 
-    NanEngine.resource->loadImage("ciftree", inventoryBoxIconsImageName, _iconsSurface);
+    NanEngine.resource->loadImage(inventoryBoxIconsImageName, _iconsSurface);
     
     uint numItems = 11; // TODO
     _fullInventorySurface.create(_screenPosition.width(), _screenPosition.height() * ((numItems / 4) + 1), GraphicsManager::pixelFormat);


Commit: b34fcec89a260820f0f1a0c3a301116633df88e5
    https://github.com/scummvm/scummvm/commit/b34fcec89a260820f0f1a0c3a301116633df88e5
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add loading images from .bmp files

Added code that allows The Vampire Diaries to load its image resources, which are stored in separate .bmp files. However, they are in a different pixel format than the ones from later games, so they don't yet display correctly on screen.

Changed paths:
    engines/nancy/nancy.cpp
    engines/nancy/resource.cpp


diff --git a/engines/nancy/nancy.cpp b/engines/nancy/nancy.cpp
index 1f3f38f75b..9a0e39008c 100644
--- a/engines/nancy/nancy.cpp
+++ b/engines/nancy/nancy.cpp
@@ -121,6 +121,8 @@ Common::Error NancyEngine::run() {
 	SearchMan.addSubDirectoryMatching(gameDataDir, "hdvideo");
 	SearchMan.addSubDirectoryMatching(gameDataDir, "cdvideo");
 	SearchMan.addSubDirectoryMatching(gameDataDir, "iff");
+	SearchMan.addSubDirectoryMatching(gameDataDir, "art");
+	SearchMan.addSubDirectoryMatching(gameDataDir, "font");
 	
 	Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember("data1.cab");
 	if (stream) {
diff --git a/engines/nancy/resource.cpp b/engines/nancy/resource.cpp
index 4d84e4149a..35eb659f46 100644
--- a/engines/nancy/resource.cpp
+++ b/engines/nancy/resource.cpp
@@ -32,6 +32,8 @@
 
 #include "graphics/surface.h"
 
+#include "image/bmp.h"
+
 namespace Nancy {
 
 static void readCifInfo20(Common::File &f, ResourceManager::CifInfo &info, uint32 *dataOffset = nullptr) {
@@ -729,13 +731,12 @@ byte *ResourceManager::loadData(const Common::String &name, uint &size) {
 	if (!buf) {
 		// Data was not found inside a cif tree or a cif file, try to open an .iff file
 		// This is used by The Vampire Diaries
-		Common::File *f = new Common::File;
-		if (f->open(name + ".iff")) {
-			size = f->size();
+		Common::File f;
+		if (f.open(name + ".iff")) {
+			size = f.size();
 			buf = new byte[size];
-			f->read(buf, size);
+			f.read(buf, size);
 		} else {
-			delete f;
 			return nullptr;
 		}
 	} else if (info.type != kResTypeScript) {
@@ -752,19 +753,33 @@ bool ResourceManager::loadImage(const Common::String &name, Graphics::Surface &s
 
 	byte *buf = getCifData(name, info);
 
-	if (!buf)
-		return false;
-
-	if (info.type != kResTypeImage) {
-		warning("Resource '%s' is not an image", name.c_str());
-		delete[] buf;
-		return false;
-	}
+	if (!buf)  {
+		// Couldn't find image in a cif tree, try to open a .bmp file
+		// This is used by The Vampire Diaries
+		Common::File f;
+		if (f.open(name + ".bmp")) {
+			Image::BitmapDecoder dec;
+			if (dec.loadStream(f)) {
+				surf.copyFrom(*dec.getSurface());
+				return true;
+			} else {
+				return false;
+			}
+		} else {
+			return false;
+		}
+	} else  {
+		if (info.type != kResTypeImage) {
+			warning("Resource '%s' is not an image", name.c_str());
+			delete[] buf;
+			return false;
+		}
 
-	if (info.depth != 16) {
-		warning("Image '%s' has unsupported depth %i", name.c_str(), info.depth);
-		delete[] buf;
-		return false;
+		if (info.depth != 16) {
+			warning("Image '%s' has unsupported depth %i", name.c_str(), info.depth);
+			delete[] buf;
+			return false;
+		}
 	}
 
 	surf.w = info.width;


Commit: a0c61660765ec82acfea0de60704173d165e87db
    https://github.com/scummvm/scummvm/commit/a0c61660765ec82acfea0de60704173d165e87db
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Remove unused subclass

Remove the unused NancyEngine_v0 subclass that was erroneously left over when refactoring the code.

Changed paths:
    engines/nancy/nancy.cpp


diff --git a/engines/nancy/nancy.cpp b/engines/nancy/nancy.cpp
index 9a0e39008c..4c286e9aed 100644
--- a/engines/nancy/nancy.cpp
+++ b/engines/nancy/nancy.cpp
@@ -427,15 +427,6 @@ void NancyEngine::setPreviousState() {
 	SWAP<Nancy::State::State *>(_gameFlow.currentState, _gameFlow.previousState);
 }
 
-class NancyEngine_v0 : public NancyEngine {
-public:
-	NancyEngine_v0(OSystem *syst, const NancyGameDescription *gd) : NancyEngine(syst, gd) { }
-
-private:
-	virtual uint getFilenameLen() const { return 9; }
-	virtual void readBootSummary(const IFF &boot);
-};
-
 void NancyEngine::readBootSummary(const IFF &boot) {
 	Common::SeekableReadStream *bsum = getBootChunkStream("BSUM");
 	bsum->seek(0);


Commit: a6f2b172f3a872611da1d05a216955aa0bb4d3d2
    https://github.com/scummvm/scummvm/commit/a6f2b172f3a872611da1d05a216955aa0bb4d3d2
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Hook Logo state to the graphics manager

The logo state now uses a FullScreenImage object instead of blitting directly to the screen.

Changed paths:
    engines/nancy/state/logo.cpp
    engines/nancy/state/logo.h


diff --git a/engines/nancy/state/logo.cpp b/engines/nancy/state/logo.cpp
index a35e7ef427..d3fa10c4cb 100644
--- a/engines/nancy/state/logo.cpp
+++ b/engines/nancy/state/logo.cpp
@@ -61,13 +61,11 @@ void Logo::process() {
 }
 
 void Logo::init() {
-	_surf = new Graphics::Surface;
-
 	Common::SeekableReadStream *lg = NanEngine.getBootChunkStream("LG0");
 	lg->seek(0);
 
-	if (!NanEngine.resource->loadImage(lg->readString(), *_surf))
-		error("Failed to load logo image");
+	_logoImage.init(lg->readString());
+	_logoImage.registerGraphics();
 
 	_state = kStartSound;
 }
@@ -85,21 +83,12 @@ void Logo::startSound() {
 }
 
 void Logo::run() {
-	switch (_runState) {
-	case kBlit:
-		NanEngine._system->copyRectToScreen(_surf->getPixels(), _surf->pitch, 0, 0, _surf->w, _surf->h);
-		_runState = kWait;
-		break;
-	case kWait:
-		if (NanEngine._system->getMillis() - _startTicks >= 7000 || (NanEngine.input->getInput().input & NancyInput::kLeftMouseButtonDown))
-			_state = kStop;
+	if (NanEngine._system->getMillis() - _startTicks >= 7000 || (NanEngine.input->getInput().input & NancyInput::kLeftMouseButtonDown)) {
+		_state = kStop;
 	}
 }
 
 void Logo::stop() {
-	_surf->free();
-	delete _surf;
-
 	// The original engine checks for N+D and N+C key combos here.
 	// For the N+C key combo it looks for some kind of cheat file
 	// to initialize the game state with.
diff --git a/engines/nancy/state/logo.h b/engines/nancy/state/logo.h
index 15339d99fa..19ac0365a3 100644
--- a/engines/nancy/state/logo.h
+++ b/engines/nancy/state/logo.h
@@ -25,6 +25,8 @@
 
 #include "engines/nancy/state/state.h"
 
+#include "engines/nancy/ui/fullscreenimage.h"
+
 #include "common/singleton.h"
 
 namespace Graphics {
@@ -39,11 +41,7 @@ namespace State {
 
 class Logo : public State, public Common::Singleton<Logo> {
 public:
-	Logo() :
-		_state(kInit),
-		_runState(kBlit),
-		_startTicks(0),
-		_surf(nullptr) { }
+	Logo() : _state(kInit), _startTicks(0) { }
 		
 	// State API
     virtual void process() override;
@@ -64,16 +62,9 @@ private:
 		kStop
 	};
 
-	enum RunState {
-		// First four states are related to testing mode
-		kBlit = 4,
-		kWait
-	};
-
 	State _state;
-	RunState _runState;
 	uint _startTicks;
-	Graphics::Surface *_surf;
+	UI::FullScreenImage _logoImage;
 };
 
 #define NancyLogoState Nancy::State::Logo::instance()


Commit: 4eace74d48dce47cd9adc11f2205b41b7a9cc952
    https://github.com/scummvm/scummvm/commit/4eace74d48dce47cd9adc11f2205b41b7a9cc952
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add 8-bit color game flag for The Vampire Diaries

Added a new NancyGameFlags enum and an entry marking The Vampire Diaries' usage of 8-bit color.

Changed paths:
    engines/nancy/detection.cpp
    engines/nancy/detection.h
    engines/nancy/ui/fullscreenimage.cpp


diff --git a/engines/nancy/detection.cpp b/engines/nancy/detection.cpp
index 05036f5d04..6d4f4131ed 100644
--- a/engines/nancy/detection.cpp
+++ b/engines/nancy/detection.cpp
@@ -60,7 +60,7 @@ static const Nancy::NancyGameDescription gameDescriptions[] = {
 			},
 			Common::EN_ANY,
 			Common::kPlatformWindows,
-			ADGF_NO_FLAGS,
+			Nancy::NGF_8BITCOLOR,
 			GUIO0()
 		},
 		Nancy::GameType::kGameTypeVampire
diff --git a/engines/nancy/detection.h b/engines/nancy/detection.h
index 9509590337..65624cee55 100644
--- a/engines/nancy/detection.h
+++ b/engines/nancy/detection.h
@@ -35,6 +35,10 @@ enum GameType {
 	kGameTypeNancy3 = 4
 };
 
+enum NancyGameFlags {
+	NGF_8BITCOLOR = 1 << 0 // Used in The Vampire Diaries
+};
+
 struct NancyGameDescription {
 	ADGameDescription desc;
 	GameType gameType;
diff --git a/engines/nancy/ui/fullscreenimage.cpp b/engines/nancy/ui/fullscreenimage.cpp
index 15cf36e1c7..7597f08d71 100644
--- a/engines/nancy/ui/fullscreenimage.cpp
+++ b/engines/nancy/ui/fullscreenimage.cpp
@@ -30,15 +30,10 @@ namespace Nancy {
 namespace UI {
 
 void FullScreenImage::init(Common::String imageName) {
-    Graphics::Surface surf;
-    NanEngine.resource->loadImage(imageName, surf);
+    NanEngine.resource->loadImage(imageName, _drawSurface);
 
-    Common::Rect srcBounds = Common::Rect(0,0, surf.w, surf.h);
+    Common::Rect srcBounds = Common::Rect(0,0, _drawSurface.w, _drawSurface.h);
     _screenPosition = srcBounds;
-    
-    _drawSurface.create(surf.w, surf.h, surf.format);
-    _drawSurface.blitFrom(surf, Common::Point(0, 0));
-    surf.free();
 
     RenderObject::init();
 }


Commit: 2d757175b00d8d2fc4b996f66911436fcccb6edb
    https://github.com/scummvm/scummvm/commit/2d757175b00d8d2fc4b996f66911436fcccb6edb
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Fix msvc compile errors

Fixed some compile errors msvc was throwing when synchronizing Time objects without casting them to uint32 first.

Changed paths:
    engines/nancy/state/scene.cpp


diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index b0c2eddd2b..fcf3fc61dc 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -196,7 +196,7 @@ void Scene::synchronize(Common::Serializer &ser) {
 	}
 
 	for (uint i = 0; i < 30; ++i) {
-		ser.syncAsUint32LE(_flags.logicConditions[i].timestamp);
+		ser.syncAsUint32LE((uint32 &)_flags.logicConditions[i].timestamp);
 	}
 
 	// TODO hardcoded inventory size
@@ -226,11 +226,11 @@ void Scene::synchronize(Common::Serializer &ser) {
     ser.syncAsSint16LE(_flags.heldItem);
     NanEngine.cursorManager->setCursorItemID(_flags.heldItem);
 
-	ser.syncAsUint32LE(_timers.lastTotalTime);
-	ser.syncAsUint32LE(_timers.sceneTime);
-	ser.syncAsUint32LE(_timers.playerTime);
-	ser.syncAsUint32LE(_timers.pushedPlayTime);
-	ser.syncAsUint32LE(_timers.timerTime);
+	ser.syncAsUint32LE((uint32 &)_timers.lastTotalTime);
+	ser.syncAsUint32LE((uint32 &)_timers.sceneTime);
+	ser.syncAsUint32LE((uint32 &)_timers.playerTime);
+	ser.syncAsUint32LE((uint32 &)_timers.pushedPlayTime);
+	ser.syncAsUint32LE((uint32 &)_timers.timerTime);
 	ser.syncAsByte(_timers.timerIsActive);
     ser.syncAsByte(_timers.timeOfDay);
 


Commit: c7e0c750bb0b11ea099b06f6f60dc0f15110f2a7
    https://github.com/scummvm/scummvm/commit/c7e0c750bb0b11ea099b06f6f60dc0f15110f2a7
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add support for older video header format

Added support for chunk file format 1.0 (used in The Vampire Diaries), which uses frame names instead of ids and has a different id string. Right now the image gets decoded upside-down, and the palette is still unknown.

Changed paths:
    engines/nancy/video.cpp
    engines/nancy/video.h


diff --git a/engines/nancy/video.cpp b/engines/nancy/video.cpp
index ab59145104..1cab9ea481 100644
--- a/engines/nancy/video.cpp
+++ b/engines/nancy/video.cpp
@@ -22,6 +22,7 @@
 
 #include "engines/nancy/video.h"
 #include "engines/nancy/decompress.h"
+#include "engines/nancy/graphics.h"
 
 #include "common/endian.h"
 #include "common/stream.h"
@@ -46,24 +47,31 @@ bool AVFDecoder::loadStream(Common::SeekableReadStream *stream) {
 	char id[15];
 	stream->read(id, 15);
 	id[14] = 0;
+	Common::String idString = id;
 
-	if (stream->eos() || Common::String(id) != "AVF WayneSikes") {
-		warning("Invalid id string found in AVF");
-		return false;
-	}
+	bool earlyHeaderFormat = false;
 
-	stream->skip(1); // Unknown
+	if (idString == "AVF WayneSikes") {	
+		stream->skip(1); // Unknown
+	} else if (idString.hasPrefix("ALG")) {
+		earlyHeaderFormat = true;
+		stream->seek(10, SEEK_SET);
+	}
 
-	uint32 ver;
-	ver = stream->readUint16LE() << 16;
-	ver |= stream->readUint16LE();
+	uint32 chunkFileFormat;
+	chunkFileFormat = stream->readUint16LE() << 16;
+	chunkFileFormat |= stream->readUint16LE();
 
-	if (ver != 0x00020000) {
-		warning("Unsupported version %d.%d found in AVF", ver >> 16, ver & 0xffff);
+	if (chunkFileFormat != 0x00020000 && chunkFileFormat != 0x00010000) {
+		warning("Unsupported version %d.%d found in AVF", chunkFileFormat >> 16, chunkFileFormat & 0xffff);
 		return false;
 	}
 
-	addTrack(new AVFVideoTrack(stream));
+	if (!earlyHeaderFormat) {
+		stream->skip(1); // Unknown
+	}
+
+	addTrack(new AVFVideoTrack(stream, chunkFileFormat));
 
 	return true;
 }
@@ -76,7 +84,7 @@ void AVFDecoder::addFrameTime(const uint16 timeToAdd) {
 	((AVFDecoder::AVFVideoTrack *)getTrack(0))->_frameTime += timeToAdd;
 }
 
-AVFDecoder::AVFVideoTrack::AVFVideoTrack(Common::SeekableReadStream *stream) {
+AVFDecoder::AVFVideoTrack::AVFVideoTrack(Common::SeekableReadStream *stream, uint32 chunkFileFormat) {
 	assert(stream);
 	_fileStream = stream;
 	_curFrame = -1;
@@ -84,8 +92,6 @@ AVFDecoder::AVFVideoTrack::AVFVideoTrack(Common::SeekableReadStream *stream) {
 	_reversed = false;
 	_dec = new Decompressor;
 
-	stream->skip(1); // Unknown
-
 	_frameCount = stream->readUint16LE();
 	_width = stream->readUint16LE();
 	_height = stream->readUint16LE();
@@ -94,23 +100,51 @@ AVFDecoder::AVFVideoTrack::AVFVideoTrack(Common::SeekableReadStream *stream) {
 
 	byte comp = stream->readByte();
 
+	uint formatHi = chunkFileFormat >> 16;
+
+	if (formatHi == 1) {
+		stream->skip(1);
+	}
+
 	if (comp != 2)
 		error("Unknown compression type %d found in AVF", comp);
 
 	_surface = new Graphics::Surface();
-	_pixelFormat = Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0);
+	
+	if (formatHi == 1) {
+		_pixelFormat = Graphics::PixelFormat::createFormatCLUT8();
+	} else if (formatHi == 2) {
+		_pixelFormat = GraphicsManager::pixelFormat;
+	}
+
 	_surface->create(_width, _height, _pixelFormat);
 	_frameSize = _width * _height * _pixelFormat.bytesPerPixel;
 
-
 	for (uint i = 0; i < _frameCount; i++) {
 		ChunkInfo info;
-		info.index = stream->readUint16LE();
-		info.offset = stream->readUint32LE();
-		info.compressedSize = stream->readUint32LE();
-		info.size = stream->readUint32LE();
-		info.type = stream->readByte();
-		stream->skip(4); // Unknown;
+
+		if (formatHi == 1) {
+			char buf[13];
+			stream->read(buf, 13);
+			info.name = buf;
+			info.size = stream->readUint32LE();
+
+			if (info.size == 0) {
+				info.size = _frameSize;
+			}
+			
+			info.offset = stream->readUint32LE();
+			info.compressedSize = stream->readUint32LE();
+			info.type = 0;
+		} else if (formatHi == 2) {
+			info.index = stream->readUint16LE();
+			info.offset = stream->readUint32LE();
+			info.compressedSize = stream->readUint32LE();
+			info.size = stream->readUint32LE();
+			info.type = stream->readByte();
+			stream->skip(4); // Unknown;
+		}
+		
 		_chunkInfo.push_back(info);
 	}
 }
diff --git a/engines/nancy/video.h b/engines/nancy/video.h
index e5ad023de9..afaf15257c 100644
--- a/engines/nancy/video.h
+++ b/engines/nancy/video.h
@@ -52,7 +52,7 @@ private:
 	class AVFVideoTrack : public FixedRateVideoTrack {
 	friend class AVFDecoder;
 	public:
-		AVFVideoTrack(Common::SeekableReadStream *stream);
+		AVFVideoTrack(Common::SeekableReadStream *stream, uint32 chunkFileFormat);
 		virtual ~AVFVideoTrack();
 
 		virtual uint16 getWidth() const override { return _width; }
@@ -73,6 +73,7 @@ private:
 
 	private:
 		struct ChunkInfo {
+			Common::String name;
 			uint16 index;
 			uint32 offset;
 			uint32 compressedSize;


Commit: 5440559918bb109491c3092a16f00decab237026
    https://github.com/scummvm/scummvm/commit/5440559918bb109491c3092a16f00decab237026
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add support for 8-bit color

Added generic methods for getting the pixel format and transparent color. Also added a method for loading an image directly into a ManagedSurface, which also loads the palette and lets The Vampire Diaries logo display properly.

Changed paths:
    engines/nancy/action/leverpuzzle.cpp
    engines/nancy/action/orderingpuzzle.cpp
    engines/nancy/action/passwordpuzzle.cpp
    engines/nancy/action/recordtypes.cpp
    engines/nancy/action/rotatinglockpuzzle.cpp
    engines/nancy/action/secondarymovie.cpp
    engines/nancy/action/secondaryvideo.cpp
    engines/nancy/action/sliderpuzzle.cpp
    engines/nancy/action/staticbitmapanim.cpp
    engines/nancy/action/telephone.cpp
    engines/nancy/cursor.cpp
    engines/nancy/font.cpp
    engines/nancy/graphics.cpp
    engines/nancy/graphics.h
    engines/nancy/nancy.cpp
    engines/nancy/resource.cpp
    engines/nancy/resource.h
    engines/nancy/state/credits.cpp
    engines/nancy/ui/inventorybox.cpp
    engines/nancy/ui/textbox.cpp
    engines/nancy/ui/viewport.cpp
    engines/nancy/ui/viewport.h
    engines/nancy/video.cpp


diff --git a/engines/nancy/action/leverpuzzle.cpp b/engines/nancy/action/leverpuzzle.cpp
index 3f549f9a04..f8ae2b84ad 100644
--- a/engines/nancy/action/leverpuzzle.cpp
+++ b/engines/nancy/action/leverpuzzle.cpp
@@ -33,14 +33,10 @@ namespace Nancy {
 namespace Action {
 
 void LeverPuzzle::init() {
-    _drawSurface.create(_screenPosition.width(), _screenPosition.height(), GraphicsManager::pixelFormat);
-    _drawSurface.clear(GraphicsManager::transColor);
-
-    Graphics::Surface surf;
-    NanEngine.resource->loadImage(imageName, surf);
-    image.create(surf.w, surf.h, surf.format);
-    image.blitFrom(surf);
-    surf.free();
+    _drawSurface.create(_screenPosition.width(), _screenPosition.height(), GraphicsManager::getInputPixelFormat());
+    _drawSurface.clear(GraphicsManager::getTransColor());
+
+    NanEngine.resource->loadImage(imageName, image);
 }
 
 uint16 LeverPuzzle::readData(Common::SeekableReadStream &stream) {
diff --git a/engines/nancy/action/orderingpuzzle.cpp b/engines/nancy/action/orderingpuzzle.cpp
index bca2da5b67..e56e0e644f 100644
--- a/engines/nancy/action/orderingpuzzle.cpp
+++ b/engines/nancy/action/orderingpuzzle.cpp
@@ -40,14 +40,10 @@ namespace Action {
 void OrderingPuzzle::init() {
     // Screen position is initialized in readData and fits exactly the bounds of all elements on screen.
     // This is a hacky way to make this particular action record work with this implementation's graphics manager
-    _drawSurface.create(_screenPosition.width(), _screenPosition.height(), GraphicsManager::pixelFormat);
+    _drawSurface.create(_screenPosition.width(), _screenPosition.height(), GraphicsManager::getInputPixelFormat());
     clearAllElements();
 
-    Graphics::Surface surf;
-    NanEngine.resource->loadImage(imageName, surf);
-    image.create(surf.w, surf.h, surf.format);
-    image.blitFrom(surf);
-    surf.free();
+    NanEngine.resource->loadImage(imageName, image);
 
     setVisible(false);
 
@@ -226,12 +222,12 @@ void OrderingPuzzle::undrawElement(uint id) {
     Common::Rect bounds = destRects[id];
     bounds.translate(-_screenPosition.left, -_screenPosition.top);
 
-    _drawSurface.fillRect(bounds, GraphicsManager::transColor);
+    _drawSurface.fillRect(bounds, GraphicsManager::getTransColor());
     _needsRedraw = true;
 }
 
 void OrderingPuzzle::clearAllElements() {
-    _drawSurface.clear(NanEngine.graphicsManager->transColor);
+    _drawSurface.clear(GraphicsManager::getTransColor());
     setVisible(false);
     clickedSequence.clear();
     return;
diff --git a/engines/nancy/action/passwordpuzzle.cpp b/engines/nancy/action/passwordpuzzle.cpp
index 0e3ef1ad79..9f8a1d4818 100644
--- a/engines/nancy/action/passwordpuzzle.cpp
+++ b/engines/nancy/action/passwordpuzzle.cpp
@@ -35,8 +35,8 @@ namespace Nancy {
 namespace Action {
 
 void PasswordPuzzle::init() {
-    _drawSurface.create(_screenPosition.width(), _screenPosition.height(), GraphicsManager::pixelFormat);
-    _drawSurface.clear(GraphicsManager::transColor);
+    _drawSurface.create(_screenPosition.width(), _screenPosition.height(), GraphicsManager::getInputPixelFormat());
+    _drawSurface.clear(GraphicsManager::getTransColor());
 
     RenderObject::init();
 }
@@ -217,7 +217,7 @@ void PasswordPuzzle::onPause(bool pause) {
 }
 
 void PasswordPuzzle::drawText() {
-    _drawSurface.clear(GraphicsManager::transColor);
+    _drawSurface.clear(GraphicsManager::getTransColor());
     Graphics::Font *font = NanEngine.graphicsManager->getFont(fontID);
 
     Common::Rect bounds = nameBounds;
diff --git a/engines/nancy/action/recordtypes.cpp b/engines/nancy/action/recordtypes.cpp
index e8a76fffe7..fa35920dca 100644
--- a/engines/nancy/action/recordtypes.cpp
+++ b/engines/nancy/action/recordtypes.cpp
@@ -378,11 +378,7 @@ void DifficultyLevel::execute() {
 }
 
 void ShowInventoryItem::init() {
-    Graphics::Surface srcSurf;
-    NanEngine.resource->loadImage(imageName, srcSurf);
-    _fullSurface.create(srcSurf.w, srcSurf.h, srcSurf.format);
-    _fullSurface.blitFrom(srcSurf);
-    srcSurf.free();
+    NanEngine.resource->loadImage(imageName, _fullSurface);
 
     _drawSurface.create(_fullSurface, bitmaps[0].src);
 
diff --git a/engines/nancy/action/rotatinglockpuzzle.cpp b/engines/nancy/action/rotatinglockpuzzle.cpp
index 8b80842616..3befb05c90 100644
--- a/engines/nancy/action/rotatinglockpuzzle.cpp
+++ b/engines/nancy/action/rotatinglockpuzzle.cpp
@@ -35,14 +35,10 @@ namespace Nancy {
 namespace Action {
 
 void RotatingLockPuzzle::init() {
-    _drawSurface.create(_screenPosition.width(), _screenPosition.height(), GraphicsManager::pixelFormat);
-    _drawSurface.clear(GraphicsManager::transColor);
-
-    Graphics::Surface surf;
-    NanEngine.resource->loadImage(imageName, surf);
-    image.create(surf.w, surf.h, surf.format);
-    image.blitFrom(surf);
-    surf.free();
+    _drawSurface.create(_screenPosition.width(), _screenPosition.height(), GraphicsManager::getInputPixelFormat());
+    _drawSurface.clear(GraphicsManager::getTransColor());
+
+    NanEngine.resource->loadImage(imageName, image);
 }
 
 uint16 RotatingLockPuzzle::readData(Common::SeekableReadStream &stream) {
diff --git a/engines/nancy/action/secondarymovie.cpp b/engines/nancy/action/secondarymovie.cpp
index db77ca809a..8ef6988ed7 100644
--- a/engines/nancy/action/secondarymovie.cpp
+++ b/engines/nancy/action/secondarymovie.cpp
@@ -74,7 +74,7 @@ void PlaySecondaryMovie::init() {
     }
 
     _decoder.loadFile(videoName + ".avf");
-    _drawSurface.create(_decoder.getWidth(), _decoder.getHeight(), GraphicsManager::pixelFormat);
+    _drawSurface.create(_decoder.getWidth(), _decoder.getHeight(), GraphicsManager::getInputPixelFormat());
     _screenPosition = _drawSurface.getBounds();
 
     RenderObject::init();
diff --git a/engines/nancy/action/secondaryvideo.cpp b/engines/nancy/action/secondaryvideo.cpp
index ddee547465..9c5beff30d 100644
--- a/engines/nancy/action/secondaryvideo.cpp
+++ b/engines/nancy/action/secondaryvideo.cpp
@@ -50,7 +50,7 @@ void PlaySecondaryVideo::init() {
     // Every secondary video frame (in nancy1) plays exactly 12ms slower than what its metadata says.
     // I'm still not sure how/why that happens so for now I'm using this hack to fix the timings
     _decoder.addFrameTime(12);
-    _drawSurface.create(_decoder.getWidth(), _decoder.getHeight(), GraphicsManager::pixelFormat);
+    _drawSurface.create(_decoder.getWidth(), _decoder.getHeight(), GraphicsManager::getInputPixelFormat());
 
     setVisible(false);
 
diff --git a/engines/nancy/action/sliderpuzzle.cpp b/engines/nancy/action/sliderpuzzle.cpp
index 1c940eb598..bd1c2348b1 100644
--- a/engines/nancy/action/sliderpuzzle.cpp
+++ b/engines/nancy/action/sliderpuzzle.cpp
@@ -38,14 +38,10 @@ Common::Array<Common::Array<int16>> SliderPuzzle::playerTileOrder = Common::Arra
 bool SliderPuzzle::playerHasTriedPuzzle = false;
 
 void SliderPuzzle::init() {
-    _drawSurface.create(_screenPosition.width(), _screenPosition.height(), GraphicsManager::pixelFormat);
-    _drawSurface.clear(GraphicsManager::transColor);
-
-    Graphics::Surface surf;
-    NanEngine.resource->loadImage(imageName, surf);
-    image.create(surf.w, surf.h, surf.format);
-    image.blitFrom(surf);
-    surf.free();
+    _drawSurface.create(_screenPosition.width(), _screenPosition.height(), GraphicsManager::getInputPixelFormat());
+    _drawSurface.clear(GraphicsManager::getTransColor());
+
+    NanEngine.resource->loadImage(imageName, image);
 }
 
 uint16 SliderPuzzle::readData(Common::SeekableReadStream &stream) {
@@ -331,7 +327,7 @@ void SliderPuzzle::drawTile(int tileID, uint posX, uint posY) {
 void SliderPuzzle::undrawTile(uint posX, uint posY) {
     Common::Rect bounds = destRects[posY][posX];
     bounds.translate(-_screenPosition.left, -_screenPosition.top);
-    _drawSurface.fillRect(bounds, GraphicsManager::transColor);
+    _drawSurface.fillRect(bounds, GraphicsManager::getTransColor());
 
     _needsRedraw = true;
 }
diff --git a/engines/nancy/action/staticbitmapanim.cpp b/engines/nancy/action/staticbitmapanim.cpp
index 7e76da4091..cf6c54f416 100644
--- a/engines/nancy/action/staticbitmapanim.cpp
+++ b/engines/nancy/action/staticbitmapanim.cpp
@@ -35,12 +35,8 @@ namespace Nancy {
 namespace Action {
 
 void PlayStaticBitmapAnimation::init() {
-    Graphics::Surface surf;
-    NanEngine.resource->loadImage(imageName, surf);
+    NanEngine.resource->loadImage(imageName, _fullSurface);
 
-    _fullSurface.create(surf.w, surf.h, surf.format);
-    _fullSurface.blitFrom(surf);
-    surf.free();
     setFrame(0);
 
     RenderObject::init();
diff --git a/engines/nancy/action/telephone.cpp b/engines/nancy/action/telephone.cpp
index 61abd428e8..92ad6b81e5 100644
--- a/engines/nancy/action/telephone.cpp
+++ b/engines/nancy/action/telephone.cpp
@@ -37,14 +37,10 @@ namespace Nancy {
 namespace Action {
 
 void Telephone::init() {
-    _drawSurface.create(_screenPosition.width(), _screenPosition.height(), GraphicsManager::pixelFormat);
-    _drawSurface.clear(GraphicsManager::transColor);
+    _drawSurface.create(_screenPosition.width(), _screenPosition.height(), GraphicsManager::getInputPixelFormat());
+    _drawSurface.clear(GraphicsManager::getTransColor());
 
-    Graphics::Surface surf;
-    NanEngine.resource->loadImage(imageName, surf);
-    image.create(surf.w, surf.h, surf.format);
-    image.blitFrom(surf);
-    surf.free();
+    NanEngine.resource->loadImage(imageName, image);
 
     NancySceneState.setShouldClearTextbox(false);
 }
@@ -312,7 +308,7 @@ void Telephone::undrawButton(uint id) {
     Common::Rect bounds = destRects[id];
     bounds.translate(-_screenPosition.left, -_screenPosition.top);
 
-    _drawSurface.fillRect(bounds, GraphicsManager::transColor);
+    _drawSurface.fillRect(bounds, GraphicsManager::getTransColor());
     _needsRedraw = true;
 }
 
diff --git a/engines/nancy/cursor.cpp b/engines/nancy/cursor.cpp
index 9b77df0a5b..3652ccfc63 100644
--- a/engines/nancy/cursor.cpp
+++ b/engines/nancy/cursor.cpp
@@ -51,12 +51,7 @@ void CursorManager::init() {
     _primaryVideoInitialPos.x = chunk->readUint16LE();
     _primaryVideoInitialPos.y = chunk->readUint16LE();
 
-    Graphics::Surface surf;
-    NanEngine.resource->loadImage(inventoryCursorsImageName, surf);
-    _invCursorsSurface.create(surf.w, surf.h, surf.format);
-    _invCursorsSurface.blitFrom(surf);
-
-    surf.free();
+    NanEngine.resource->loadImage(inventoryCursorsImageName, _invCursorsSurface);
 
     setCursor(kNormalArrow, -1);
     showCursor(false);
@@ -121,7 +116,7 @@ void CursorManager::setCursor(CursorType type, int16 itemID) {
     s.copyRectToSurface(*surf, 0, 0, bounds);
 
     // TODO hotspots are terrible for arrow cursors, fix that??
-    CursorMan.replaceCursor(s.getPixels(), s.w, s.h, hotspot.x, hotspot.y, GraphicsManager::transColor, false, &GraphicsManager::pixelFormat);
+    CursorMan.replaceCursor(s.getPixels(), s.w, s.h, hotspot.x, hotspot.y, GraphicsManager::getTransColor(), false, &GraphicsManager::getInputPixelFormat());
 
     s.free();
 
diff --git a/engines/nancy/font.cpp b/engines/nancy/font.cpp
index dc3c39ea99..9172109a4f 100644
--- a/engines/nancy/font.cpp
+++ b/engines/nancy/font.cpp
@@ -30,7 +30,7 @@
 namespace Nancy {
 
 void Font::read(Common::SeekableReadStream &stream) {
-    _transColor = NanEngine.graphicsManager->transColor;
+    _transColor = GraphicsManager::getTransColor();
     _maxCharWidth = 0;
     _fontHeight = 0;
 
diff --git a/engines/nancy/graphics.cpp b/engines/nancy/graphics.cpp
index 99630cfcc6..4bb5e297c8 100644
--- a/engines/nancy/graphics.cpp
+++ b/engines/nancy/graphics.cpp
@@ -30,23 +30,36 @@
 #include "engines/nancy/state/scene.h"
 #include "engines/nancy/ui/viewport.h"
 
+#include "common/file.h"
+#include "common/system.h"
+
+#include "image/bmp.h"
+
+#include "engines/util.h"
+
 namespace Nancy {
 
-const Graphics::PixelFormat GraphicsManager::pixelFormat = Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0);
+const Graphics::PixelFormat GraphicsManager::inputPixelFormat = Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0);
+const Graphics::PixelFormat GraphicsManager::outputPixelFormat = Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0);
+const Graphics::PixelFormat GraphicsManager::clut8Format = Graphics::PixelFormat::createFormatCLUT8();
 const uint GraphicsManager::transColor = 0x3E0;
 
 void GraphicsManager::init() {
-    _screen.create(640, 480, pixelFormat);
-    _screen.setTransparentColor(transColor); 
+    initGraphics(640, 480, &outputPixelFormat);
+    _screen.create(640, 480, outputPixelFormat);
+    _screen.setTransparentColor(getTransColor());
+
+    auto formats = NanEngine._system->getSupportedFormats();
+
+    for (auto f : formats) {
+        debug(f.toString().c_str());
+
+    }
 
     Common::SeekableReadStream *ob = NanEngine.getBootChunkStream("OB0");
     ob->seek(0);
 
-    Graphics::Surface surf;
-    NanEngine.resource->loadImage(ob->readString(), surf);
-    object0.create(surf.w, surf.h, surf.format);
-    object0.blitFrom(surf, Common::Point(0, 0));
-    surf.free();
+    NanEngine.resource->loadImage(ob->readString(), object0);
 
     loadFonts();
 }
@@ -127,6 +140,23 @@ void GraphicsManager::redrawAll() {
     }
 }
 
+const Graphics::PixelFormat &GraphicsManager::getInputPixelFormat() {
+    if (NanEngine._gameDescription->desc.flags & NGF_8BITCOLOR) {
+        return clut8Format;
+    } else {
+        return inputPixelFormat;
+    }
+}
+
+uint GraphicsManager::getTransColor() {
+    if (NanEngine._gameDescription->desc.flags & NGF_8BITCOLOR) {
+        Graphics::PixelFormat format = Graphics::PixelFormat::createFormatCLUT8();
+        return format.RGBToColor(255, 0, 255);
+    } else {
+        return transColor;
+    }
+}
+
 void GraphicsManager::loadFonts() {
     Common::SeekableReadStream *chunk = NanEngine.getBootChunkStream("FONT");
     
diff --git a/engines/nancy/graphics.h b/engines/nancy/graphics.h
index bd8a281bf7..6662e742b9 100644
--- a/engines/nancy/graphics.h
+++ b/engines/nancy/graphics.h
@@ -50,19 +50,25 @@ public:
 
     Font *getFont(uint id) { return id < _fonts.size() ? &_fonts[id] : nullptr; }
 
+    static const Graphics::PixelFormat &getInputPixelFormat();
+    static uint getTransColor();
+
     Graphics::ManagedSurface object0;
     
-    static const Graphics::PixelFormat pixelFormat;
-    static const uint transColor;
-
 private:
     void loadFonts();
     void blitToScreen(const RenderObject &src, Common::Rect dest);
 
     static int objectComparator(const void *a, const void *b);
 
+    static const uint transColor;
+
     Common::SortedArray<RenderObject *> _objects;
 
+    static const Graphics::PixelFormat inputPixelFormat;
+    static const Graphics::PixelFormat outputPixelFormat;
+    static const Graphics::PixelFormat clut8Format;
+
     Graphics::Screen _screen;
     Common::Array<Font> _fonts;
 };
diff --git a/engines/nancy/nancy.cpp b/engines/nancy/nancy.cpp
index 4c286e9aed..49b13b9c14 100644
--- a/engines/nancy/nancy.cpp
+++ b/engines/nancy/nancy.cpp
@@ -111,8 +111,6 @@ Common::Platform NancyEngine::getPlatform() const {
 }
 
 Common::Error NancyEngine::run() {
-	initGraphics(640, 480, &GraphicsManager::pixelFormat);
-
 	const Common::FSNode gameDataDir(ConfMan.get("path"));
 	SearchMan.addSubDirectoryMatching(gameDataDir, "game");
 	SearchMan.addSubDirectoryMatching(gameDataDir, "datafiles");
diff --git a/engines/nancy/resource.cpp b/engines/nancy/resource.cpp
index 35eb659f46..e179549312 100644
--- a/engines/nancy/resource.cpp
+++ b/engines/nancy/resource.cpp
@@ -23,6 +23,7 @@
 #include "engines/nancy/resource.h"
 #include "engines/nancy/decompress.h"
 #include "engines/nancy/graphics.h"
+#include "engines/nancy/nancy.h"
 
 #include "common/file.h"
 #include "common/textconsole.h"
@@ -750,6 +751,7 @@ byte *ResourceManager::loadData(const Common::String &name, uint &size) {
 
 bool ResourceManager::loadImage(const Common::String &name, Graphics::Surface &surf) {
 	CifInfo info;
+	surf.free();
 
 	byte *buf = getCifData(name, info);
 
@@ -768,7 +770,7 @@ bool ResourceManager::loadImage(const Common::String &name, Graphics::Surface &s
 		} else {
 			return false;
 		}
-	} else  {
+	} else {
 		if (info.type != kResTypeImage) {
 			warning("Resource '%s' is not an image", name.c_str());
 			delete[] buf;
@@ -786,7 +788,63 @@ bool ResourceManager::loadImage(const Common::String &name, Graphics::Surface &s
 	surf.h = info.height;
 	surf.pitch = info.pitch;
 	surf.setPixels(buf);
-	surf.format = GraphicsManager::pixelFormat;
+	surf.format = GraphicsManager::getInputPixelFormat();
+	return true;
+}
+
+bool ResourceManager::loadImage(const Common::String &name, Graphics::ManagedSurface &surf) {
+	CifInfo info;
+	Image::BitmapDecoder dec;
+	Common::File f;
+	bool loadedFromBitmapFile = false;
+	surf.free();
+
+	Graphics::Surface tmpSurf;
+
+	byte *buf = getCifData(name, info);
+
+	if (!buf)  {
+		// Couldn't find image in a cif tree, try to open a .bmp file
+		// This is used by The Vampire Diaries
+		loadedFromBitmapFile = f.open(name + ".bmp");
+		if (loadedFromBitmapFile) {
+			if (dec.loadStream(f)) {
+				tmpSurf.copyFrom(*dec.getSurface());
+			} else {
+				return false;
+			}
+		} else {
+			return false;
+		}
+	} else {
+		if (info.type != kResTypeImage) {
+			warning("Resource '%s' is not an image", name.c_str());
+			delete[] buf;
+			return false;
+		}
+
+		if (info.depth != 16) {
+			warning("Image '%s' has unsupported depth %i", name.c_str(), info.depth);
+			delete[] buf;
+			return false;
+		}
+
+		tmpSurf.w = info.width;
+		tmpSurf.h = info.height;
+		tmpSurf.pitch = info.pitch;
+		tmpSurf.setPixels(buf);
+		tmpSurf.format = GraphicsManager::getInputPixelFormat();
+	}
+
+	surf.create(tmpSurf.w, tmpSurf.h, tmpSurf.format);
+	surf.blitFrom(tmpSurf);
+	
+	if (NanEngine._gameDescription->desc.flags & NGF_8BITCOLOR && loadedFromBitmapFile) {
+		surf.setPalette(dec.getPalette(), dec.getPaletteStartIndex(), dec.getPaletteColorCount() - 1);
+	}
+
+	tmpSurf.free();
+
 	return true;
 }
 
diff --git a/engines/nancy/resource.h b/engines/nancy/resource.h
index 09d0f65ec7..b635ee7a01 100644
--- a/engines/nancy/resource.h
+++ b/engines/nancy/resource.h
@@ -28,6 +28,7 @@
 
 namespace Graphics {
 struct Surface;
+class ManagedSurface;
 }
 
 namespace Nancy {
@@ -65,6 +66,7 @@ public:
 	void initialize();
 	bool loadCifTree(const Common::String &name, const Common::String &ext);
 	bool loadImage(const Common::String &name, Graphics::Surface &surf);
+	bool loadImage(const Common::String &name, Graphics::ManagedSurface &surf);
 	void freeImage(Graphics::Surface &surf);
 	byte *loadData(const Common::String &name, uint &size);
 
diff --git a/engines/nancy/state/credits.cpp b/engines/nancy/state/credits.cpp
index 55e17fcb21..1a3fdfd3dd 100644
--- a/engines/nancy/state/credits.cpp
+++ b/engines/nancy/state/credits.cpp
@@ -66,12 +66,7 @@ void Credits::init() {
     _pixelsToScroll = cred->readUint16LE();
     _sound.read(*cred, SoundDescription::kMenu);
 
-    Graphics::Surface surf;
-    NanEngine.resource->loadImage(buf, surf);
-    _fullTextSurface.create(surf.w, surf.h + _text._screenPosition.height() * 2, GraphicsManager::pixelFormat);
-    _fullTextSurface.clear(GraphicsManager::transColor);
-    _fullTextSurface.blitFrom(surf, Common::Point(0, _text._screenPosition.height()));
-    surf.free();
+    NanEngine.resource->loadImage(buf, _fullTextSurface);
     
     Common::Rect src = _text._screenPosition;
     src.moveTo(Common::Point());
diff --git a/engines/nancy/ui/inventorybox.cpp b/engines/nancy/ui/inventorybox.cpp
index 6fc9d254e4..82bf9a8cca 100644
--- a/engines/nancy/ui/inventorybox.cpp
+++ b/engines/nancy/ui/inventorybox.cpp
@@ -75,7 +75,7 @@ void InventoryBox::init() {
     NanEngine.resource->loadImage(inventoryBoxIconsImageName, _iconsSurface);
     
     uint numItems = 11; // TODO
-    _fullInventorySurface.create(_screenPosition.width(), _screenPosition.height() * ((numItems / 4) + 1), GraphicsManager::pixelFormat);
+    _fullInventorySurface.create(_screenPosition.width(), _screenPosition.height() * ((numItems / 4) + 1), GraphicsManager::getInputPixelFormat());
     Common::Rect sourceRect = _screenPosition;
     sourceRect.moveTo(0, 0);
     _drawSurface.create(_fullInventorySurface, sourceRect);
@@ -224,7 +224,7 @@ void InventoryBox::InventoryScrollbar::init() {
 
 void InventoryBox::Shades::init() {
     Common::Rect bounds = _parent->getBounds();
-    _drawSurface.create(bounds.width(), bounds.height(), GraphicsManager::pixelFormat);
+    _drawSurface.create(bounds.width(), bounds.height(), GraphicsManager::getInputPixelFormat());
     _screenPosition = _parent->getScreenPosition();
     _nextFrameTime = 0;
     setAnimationFrame(_curFrame);
@@ -273,7 +273,7 @@ void InventoryBox::Shades::setAnimationFrame(uint frame) {
         setVisible(true);
     }
 
-    _drawSurface.clear(GraphicsManager::transColor);
+    _drawSurface.clear(GraphicsManager::getTransColor());
 
     // Draw left shade
     srcRect = _parent->_shadesSrc[frame * 2];
diff --git a/engines/nancy/ui/textbox.cpp b/engines/nancy/ui/textbox.cpp
index 0f741e1bd8..e7f95e639a 100644
--- a/engines/nancy/ui/textbox.cpp
+++ b/engines/nancy/ui/textbox.cpp
@@ -55,7 +55,7 @@ void Textbox::init() {
     chunk->seek(0x20);
     Common::Rect innerBoundingBox;
     readRect(*chunk, innerBoundingBox);
-    _fullSurface.create(innerBoundingBox.width(), innerBoundingBox.height(), GraphicsManager::pixelFormat);
+    _fullSurface.create(innerBoundingBox.width(), innerBoundingBox.height(), GraphicsManager::getInputPixelFormat());
     
     _scrollbarDefaultDest.x = chunk->readUint16LE();
     _scrollbarDefaultDest.y = chunk->readUint16LE();
diff --git a/engines/nancy/ui/viewport.cpp b/engines/nancy/ui/viewport.cpp
index d030df439f..9cd7a72f80 100644
--- a/engines/nancy/ui/viewport.cpp
+++ b/engines/nancy/ui/viewport.cpp
@@ -199,7 +199,7 @@ void Viewport::setFrame(uint frameNr) {
     const Graphics::Surface *newFrame = _decoder.decodeFrame(frameNr);
 
     if (_fullFrame.w != newFrame->w || _fullFrame.h != newFrame->h) {
-        _fullFrame.create(newFrame->w, newFrame->h, GraphicsManager::pixelFormat);
+        _fullFrame.create(newFrame->w, newFrame->h, GraphicsManager::getInputPixelFormat());
     }
 
     _fullFrame.blitFrom(*newFrame);
diff --git a/engines/nancy/ui/viewport.h b/engines/nancy/ui/viewport.h
index ee3e1cd33a..8a1bc87a2c 100644
--- a/engines/nancy/ui/viewport.h
+++ b/engines/nancy/ui/viewport.h
@@ -50,9 +50,7 @@ public:
     void handleInput(NancyInput &input);
 
     void loadVideo(const Common::String &filename, uint frameNr = 0, uint verticalScroll = 0);
-
-    // Check for click events and return a MovementDirection
-
+    
     void setFrame(uint frameNr);
     void setNextFrame();
     void setPreviousFrame();
diff --git a/engines/nancy/video.cpp b/engines/nancy/video.cpp
index 1cab9ea481..58db17a84d 100644
--- a/engines/nancy/video.cpp
+++ b/engines/nancy/video.cpp
@@ -110,13 +110,7 @@ AVFDecoder::AVFVideoTrack::AVFVideoTrack(Common::SeekableReadStream *stream, uin
 		error("Unknown compression type %d found in AVF", comp);
 
 	_surface = new Graphics::Surface();
-	
-	if (formatHi == 1) {
-		_pixelFormat = Graphics::PixelFormat::createFormatCLUT8();
-	} else if (formatHi == 2) {
-		_pixelFormat = GraphicsManager::pixelFormat;
-	}
-
+	_pixelFormat = GraphicsManager::getInputPixelFormat();
 	_surface->create(_width, _height, _pixelFormat);
 	_frameSize = _width * _height * _pixelFormat.bytesPerPixel;
 


Commit: d16876272aa0e3bf5f0bf720cb18ab55ca0acf43
    https://github.com/scummvm/scummvm/commit/d16876272aa0e3bf5f0bf720cb18ab55ca0acf43
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Load hints only when a HINT chunk exists

Fixed a crash in The Vampire Diaries caused by trying to access a nonexistent HINT chunk.

Changed paths:
    engines/nancy/state/scene.cpp


diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index fcf3fc61dc..f5c60a00df 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -272,15 +272,18 @@ void Scene::init() {
     _sceneState.nextScene.sceneID = NanEngine.firstSceneID;
 
     Common::SeekableReadStream *chunk = NanEngine.getBootChunkStream("HINT");
-    chunk->seek(0);
+    
+    if (chunk) {
+        chunk->seek(0);
 
-    _hintsRemaining.clear();
+        _hintsRemaining.clear();
 
-    for (uint i = 0; i < 3; ++i) {
-        _hintsRemaining.push_back(chunk->readByte());
-    }
+        for (uint i = 0; i < 3; ++i) {
+            _hintsRemaining.push_back(chunk->readByte());
+        }
 
-    _lastHint = -1;
+        _lastHint = -1;
+    }
 
     Action::SliderPuzzle::playerHasTriedPuzzle = false;
 


Commit: 8468071ca266f0a748216c7680d851c11e6b5ce5
    https://github.com/scummvm/scummvm/commit/8468071ca266f0a748216c7680d851c11e6b5ce5
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Load video palette from scene summary

Added support for The Vampire Diaries' scene summary format, which contains the name of a dummy bitmap file with an embedded palette.

Changed paths:
    engines/nancy/state/scene.cpp
    engines/nancy/state/scene.h
    engines/nancy/ui/viewport.cpp
    engines/nancy/ui/viewport.h


diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index f5c60a00df..16c7a3bb60 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -339,7 +339,7 @@ void Scene::load() {
         error("Invalid IFF Chunk SSUM");
     }
 
-    readSceneSummary(*sceneSummaryChunk);
+    _sceneState.summary.read(*sceneSummaryChunk);
 
     debugC(0, kDebugScene, "Loading new scene %i: description \"%s\", frame %i, vertical scroll %i, doNotStartSound == %s",
                 _sceneState.nextScene.sceneID,
@@ -359,7 +359,7 @@ void Scene::load() {
         _actionManager.addNewActionRecord(*actionRecordChunk);
     }
 
-    _viewport.loadVideo(_sceneState.summary.videoFile, _sceneState.nextScene.frameID, _sceneState.nextScene.verticalOffset);
+    _viewport.loadVideo(_sceneState.summary.videoFile, _sceneState.nextScene.frameID, _sceneState.nextScene.verticalOffset, _sceneState.summary.videoPaletteFile);
 
     // TODO TEMPORARY
     _viewport.setEdgesSize(25, 25, 25, 25);
@@ -466,35 +466,42 @@ void Scene::run() {
     _actionManager.processActionRecords();
 }
 
-void Scene::readSceneSummary(Common::SeekableReadStream &stream) {
+void Scene::SceneSummary::read(Common::SeekableReadStream &stream) {
     char *buf = new char[0x32];
 
     stream.seek(0);
-    stream.read(buf, 0x31);
-    buf[32] = 0;
-    _sceneState.summary.description = Common::String(buf);
+    Common::Serializer ser(&stream, nullptr);
+    ser.setVersion(NanEngine._gameDescription->gameType);
 
-    stream.seek(1, SEEK_CUR);
-    stream.read(buf, 9);
+    ser.syncBytes((byte *)buf, 0x32);
+    description = Common::String(buf);
+
+    ser.syncBytes((byte *)buf, 10);
     buf[9] = 0;
-    _sceneState.summary.videoFile = Common::String(buf);
+    videoFile = Common::String(buf);
+
+    // skip 2 unknown bytes
+    ser.skip(2);
+    videoFormat = stream.readUint16LE();
 
-    // skip 1 extra byte & 2 unknown bytes
-    stream.seek(3, SEEK_CUR);
-    _sceneState.summary.videoFormat = stream.readUint16LE();
+    // Load the palette data in The Vampire Diaries
+    ser.skip(4, kGameTypeVampire, kGameTypeVampire);
+    ser.syncBytes((byte *)buf, 10, kGameTypeVampire, kGameTypeVampire);
+    videoPaletteFile = buf;
+    ser.skip(0x14, kGameTypeVampire, kGameTypeVampire);
 
-    _sceneState.summary.sound.read(stream, SoundDescription::kScene);
+    sound.read(stream, SoundDescription::kScene);
 
-    stream.seek(0x72);
-    _sceneState.summary.verticalScrollDelta = stream.readUint16LE();
-    _sceneState.summary.horizontalEdgeSize = stream.readUint16LE();
-    _sceneState.summary.verticalEdgeSize = stream.readUint16LE();
-    _sceneState.summary.slowMoveTimeDelta = stream.readUint16LE();
-    _sceneState.summary.fastMoveTimeDelta = stream.readUint16LE();
+    ser.skip(0x10);
+    ser.syncAsUint16LE(verticalScrollDelta);
+    ser.syncAsUint16LE(horizontalEdgeSize);
+    ser.syncAsUint16LE(verticalEdgeSize);
+    ser.syncAsUint16LE((uint32 &)slowMoveTimeDelta);
+    ser.syncAsUint16LE((uint32 &)fastMoveTimeDelta);
 
     if (NanEngine.overrideMovementTimeDeltas) {
-        _sceneState.summary.slowMoveTimeDelta = NanEngine.slowMovementTimeDelta;
-        _sceneState.summary.fastMoveTimeDelta = NanEngine.fastMovementTimeDelta;
+        slowMoveTimeDelta = NanEngine.slowMovementTimeDelta;
+        fastMoveTimeDelta = NanEngine.fastMovementTimeDelta;
     }
 
     delete[] buf;
diff --git a/engines/nancy/state/scene.h b/engines/nancy/state/scene.h
index d3bb9a284a..18daa8392e 100644
--- a/engines/nancy/state/scene.h
+++ b/engines/nancy/state/scene.h
@@ -72,20 +72,23 @@ class Scene : public State, public Common::Singleton<Scene> {
     friend class Nancy::CheatDialog;
 public:
     struct SceneSummary { // SSUM
-        Common::String description;             // 0x00
-        Common::String videoFile;               // 0x32
+        Common::String description;
+        Common::String videoFile;
         //
-        uint16 videoFormat;                     // 0x3E, value is 1 or 2
+        uint16 videoFormat;
+        Common::String videoPaletteFile;
         Common::String audioFile;               
-        SoundDescription sound;   // 0x40
+        SoundDescription sound;
         //
-        uint16 verticalScrollDelta;             // 0x72
-        uint16 horizontalEdgeSize;              // 0x74
-        uint16 verticalEdgeSize;                // 0x76
-        Time slowMoveTimeDelta;                 // 0x78
-        Time fastMoveTimeDelta;                 // 0x7A
+        uint16 verticalScrollDelta;
+        uint16 horizontalEdgeSize;
+        uint16 verticalEdgeSize;
+        Time slowMoveTimeDelta;
+        Time fastMoveTimeDelta;
         // byte unknown7C enum with 4 values
         //
+
+        void read(Common::SeekableReadStream &stream);
     };
 
     Scene() :
@@ -164,8 +167,6 @@ private:
     void load();
     void run();
 
-    void readSceneSummary(Common::SeekableReadStream &stream);
-
     void clearSceneData();
 
 public:
diff --git a/engines/nancy/ui/viewport.cpp b/engines/nancy/ui/viewport.cpp
index 9cd7a72f80..cbbd081f9d 100644
--- a/engines/nancy/ui/viewport.cpp
+++ b/engines/nancy/ui/viewport.cpp
@@ -29,6 +29,10 @@
 #include "engines/nancy/cursor.h"
 #include "engines/nancy/util.h"
 
+#include "common/file.h"
+
+#include "image/bmp.h"
+
 namespace Nancy {
 namespace UI {
 
@@ -178,7 +182,7 @@ void Viewport::handleInput(NancyInput &input) {
     _movementLastFrame = direction;
 }
 
-void Viewport::loadVideo(const Common::String &filename, uint frameNr, uint verticalScroll) {
+void Viewport::loadVideo(const Common::String &filename, uint frameNr, uint verticalScroll, const Common::String &palette) {
     if (_decoder.isVideoLoaded()) {
         _decoder.close();
     }
@@ -189,6 +193,17 @@ void Viewport::loadVideo(const Common::String &filename, uint frameNr, uint vert
     setFrame(frameNr);
     setVerticalScroll(verticalScroll);
 
+    if (palette.size()) {
+        Common::File paletteFile;
+        if (paletteFile.open(palette + ".bmp")) {
+            Image::BitmapDecoder b;
+            if (b.loadStream(paletteFile)) {
+                _drawSurface.setPalette(b.getPalette(), b.getPaletteStartIndex(), b.getPaletteColorCount() - 1);
+                _fullFrame.setPalette(b.getPalette(), b.getPaletteStartIndex(), b.getPaletteColorCount() - 1);
+            }
+        }
+    }
+
     _movementLastFrame = 0;
     _nextMovementTime = 0;
 }
diff --git a/engines/nancy/ui/viewport.h b/engines/nancy/ui/viewport.h
index 8a1bc87a2c..e4285c9732 100644
--- a/engines/nancy/ui/viewport.h
+++ b/engines/nancy/ui/viewport.h
@@ -49,7 +49,7 @@ public:
     virtual void init() override;
     void handleInput(NancyInput &input);
 
-    void loadVideo(const Common::String &filename, uint frameNr = 0, uint verticalScroll = 0);
+    void loadVideo(const Common::String &filename, uint frameNr = 0, uint verticalScroll = 0, const Common::String &palette = Common::String());
     
     void setFrame(uint frameNr);
     void setNextFrame();


Commit: 8fccf950fe6f43f9428263a45e8bf5250d2452e1
    https://github.com/scummvm/scummvm/commit/8fccf950fe6f43f9428263a45e8bf5250d2452e1
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Initialize additional surfaces with screen format

Changed surfaces in the textbox and inventory box so they are now initialized with the screen pixel format instead of the one used inside images. With this change The Vampire Diaries no longer fails a particular assert in the ManagedSurface blitting code, and can now boot successfully.

Changed paths:
    engines/nancy/graphics.cpp
    engines/nancy/graphics.h
    engines/nancy/state/scene.cpp
    engines/nancy/ui/inventorybox.cpp
    engines/nancy/ui/textbox.cpp


diff --git a/engines/nancy/graphics.cpp b/engines/nancy/graphics.cpp
index 4bb5e297c8..014e31921f 100644
--- a/engines/nancy/graphics.cpp
+++ b/engines/nancy/graphics.cpp
@@ -40,13 +40,13 @@
 namespace Nancy {
 
 const Graphics::PixelFormat GraphicsManager::inputPixelFormat = Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0);
-const Graphics::PixelFormat GraphicsManager::outputPixelFormat = Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0);
+const Graphics::PixelFormat GraphicsManager::screenPixelFormat = Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0);
 const Graphics::PixelFormat GraphicsManager::clut8Format = Graphics::PixelFormat::createFormatCLUT8();
 const uint GraphicsManager::transColor = 0x3E0;
 
 void GraphicsManager::init() {
-    initGraphics(640, 480, &outputPixelFormat);
-    _screen.create(640, 480, outputPixelFormat);
+    initGraphics(640, 480, &screenPixelFormat);
+    _screen.create(640, 480, screenPixelFormat);
     _screen.setTransparentColor(getTransColor());
 
     auto formats = NanEngine._system->getSupportedFormats();
diff --git a/engines/nancy/graphics.h b/engines/nancy/graphics.h
index 6662e742b9..334a43a315 100644
--- a/engines/nancy/graphics.h
+++ b/engines/nancy/graphics.h
@@ -55,6 +55,8 @@ public:
 
     Graphics::ManagedSurface object0;
     
+    static const Graphics::PixelFormat screenPixelFormat;
+    
 private:
     void loadFonts();
     void blitToScreen(const RenderObject &src, Common::Rect dest);
@@ -66,7 +68,6 @@ private:
     Common::SortedArray<RenderObject *> _objects;
 
     static const Graphics::PixelFormat inputPixelFormat;
-    static const Graphics::PixelFormat outputPixelFormat;
     static const Graphics::PixelFormat clut8Format;
 
     Graphics::Screen _screen;
diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index 16c7a3bb60..e982c35c24 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -164,6 +164,7 @@ void Scene::registerGraphics() {
     _textbox.registerGraphics();
     _inventoryBox.registerGraphics();
     _menuButton.registerGraphics();
+    _helpButton.registerGraphics();
 
     _textbox.setVisible(!_shouldClearTextbox);
     _menuButton.setVisible(false);
diff --git a/engines/nancy/ui/inventorybox.cpp b/engines/nancy/ui/inventorybox.cpp
index 82bf9a8cca..30824868be 100644
--- a/engines/nancy/ui/inventorybox.cpp
+++ b/engines/nancy/ui/inventorybox.cpp
@@ -75,7 +75,7 @@ void InventoryBox::init() {
     NanEngine.resource->loadImage(inventoryBoxIconsImageName, _iconsSurface);
     
     uint numItems = 11; // TODO
-    _fullInventorySurface.create(_screenPosition.width(), _screenPosition.height() * ((numItems / 4) + 1), GraphicsManager::getInputPixelFormat());
+    _fullInventorySurface.create(_screenPosition.width(), _screenPosition.height() * ((numItems / 4) + 1), GraphicsManager::screenPixelFormat);
     Common::Rect sourceRect = _screenPosition;
     sourceRect.moveTo(0, 0);
     _drawSurface.create(_fullInventorySurface, sourceRect);
@@ -224,7 +224,7 @@ void InventoryBox::InventoryScrollbar::init() {
 
 void InventoryBox::Shades::init() {
     Common::Rect bounds = _parent->getBounds();
-    _drawSurface.create(bounds.width(), bounds.height(), GraphicsManager::getInputPixelFormat());
+    _drawSurface.create(bounds.width(), bounds.height(), GraphicsManager::screenPixelFormat);
     _screenPosition = _parent->getScreenPosition();
     _nextFrameTime = 0;
     setAnimationFrame(_curFrame);
diff --git a/engines/nancy/ui/textbox.cpp b/engines/nancy/ui/textbox.cpp
index e7f95e639a..3339653f6a 100644
--- a/engines/nancy/ui/textbox.cpp
+++ b/engines/nancy/ui/textbox.cpp
@@ -55,7 +55,7 @@ void Textbox::init() {
     chunk->seek(0x20);
     Common::Rect innerBoundingBox;
     readRect(*chunk, innerBoundingBox);
-    _fullSurface.create(innerBoundingBox.width(), innerBoundingBox.height(), GraphicsManager::getInputPixelFormat());
+    _fullSurface.create(innerBoundingBox.width(), innerBoundingBox.height(), GraphicsManager::screenPixelFormat);
     
     _scrollbarDefaultDest.x = chunk->readUint16LE();
     _scrollbarDefaultDest.y = chunk->readUint16LE();


Commit: 3910d87c826f3705ea2c7b8582044bf60e6a1a53
    https://github.com/scummvm/scummvm/commit/3910d87c826f3705ea2c7b8582044bf60e6a1a53
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Load correct first scene in The Vampire Diaries

The correct first scene is now getting read from The Vampire Diaries' boot summary.

Changed paths:
    engines/nancy/nancy.cpp


diff --git a/engines/nancy/nancy.cpp b/engines/nancy/nancy.cpp
index 49b13b9c14..9b0b57c4e9 100644
--- a/engines/nancy/nancy.cpp
+++ b/engines/nancy/nancy.cpp
@@ -433,12 +433,13 @@ void NancyEngine::readBootSummary(const IFF &boot) {
 	Common::Serializer ser(bsum, nullptr);
 	ser.setVersion(_gameDescription->gameType);
 
-	ser.skip(0xA3, kGameTypeVampire, kGameTypeNancy2);
+	ser.skip(0x71, kGameTypeVampire, kGameTypeVampire);
+	ser.skip(0xA3, kGameTypeNancy1, kGameTypeNancy2);
 	ser.syncAsUint16LE(firstSceneID);
-	ser.skip(4, kGameTypeVampire, kGameTypeNancy2);
-	ser.syncAsUint16LE(startTimeHours);
+	ser.skip(4, kGameTypeNancy1, kGameTypeNancy2);
+	ser.syncAsUint16LE(startTimeHours, kGameTypeNancy1, kGameTypeNancy2);
 	
-	ser.skip(0x80, kGameTypeVampire, kGameTypeVampire);
+	ser.skip(0xB8, kGameTypeVampire, kGameTypeVampire);
 	ser.skip(0xA6, kGameTypeNancy1, kGameTypeNancy1);
 	ser.skip(0xA0, kGameTypeNancy2, kGameTypeNancy2);
 


Commit: 6eb5be2c51de3f366add0e2cc4e485b2ce1bf7d2
    https://github.com/scummvm/scummvm/commit/6eb5be2c51de3f366add0e2cc4e485b2ce1bf7d2
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Action record reading changes

Action records are no longer expected to return how much data they've read, which will be helpful with supporting different versions in the same class. Also added a much more helpful error message on a bad read.

Changed paths:
    engines/nancy/action/actionmanager.cpp
    engines/nancy/action/actionrecord.h
    engines/nancy/action/arfactory_v1.cpp
    engines/nancy/action/leverpuzzle.cpp
    engines/nancy/action/leverpuzzle.h
    engines/nancy/action/orderingpuzzle.cpp
    engines/nancy/action/orderingpuzzle.h
    engines/nancy/action/passwordpuzzle.cpp
    engines/nancy/action/passwordpuzzle.h
    engines/nancy/action/primaryvideo.cpp
    engines/nancy/action/primaryvideo.h
    engines/nancy/action/recordtypes.cpp
    engines/nancy/action/recordtypes.h
    engines/nancy/action/rotatinglockpuzzle.cpp
    engines/nancy/action/rotatinglockpuzzle.h
    engines/nancy/action/secondarymovie.cpp
    engines/nancy/action/secondarymovie.h
    engines/nancy/action/secondaryvideo.cpp
    engines/nancy/action/secondaryvideo.h
    engines/nancy/action/sliderpuzzle.cpp
    engines/nancy/action/sliderpuzzle.h
    engines/nancy/action/staticbitmapanim.cpp
    engines/nancy/action/staticbitmapanim.h
    engines/nancy/action/telephone.cpp
    engines/nancy/action/telephone.h
    engines/nancy/graphics.cpp


diff --git a/engines/nancy/action/actionmanager.cpp b/engines/nancy/action/actionmanager.cpp
index 9707e584d4..2bbdcd28a8 100644
--- a/engines/nancy/action/actionmanager.cpp
+++ b/engines/nancy/action/actionmanager.cpp
@@ -111,7 +111,9 @@ bool ActionManager::addNewActionRecord(Common::SeekableReadStream &inputData) {
     newRecord->type = inputData.readByte(); // redundant
     newRecord->execType = (ActionRecord::ExecutionType)inputData.readByte();
 
-    uint16 localChunkSize = newRecord->readData(inputData);
+    uint16 localChunkSize = inputData.pos();
+    newRecord->readData(inputData);
+    localChunkSize = inputData.pos() - localChunkSize;
     localChunkSize += 0x32;
 
     // If the localChunkSize is less than the total data, there must be dependencies at the end of the chunk
@@ -120,7 +122,7 @@ bool ActionManager::addNewActionRecord(Common::SeekableReadStream &inputData) {
         // Each dependency is 0x0C bytes long (in v1)
         uint numDependencies = depsDataSize / 0xC;
         if (depsDataSize % 0xC) {
-            error("Invalid dependency data size!");
+            error("Action record type %s has incorrect read size", newRecord->getRecordTypeName().c_str());;
         }
 
         // Initialize the dependencies data
diff --git a/engines/nancy/action/actionrecord.h b/engines/nancy/action/actionrecord.h
index 3c88bb7a82..ad49259c99 100644
--- a/engines/nancy/action/actionrecord.h
+++ b/engines/nancy/action/actionrecord.h
@@ -93,20 +93,14 @@ public:
         itemRequired(-1) {}
     virtual ~ActionRecord() {}
 
-    virtual uint16 readData(Common::SeekableReadStream &stream) =0;
+    virtual void readData(Common::SeekableReadStream &stream) =0;
     virtual void execute() {}
     virtual void onPause(bool pause) {}
 
     virtual CursorManager::CursorType getHoverCursor() const { return CursorManager::kHotspot; }
     virtual void handleInput(NancyInput &input) {}
 
-protected:
-    // TODO this is temporary until every record type is figured out
-    uint16 readRaw(Common::SeekableReadStream &stream, uint16 bytes) {
-        stream.skip(bytes);
-        return bytes;
-    }
-    
+protected:   
     void finishExecution() {
         switch (execType) {
         case kOneShot:
diff --git a/engines/nancy/action/arfactory_v1.cpp b/engines/nancy/action/arfactory_v1.cpp
index d5afa51d40..8a34dbc676 100644
--- a/engines/nancy/action/arfactory_v1.cpp
+++ b/engines/nancy/action/arfactory_v1.cpp
@@ -64,9 +64,9 @@ ActionRecord *ActionManager::createActionRecord(uint16 type) {
     case 0x28:
         return new PlayPrimaryVideoChan0(NancySceneState.getViewport());
     case 0x29:
-        return new PlaySecondaryVideo('0', NancySceneState.getViewport());
+        return new PlaySecondaryVideo(0, NancySceneState.getViewport());
     case 0x2A:
-        return new PlaySecondaryVideo('1', NancySceneState.getViewport());
+        return new PlaySecondaryVideo(1, NancySceneState.getViewport());
     case 0x2B:
         return new PlaySecondaryMovie(NancySceneState.getViewport());
     case 0x2C:
diff --git a/engines/nancy/action/leverpuzzle.cpp b/engines/nancy/action/leverpuzzle.cpp
index f8ae2b84ad..3265f91bd8 100644
--- a/engines/nancy/action/leverpuzzle.cpp
+++ b/engines/nancy/action/leverpuzzle.cpp
@@ -39,7 +39,7 @@ void LeverPuzzle::init() {
     NanEngine.resource->loadImage(imageName, image);
 }
 
-uint16 LeverPuzzle::readData(Common::SeekableReadStream &stream) {
+void LeverPuzzle::readData(Common::SeekableReadStream &stream) {
     char buf[10];
     stream.read(buf, 10);
     imageName = buf;
@@ -85,8 +85,6 @@ uint16 LeverPuzzle::readData(Common::SeekableReadStream &stream) {
     flagOnExit.label = stream.readSint16LE();
     flagOnExit.flag = (NancyFlag)stream.readByte();
     readRect(stream, exitHotspot);
-
-    return 0x192;
 }
 
 void LeverPuzzle::execute() {
diff --git a/engines/nancy/action/leverpuzzle.h b/engines/nancy/action/leverpuzzle.h
index 5b35d9b633..8aba5c63c1 100644
--- a/engines/nancy/action/leverpuzzle.h
+++ b/engines/nancy/action/leverpuzzle.h
@@ -44,7 +44,7 @@ public:
 
     virtual void init() override;
     
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void readData(Common::SeekableReadStream &stream) override;
     virtual void execute() override;
     virtual void handleInput(NancyInput &input) override;
     virtual void onPause(bool pause) override;
diff --git a/engines/nancy/action/orderingpuzzle.cpp b/engines/nancy/action/orderingpuzzle.cpp
index e56e0e644f..e5e3197527 100644
--- a/engines/nancy/action/orderingpuzzle.cpp
+++ b/engines/nancy/action/orderingpuzzle.cpp
@@ -50,7 +50,7 @@ void OrderingPuzzle::init() {
     RenderObject::init();
 }
 
-uint16 OrderingPuzzle::readData(Common::SeekableReadStream &stream) {
+void OrderingPuzzle::readData(Common::SeekableReadStream &stream) {
     char buf[10];
 
     stream.read(buf, 10);
@@ -97,8 +97,6 @@ uint16 OrderingPuzzle::readData(Common::SeekableReadStream &stream) {
     flagOnExit.label = stream.readSint16LE();
     flagOnExit.flag = (NancyFlag)stream.readByte();
     readRect(stream, exitHotspot);
-
-    return 0x26D;
 }
 
 void OrderingPuzzle::execute() {
diff --git a/engines/nancy/action/orderingpuzzle.h b/engines/nancy/action/orderingpuzzle.h
index 40d90a8a7a..e35a88a749 100644
--- a/engines/nancy/action/orderingpuzzle.h
+++ b/engines/nancy/action/orderingpuzzle.h
@@ -45,7 +45,7 @@ public:
 
     virtual void init() override;
     
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void readData(Common::SeekableReadStream &stream) override;
     virtual void execute() override;
     virtual void handleInput(NancyInput &input) override;
     virtual void onPause(bool pause) override;
diff --git a/engines/nancy/action/passwordpuzzle.cpp b/engines/nancy/action/passwordpuzzle.cpp
index 9f8a1d4818..23600a391e 100644
--- a/engines/nancy/action/passwordpuzzle.cpp
+++ b/engines/nancy/action/passwordpuzzle.cpp
@@ -41,7 +41,7 @@ void PasswordPuzzle::init() {
     RenderObject::init();
 }
 
-uint16 PasswordPuzzle::readData(Common::SeekableReadStream &stream) {
+void PasswordPuzzle::readData(Common::SeekableReadStream &stream) {
     fontID = stream.readUint16LE();
     cursorBlinkTime = stream.readUint16LE();
     readRect(stream, nameBounds);
@@ -68,8 +68,6 @@ uint16 PasswordPuzzle::readData(Common::SeekableReadStream &stream) {
     flagOnExit.label = stream.readSint16LE();
     flagOnExit.flag = (NancyFlag)stream.readByte();
     readRect(stream, exitHotspot);
-
-    return 0xD7;
 }
 
 void PasswordPuzzle::execute() {
diff --git a/engines/nancy/action/passwordpuzzle.h b/engines/nancy/action/passwordpuzzle.h
index 8ed0132cbc..53150a75cf 100644
--- a/engines/nancy/action/passwordpuzzle.h
+++ b/engines/nancy/action/passwordpuzzle.h
@@ -47,7 +47,7 @@ public:
 
     virtual void init() override;
     
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void readData(Common::SeekableReadStream &stream) override;
     virtual void execute() override;
     virtual void handleInput(NancyInput &input) override;
     virtual void onPause(bool pause) override;
diff --git a/engines/nancy/action/primaryvideo.cpp b/engines/nancy/action/primaryvideo.cpp
index 58b8a6836f..c8edf82b11 100644
--- a/engines/nancy/action/primaryvideo.cpp
+++ b/engines/nancy/action/primaryvideo.cpp
@@ -151,8 +151,8 @@ void PlayPrimaryVideoChan0::onPause(bool pause) {
     }
 }
 
-uint16 PlayPrimaryVideoChan0::readData(Common::SeekableReadStream &stream) {
-    uint16 bytesRead = stream.pos();
+void PlayPrimaryVideoChan0::readData(Common::SeekableReadStream &stream) {
+    uint16 beginOffset = stream.pos();
 
     char name[10];
     stream.read(name, 10);
@@ -177,7 +177,7 @@ uint16 PlayPrimaryVideoChan0::readData(Common::SeekableReadStream &stream) {
     doNotPop = (NancyFlag)stream.readByte();
     sceneChange.readData(stream);
 
-    stream.seek(bytesRead + 0x69C);
+    stream.seek(beginOffset + 0x69C);
 
     uint16 numResponses = stream.readUint16LE();
     if (numResponses > 0) {
@@ -217,9 +217,6 @@ uint16 PlayPrimaryVideoChan0::readData(Common::SeekableReadStream &stream) {
             flagsStruct.flagToSet.flag.flag = (NancyFlag)stream.readByte();
         }
     }
-
-    bytesRead = stream.pos() - bytesRead;
-    return bytesRead;
 }
 
 void PlayPrimaryVideoChan0::execute() {
diff --git a/engines/nancy/action/primaryvideo.h b/engines/nancy/action/primaryvideo.h
index 3bf8be6c91..794afec13a 100644
--- a/engines/nancy/action/primaryvideo.h
+++ b/engines/nancy/action/primaryvideo.h
@@ -81,7 +81,7 @@ public:
     virtual void updateGraphics() override;
     virtual void onPause(bool pause) override;
 
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void readData(Common::SeekableReadStream &stream) override;
     virtual void execute() override;
     
     // Functions for handling the built-in dialogue responses found in the executable
diff --git a/engines/nancy/action/recordtypes.cpp b/engines/nancy/action/recordtypes.cpp
index fa35920dca..e431f839af 100644
--- a/engines/nancy/action/recordtypes.cpp
+++ b/engines/nancy/action/recordtypes.cpp
@@ -40,9 +40,8 @@
 namespace Nancy {
 namespace Action {
 
-uint16 SceneChange::readData(Common::SeekableReadStream &stream) {
+void SceneChange::readData(Common::SeekableReadStream &stream) {
     sceneChange.readData(stream);
-    return 8;
 }
 
 void SceneChange::execute() {
@@ -50,8 +49,8 @@ void SceneChange::execute() {
     isDone = true;
 }
 
-uint16 HotMultiframeSceneChange::readData(Common::SeekableReadStream &stream) {
-    uint16 ret = SceneChange::readData(stream);
+void HotMultiframeSceneChange::readData(Common::SeekableReadStream &stream) {
+    SceneChange::readData(stream);
     uint16 numHotspots = stream.readUint16LE();
 
     for (uint i = 0; i < numHotspots; ++i) {
@@ -59,8 +58,6 @@ uint16 HotMultiframeSceneChange::readData(Common::SeekableReadStream &stream) {
         HotspotDescription &newDesc = hotspots[i];
         newDesc.readData(stream);
     }
-
-    return ret + (numHotspots * 0x12) + 2;
 }
 
 void HotMultiframeSceneChange::execute() {
@@ -84,10 +81,9 @@ void HotMultiframeSceneChange::execute() {
     }
 }
 
-uint16 Hot1FrSceneChange::readData(Common::SeekableReadStream &stream) {
+void Hot1FrSceneChange::readData(Common::SeekableReadStream &stream) {
     SceneChange::readData(stream);
     hotspotDesc.readData(stream);
-    return 0x1A;
 }
 
 void Hot1FrSceneChange::execute() {
@@ -109,26 +105,22 @@ void Hot1FrSceneChange::execute() {
     }
 }
 
-uint16 HotMultiframeMultisceneChange::readData(Common::SeekableReadStream &stream) {
+void HotMultiframeMultisceneChange::readData(Common::SeekableReadStream &stream) {
     stream.seek(0x14, SEEK_CUR);
-    uint size = stream.readUint16LE() * 0x12 + 0x16;
-    stream.seek(-0x16, SEEK_CUR);
-
-    return readRaw(stream, size); // TODO
+    uint size = stream.readUint16LE() * 0x12;
+    stream.skip(size);
 }
 
-uint16 StartFrameNextScene::readData(Common::SeekableReadStream &stream) {
-    return readRaw(stream, 0x4); // TODO
+void StartFrameNextScene::readData(Common::SeekableReadStream &stream) {
+    stream.skip(4);
 }
 
-uint16 StartStopPlayerScrolling::readData(Common::SeekableReadStream &stream) {
-    type = stream.readByte();    
-    return 1;
+void StartStopPlayerScrolling::readData(Common::SeekableReadStream &stream) {
+    stream.skip(1);
 }
 
-uint16 MapCall::readData(Common::SeekableReadStream &stream) {
+void MapCall::readData(Common::SeekableReadStream &stream) {
     stream.skip(1);
-    return 1;
 }
 
 void MapCall::execute() {
@@ -137,9 +129,8 @@ void MapCall::execute() {
     finishExecution();
 }
 
-uint16 MapCallHot1Fr::readData(Common::SeekableReadStream &stream) {
-    hotspotDesc.readData(stream);
-    return 0x12;
+void MapCallHot1Fr::readData(Common::SeekableReadStream &stream) {
+    stream.skip(0x12);
 }
 
 void MapCallHot1Fr::execute() {
@@ -159,14 +150,12 @@ void MapCallHot1Fr::execute() {
     }
 }
 
-uint16 MapCallHotMultiframe::readData(Common::SeekableReadStream &stream) {
+void MapCallHotMultiframe::readData(Common::SeekableReadStream &stream) {
     uint16 numDescs = stream.readUint16LE();
     for (uint i = 0; i < numDescs; ++i) {
         hotspots.push_back(HotspotDescription());
         hotspots[i].readData(stream);
     }
-
-    return 2 + numDescs * 0x12;
 }
 
 void MapCallHotMultiframe::execute() {
@@ -189,61 +178,53 @@ void MapCallHotMultiframe::execute() {
     }
 }
 
-uint16 MapLocationAccess::readData(Common::SeekableReadStream &stream) {
-    return readRaw(stream, 0x4); // TODO
+void MapLocationAccess::readData(Common::SeekableReadStream &stream) {
+    stream.skip(4);
 }
 
-uint16 MapSound::readData(Common::SeekableReadStream &stream) {
-    return readRaw(stream, 0x10); // TODO
+void MapSound::readData(Common::SeekableReadStream &stream) {
+    stream.skip(0x10);
 }
 
-uint16 MapAviOverride::readData(Common::SeekableReadStream &stream) {
-    return readRaw(stream, 0x2); // TODO
+void MapAviOverride::readData(Common::SeekableReadStream &stream) {
+    stream.skip(2);
 }
 
-uint16 MapAviOverrideOff::readData(Common::SeekableReadStream &stream) {
-    overrideOffData = stream.readByte();
-    return 1;
+void MapAviOverrideOff::readData(Common::SeekableReadStream &stream) {
+    stream.skip(1);
 }
 
-uint16 TextBoxWrite::readData(Common::SeekableReadStream &stream) {
+void TextBoxWrite::readData(Common::SeekableReadStream &stream) {
     uint16 size = stream.readUint16LE();
-    stream.seek(-2, SEEK_CUR);
+    stream.skip(size);
 
-    if (size > 0x2710) {
+    if (size > 10000) {
         error("Action Record atTextboxWrite has too many text box chars: %d", size);;
     }
-
-    return readRaw(stream, size+2); // TODO
 }
 
-uint16 TextBoxClear::readData(Common::SeekableReadStream &stream) {
-    clearData = stream.readByte();
-    return 1;
+void TextBoxClear::readData(Common::SeekableReadStream &stream) {
+    stream.skip(1);
 }
 
-uint16 BumpPlayerClock::readData(Common::SeekableReadStream &stream) {
-    return readRaw(stream, 0x5); // TODO
+void BumpPlayerClock::readData(Common::SeekableReadStream &stream) {
+    stream.skip(5);
 }
 
-uint16 SaveContinueGame::readData(Common::SeekableReadStream &stream) {
-    saveContinueData = stream.readByte();
-    return 1;
+void SaveContinueGame::readData(Common::SeekableReadStream &stream) {
+    stream.skip(1);
 }
 
-uint16 TurnOffMainRendering::readData(Common::SeekableReadStream &stream) {
-    turnOffData = stream.readByte();
-    return 1;
+void TurnOffMainRendering::readData(Common::SeekableReadStream &stream) {
+    stream.skip(1);
 }
 
-uint16 TurnOnMainRendering::readData(Common::SeekableReadStream &stream) {
-    turnOnData = stream.readByte();
-    return 1;
+void TurnOnMainRendering::readData(Common::SeekableReadStream &stream) {
+    stream.skip(1);
 }
 
-uint16 ResetAndStartTimer::readData(Common::SeekableReadStream &stream) {
+void ResetAndStartTimer::readData(Common::SeekableReadStream &stream) {
     stream.skip(1);
-    return 1;
 }
 
 void ResetAndStartTimer::execute() {
@@ -251,9 +232,8 @@ void ResetAndStartTimer::execute() {
     isDone = true;
 }
 
-uint16 StopTimer::readData(Common::SeekableReadStream &stream) {
+void StopTimer::readData(Common::SeekableReadStream &stream) {
     stream.skip(1);
-    return 1;
 }
 
 void StopTimer::execute() {
@@ -261,9 +241,8 @@ void StopTimer::execute() {
     isDone = true;
 }
 
-uint16 EventFlags::readData(Common::SeekableReadStream &stream) {
+void EventFlags::readData(Common::SeekableReadStream &stream) {
     flags.readData(stream);
-    return 0x28;
 }
 
 void EventFlags::execute() {
@@ -271,8 +250,8 @@ void EventFlags::execute() {
     isDone = true;
 }
 
-uint16 EventFlagsMultiHS::readData(Common::SeekableReadStream &stream) {
-    uint16 returnSize = EventFlags::readData(stream);
+void EventFlagsMultiHS::readData(Common::SeekableReadStream &stream) {
+    EventFlags::readData(stream);
     uint16 numHotspots = stream.readUint16LE();
 
     for (uint16 i = 0; i < numHotspots; ++i) {
@@ -280,10 +259,6 @@ uint16 EventFlagsMultiHS::readData(Common::SeekableReadStream &stream) {
         HotspotDescription &newDesc = hotspots[i];
         newDesc.readData(stream);
     }
-
-    returnSize += numHotspots * 0x12 + 0x2;
-
-    return returnSize;
 }
 
 void EventFlagsMultiHS::execute() {
@@ -311,9 +286,8 @@ void EventFlagsMultiHS::execute() {
     }
 }
 
-uint16 LoseGame::readData(Common::SeekableReadStream &stream) {
+void LoseGame::readData(Common::SeekableReadStream &stream) {
     stream.skip(1);
-    return 1;
 }
 
 void LoseGame::execute() {
@@ -323,19 +297,16 @@ void LoseGame::execute() {
     isDone = true;
 }
 
-uint16 PushScene::readData(Common::SeekableReadStream &stream) {
-    pushData = stream.readByte();
-    return 1;
+void PushScene::readData(Common::SeekableReadStream &stream) {
+    stream.skip(1);
 }
 
-uint16 PopScene::readData(Common::SeekableReadStream &stream) {
-    popData = stream.readByte();
-    return 1;
+void PopScene::readData(Common::SeekableReadStream &stream) {
+    stream.skip(1);
 }
 
-uint16 WinGame::readData(Common::SeekableReadStream &stream) {
+void WinGame::readData(Common::SeekableReadStream &stream) {
     stream.skip(1);
-    return 1;
 }
 
 void WinGame::execute() {
@@ -347,9 +318,8 @@ void WinGame::execute() {
     isDone = true;
 }
 
-uint16 AddInventoryNoHS::readData(Common::SeekableReadStream &stream) {
+void AddInventoryNoHS::readData(Common::SeekableReadStream &stream) {
     itemID = stream.readUint16LE();
-    return 2;
 }
 
 void AddInventoryNoHS::execute() {
@@ -360,15 +330,14 @@ void AddInventoryNoHS::execute() {
     isDone = true;
 }
 
-uint16 RemoveInventoryNoHS::readData(Common::SeekableReadStream &stream) {
-    return readRaw(stream, 0x2); // TODO
+void RemoveInventoryNoHS::readData(Common::SeekableReadStream &stream) {
+    stream.skip(2);
 }
 
-uint16 DifficultyLevel::readData(Common::SeekableReadStream &stream) {
+void DifficultyLevel::readData(Common::SeekableReadStream &stream) {
     difficulty = stream.readUint16LE();
     flag.label = stream.readSint16LE();
     flag.flag = (NancyFlag)stream.readUint16LE();
-    return 6;
 }
 
 void DifficultyLevel::execute() {
@@ -385,7 +354,7 @@ void ShowInventoryItem::init() {
     RenderObject::init();
 }
 
-uint16 ShowInventoryItem::readData(Common::SeekableReadStream &stream) {
+void ShowInventoryItem::readData(Common::SeekableReadStream &stream) {
     objectID = stream.readUint16LE();
     char name[10];
     stream.read(name, 10);
@@ -397,8 +366,6 @@ uint16 ShowInventoryItem::readData(Common::SeekableReadStream &stream) {
         bitmaps.push_back(BitmapDescription());
         bitmaps[i].readData(stream);
     }
-
-    return 0xE + 0x22 * numFrames;
 }
 
 void ShowInventoryItem::execute() {
@@ -451,13 +418,12 @@ void ShowInventoryItem::onPause(bool pause) {
     }
 }
 
-uint16 PlayDigiSoundAndDie::readData(Common::SeekableReadStream &stream) {
+void PlayDigiSoundAndDie::readData(Common::SeekableReadStream &stream) {
     sound.read(stream, SoundDescription::kDIGI);
     sceneChange.readData(stream);
     flagOnTrigger.label = stream.readSint16LE();
     flagOnTrigger.flag = (NancyFlag)stream.readByte();
-    stream.skip(1);
-    return 0x2B;
+    stream.skip(2);
 }
 
 void PlayDigiSoundAndDie::execute() {
@@ -486,11 +452,11 @@ void PlayDigiSoundAndDie::execute() {
     }
 }
 
-uint16 PlaySoundPanFrameAnchorAndDie::readData(Common::SeekableReadStream &stream) {
-    return readRaw(stream, 0x20); // TODO
+void PlaySoundPanFrameAnchorAndDie::readData(Common::SeekableReadStream &stream) {
+    stream.skip(0x20);
 }
 
-uint16 PlaySoundMultiHS::readData(Common::SeekableReadStream &stream) {
+void PlaySoundMultiHS::readData(Common::SeekableReadStream &stream) {
     sound.read(stream, SoundDescription::kNormal);
     sceneChange.readData(stream);
     flag.label = stream.readSint16LE();
@@ -503,8 +469,6 @@ uint16 PlaySoundMultiHS::readData(Common::SeekableReadStream &stream) {
         hotspots.back().frameID = stream.readUint16LE();
         readRect(stream, hotspots.back().coords);
     }
-
-    return 0x31 + numHotspots * 0x12;
 }
 
 void PlaySoundMultiHS::execute() {
@@ -536,10 +500,9 @@ void PlaySoundMultiHS::execute() {
     }
 }
 
-uint16 HintSystem::readData(Common::SeekableReadStream &stream) {
+void HintSystem::readData(Common::SeekableReadStream &stream) {
     characterID = stream.readByte();
     genericSound.read(stream, SoundDescription::kNormal);
-    return 0x23;
 }
 
 void HintSystem::execute() {
diff --git a/engines/nancy/action/recordtypes.h b/engines/nancy/action/recordtypes.h
index 8b8bff60ed..980c5fd577 100644
--- a/engines/nancy/action/recordtypes.h
+++ b/engines/nancy/action/recordtypes.h
@@ -41,7 +41,7 @@ namespace Action {
 
 class SceneChange : public ActionRecord {
 public:
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void readData(Common::SeekableReadStream &stream) override;
     virtual void execute() override;
 
     SceneChangeDescription sceneChange;
@@ -52,7 +52,7 @@ protected:
 
 class HotMultiframeSceneChange : public SceneChange {
 public:
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void readData(Common::SeekableReadStream &stream) override;
     virtual void execute() override;
 
     Common::Array<HotspotDescription> hotspots;
@@ -63,7 +63,7 @@ protected:
 
 class Hot1FrSceneChange : public SceneChange {
 public:
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void readData(Common::SeekableReadStream &stream) override;
     virtual void execute() override;
 
     HotspotDescription hotspotDesc;
@@ -81,7 +81,7 @@ protected:
 
 class HotMultiframeMultisceneChange : public ActionRecord {
 public:
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void readData(Common::SeekableReadStream &stream) override;
 
 protected:
     virtual Common::String getRecordTypeName() const override { return "HotMultiframeMultisceneChange"; }
@@ -89,7 +89,7 @@ protected:
 
 class StartFrameNextScene : public ActionRecord {
 public:
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void readData(Common::SeekableReadStream &stream) override;
 
 protected:
     virtual Common::String getRecordTypeName() const override { return "StartFrameNextScene"; }
@@ -97,7 +97,7 @@ protected:
 
 class StartStopPlayerScrolling : public ActionRecord {
 public:
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void readData(Common::SeekableReadStream &stream) override;
     // TODO add a Start and Stop subclass
 
     byte type = 0;
@@ -108,7 +108,7 @@ protected:
 
 class MapCall : public ActionRecord {
 public:
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void readData(Common::SeekableReadStream &stream) override;
     virtual void execute() override;
 
     virtual CursorManager::CursorType getHoverCursor() const override { return CursorManager::kExitArrow; }
@@ -119,7 +119,7 @@ protected:
 
 class MapCallHot1Fr : public MapCall {
 public:
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void readData(Common::SeekableReadStream &stream) override;
     virtual void execute() override;
 
     HotspotDescription hotspotDesc;
@@ -130,7 +130,7 @@ protected:
 
 class MapCallHotMultiframe : public MapCall {
 public:
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void readData(Common::SeekableReadStream &stream) override;
     virtual void execute() override;
 
     Common::Array<HotspotDescription> hotspots;
@@ -141,7 +141,7 @@ protected:
 
 class MapLocationAccess : public ActionRecord {
 public:
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void readData(Common::SeekableReadStream &stream) override;
     
 protected:
     virtual Common::String getRecordTypeName() const override { return "MapLocationAccess"; }
@@ -149,7 +149,7 @@ protected:
 
 class MapSound : public ActionRecord {
 public:
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void readData(Common::SeekableReadStream &stream) override;
     
 protected:
     virtual Common::String getRecordTypeName() const override { return "MapSound"; }
@@ -157,7 +157,7 @@ protected:
 
 class MapAviOverride : public ActionRecord {
 public:
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void readData(Common::SeekableReadStream &stream) override;
     
 protected:
     virtual Common::String getRecordTypeName() const override { return "MapAviOverride"; }
@@ -165,9 +165,7 @@ protected:
 
 class MapAviOverrideOff : public ActionRecord {
 public:
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
-
-    byte overrideOffData = 0;
+    virtual void readData(Common::SeekableReadStream &stream) override;
     
 protected:
     virtual Common::String getRecordTypeName() const override { return "MapAviOverrideOff"; }
@@ -175,7 +173,7 @@ protected:
 
 class TextBoxWrite : public ActionRecord {
 public:
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void readData(Common::SeekableReadStream &stream) override;
     
 protected:
     virtual Common::String getRecordTypeName() const override { return "TextBoxWrite"; }
@@ -183,9 +181,7 @@ protected:
 
 class TextBoxClear : public ActionRecord {
 public:
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
-
-    byte clearData = 0;
+    virtual void readData(Common::SeekableReadStream &stream) override;
     
 protected:
     virtual Common::String getRecordTypeName() const override { return "TextBoxClear"; }
@@ -193,7 +189,7 @@ protected:
 
 class BumpPlayerClock : public ActionRecord {
 public:
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void readData(Common::SeekableReadStream &stream) override;
     
 protected:
     virtual Common::String getRecordTypeName() const override { return "BumpPlayerClock"; }
@@ -201,9 +197,7 @@ protected:
 
 class SaveContinueGame : public ActionRecord {
 public:
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
-
-    byte saveContinueData = 0;
+    virtual void readData(Common::SeekableReadStream &stream) override;
     
 protected:
     virtual Common::String getRecordTypeName() const override { return "SaveContinueGame"; }
@@ -211,9 +205,7 @@ protected:
 
 class TurnOffMainRendering : public ActionRecord {
 public:
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
-
-    byte turnOffData = 0;
+    virtual void readData(Common::SeekableReadStream &stream) override;
     
 protected:
     virtual Common::String getRecordTypeName() const override { return "TurnOffMainRendering"; }
@@ -221,9 +213,7 @@ protected:
 
 class TurnOnMainRendering : public ActionRecord {
 public:
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
-
-    byte turnOnData = 0;
+    virtual void readData(Common::SeekableReadStream &stream) override;
     
 protected:
     virtual Common::String getRecordTypeName() const override { return "TurnOnMainRendering"; }
@@ -231,7 +221,7 @@ protected:
 
 class ResetAndStartTimer : public ActionRecord {
 public:
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void readData(Common::SeekableReadStream &stream) override;
     virtual void execute() override;
     
 protected:
@@ -240,7 +230,7 @@ protected:
 
 class StopTimer : public ActionRecord {
 public:
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void readData(Common::SeekableReadStream &stream) override;
     virtual void execute() override;
     
 protected:
@@ -249,7 +239,7 @@ protected:
 
 class EventFlags : public ActionRecord {
 public:
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void readData(Common::SeekableReadStream &stream) override;
     virtual void execute() override;
 
     MultiEventFlagDescription flags;
@@ -260,7 +250,7 @@ protected:
 
 class EventFlagsMultiHS : public EventFlags {
 public:
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void readData(Common::SeekableReadStream &stream) override;
     virtual void execute() override;
 
     Common::Array<HotspotDescription> hotspots;
@@ -271,7 +261,7 @@ protected:
 
 class LoseGame : public ActionRecord {
 public:
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void readData(Common::SeekableReadStream &stream) override;
     virtual void execute() override;
     
 protected:
@@ -280,9 +270,7 @@ protected:
 
 class PushScene : public ActionRecord {
 public:
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
-
-    byte pushData = 0;
+    virtual void readData(Common::SeekableReadStream &stream) override;
     
 protected:
     virtual Common::String getRecordTypeName() const override { return "PushScene"; }
@@ -290,9 +278,7 @@ protected:
 
 class PopScene : public ActionRecord {
 public:
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
-
-    byte popData = 0;
+    virtual void readData(Common::SeekableReadStream &stream) override;
     
 protected:
     virtual Common::String getRecordTypeName() const override { return "PopScene"; }
@@ -300,7 +286,7 @@ protected:
 
 class WinGame : public ActionRecord {
 public:
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void readData(Common::SeekableReadStream &stream) override;
     virtual void execute() override;
     
 protected:
@@ -309,7 +295,7 @@ protected:
 
 class AddInventoryNoHS : public ActionRecord {
 public:
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void readData(Common::SeekableReadStream &stream) override;
     virtual void execute() override;
     
     uint itemID;
@@ -320,7 +306,7 @@ protected:
 
 class RemoveInventoryNoHS : public ActionRecord {
 public:
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void readData(Common::SeekableReadStream &stream) override;
     
 protected:
     virtual Common::String getRecordTypeName() const override { return "RemoveInventoryNoHS"; }
@@ -328,7 +314,7 @@ protected:
 
 class DifficultyLevel : public ActionRecord {
 public:
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void readData(Common::SeekableReadStream &stream) override;
     virtual void execute() override;
 
     uint16 difficulty = 0;
@@ -340,7 +326,7 @@ protected:
 
 class ShowInventoryItem : public ActionRecord, public RenderObject {
 public:
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void readData(Common::SeekableReadStream &stream) override;
     virtual void execute() override;
 
     ShowInventoryItem(RenderObject &redrawFrom) : RenderObject(redrawFrom) {}
@@ -366,7 +352,7 @@ protected:
 
 class PlayDigiSoundAndDie : public ActionRecord {
 public:
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void readData(Common::SeekableReadStream &stream) override;
     virtual void execute() override;
     // TODO subclass into Play and Stop (?)
 
@@ -380,7 +366,7 @@ protected:
 
 class PlaySoundPanFrameAnchorAndDie : public ActionRecord {
 public:
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void readData(Common::SeekableReadStream &stream) override;
     
 protected:
     virtual Common::String getRecordTypeName() const override { return "PlaySoundPanFrameAnchorAndDie"; }
@@ -388,7 +374,7 @@ protected:
 
 class PlaySoundMultiHS : public ActionRecord {
 public:
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void readData(Common::SeekableReadStream &stream) override;
     virtual void execute() override;
 
     SoundDescription sound; // 0x0
@@ -402,7 +388,7 @@ protected:
 
 class HintSystem : public ActionRecord {
 public:
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void readData(Common::SeekableReadStream &stream) override;
     virtual void execute() override;
 
     byte characterID; // 0x00
diff --git a/engines/nancy/action/rotatinglockpuzzle.cpp b/engines/nancy/action/rotatinglockpuzzle.cpp
index 3befb05c90..8d93949c7a 100644
--- a/engines/nancy/action/rotatinglockpuzzle.cpp
+++ b/engines/nancy/action/rotatinglockpuzzle.cpp
@@ -41,7 +41,7 @@ void RotatingLockPuzzle::init() {
     NanEngine.resource->loadImage(imageName, image);
 }
 
-uint16 RotatingLockPuzzle::readData(Common::SeekableReadStream &stream) {
+void RotatingLockPuzzle::readData(Common::SeekableReadStream &stream) {
     char buf[10];
     stream.read(buf, 10);
     imageName = buf;
@@ -98,8 +98,6 @@ uint16 RotatingLockPuzzle::readData(Common::SeekableReadStream &stream) {
     flagOnExit.label = stream.readSint16LE();
     flagOnExit.flag = (NancyFlag)stream.readByte();
     readRect(stream, exitHotspot);
-
-    return 0x2A4;
 }
 
 void RotatingLockPuzzle::execute() {
diff --git a/engines/nancy/action/rotatinglockpuzzle.h b/engines/nancy/action/rotatinglockpuzzle.h
index 0633cc8b0b..24e49fa7f6 100644
--- a/engines/nancy/action/rotatinglockpuzzle.h
+++ b/engines/nancy/action/rotatinglockpuzzle.h
@@ -44,7 +44,7 @@ public:
     
     virtual void init() override;
     
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void readData(Common::SeekableReadStream &stream) override;
     virtual void execute() override;
     virtual void handleInput(NancyInput &input) override;
     virtual void onPause(bool pause) override;
diff --git a/engines/nancy/action/secondarymovie.cpp b/engines/nancy/action/secondarymovie.cpp
index 8ef6988ed7..92d6abff7a 100644
--- a/engines/nancy/action/secondarymovie.cpp
+++ b/engines/nancy/action/secondarymovie.cpp
@@ -37,7 +37,7 @@ PlaySecondaryMovie::~PlaySecondaryMovie() {
     }
 }
 
-uint16 PlaySecondaryMovie::readData(Common::SeekableReadStream &stream) {  
+void PlaySecondaryMovie::readData(Common::SeekableReadStream &stream) {  
     char name[10];
     stream.read(name, 10);
     videoName = name;
@@ -64,8 +64,6 @@ uint16 PlaySecondaryMovie::readData(Common::SeekableReadStream &stream) {
         videoDescs.push_back(SecondaryVideoDescription());
         videoDescs[i].readData(stream);
     }
-
-    return 0xD4 + numVideoDescs * 0x42; // TODO
 }
 
 void PlaySecondaryMovie::init() {
diff --git a/engines/nancy/action/secondarymovie.h b/engines/nancy/action/secondarymovie.h
index 8155e17a2c..8a760d5b16 100644
--- a/engines/nancy/action/secondarymovie.h
+++ b/engines/nancy/action/secondarymovie.h
@@ -52,7 +52,7 @@ public:
     virtual void updateGraphics() override;
     virtual void onPause(bool pause) override;
 
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void readData(Common::SeekableReadStream &stream) override;
     virtual void execute() override;
 
     Common::String videoName; // 0x00
diff --git a/engines/nancy/action/secondaryvideo.cpp b/engines/nancy/action/secondaryvideo.cpp
index 9c5beff30d..bd285be696 100644
--- a/engines/nancy/action/secondaryvideo.cpp
+++ b/engines/nancy/action/secondaryvideo.cpp
@@ -140,7 +140,7 @@ void PlaySecondaryVideo::handleInput(NancyInput &input) {
     }
 }
 
-uint16 PlaySecondaryVideo::readData(Common::SeekableReadStream &stream) {
+void PlaySecondaryVideo::readData(Common::SeekableReadStream &stream) {
     char buf[10];
     stream.read(buf, 10);
     filename = buf;
@@ -159,8 +159,6 @@ uint16 PlaySecondaryVideo::readData(Common::SeekableReadStream &stream) {
         videoDescs.push_back(SecondaryVideoDescription());
         videoDescs[i].readData(stream);
     }
-
-    return 0x35 + (numVideoDescs * 0x42);
 }
 
 void PlaySecondaryVideo::execute() {
diff --git a/engines/nancy/action/secondaryvideo.h b/engines/nancy/action/secondaryvideo.h
index 690634d1fe..5fa73480ec 100644
--- a/engines/nancy/action/secondaryvideo.h
+++ b/engines/nancy/action/secondaryvideo.h
@@ -41,7 +41,7 @@ class PlaySecondaryVideo : public ActionRecord, public RenderObject {
 public:
     enum HoverState { kNoHover, kHover, kEndHover };
 
-    PlaySecondaryVideo(char chan, RenderObject &redrawFrom) : RenderObject(redrawFrom), channel(chan) {}
+    PlaySecondaryVideo(uint chan, RenderObject &redrawFrom) : RenderObject(redrawFrom), channel(chan) {}
     virtual ~PlaySecondaryVideo() { _decoder.close(); }
 
     virtual void init() override;
@@ -49,7 +49,7 @@ public:
     virtual void onPause(bool pause) override;
     virtual void handleInput(NancyInput &input) override;
 
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void readData(Common::SeekableReadStream &stream) override;
     virtual void execute() override;
 
     Common::String filename;
@@ -65,7 +65,7 @@ public:
     Common::Array<SecondaryVideoDescription> videoDescs; // 0x35
 
 protected:
-    virtual Common::String getRecordTypeName() const override { return Common::String("PlaySecondaryVideoChan" + channel); }
+    virtual Common::String getRecordTypeName() const override { return Common::String::format("PlaySecondaryVideoChan%i", channel); }
 
     virtual uint16 getZOrder() const override { return 8; }
     virtual BlitType getBlitType() const override { return kTrans; }
@@ -77,7 +77,7 @@ protected:
     bool _isPlaying = false;
     bool _isHovered = false;
 
-    char channel;
+    uint channel;
 };
 
 } // End of namespace Action
diff --git a/engines/nancy/action/sliderpuzzle.cpp b/engines/nancy/action/sliderpuzzle.cpp
index bd1c2348b1..31e6ef9ca4 100644
--- a/engines/nancy/action/sliderpuzzle.cpp
+++ b/engines/nancy/action/sliderpuzzle.cpp
@@ -44,7 +44,7 @@ void SliderPuzzle::init() {
     NanEngine.resource->loadImage(imageName, image);
 }
 
-uint16 SliderPuzzle::readData(Common::SeekableReadStream &stream) {
+void SliderPuzzle::readData(Common::SeekableReadStream &stream) {
     char buf[10];
     stream.read(buf, 10);
     imageName = buf;
@@ -101,8 +101,6 @@ uint16 SliderPuzzle::readData(Common::SeekableReadStream &stream) {
     flagOnExit.label = stream.readSint16LE();
     flagOnExit.flag = (NancyFlag)stream.readByte();
     readRect(stream, exitHotspot);
-
-    return 0x544;
 }
 
 void SliderPuzzle::execute() {
diff --git a/engines/nancy/action/sliderpuzzle.h b/engines/nancy/action/sliderpuzzle.h
index 0105fbc66e..b941754932 100644
--- a/engines/nancy/action/sliderpuzzle.h
+++ b/engines/nancy/action/sliderpuzzle.h
@@ -49,7 +49,7 @@ public:
     
     virtual void init() override;
     
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void readData(Common::SeekableReadStream &stream) override;
     virtual void execute() override;
     virtual void handleInput(NancyInput &input) override;
     virtual void onPause(bool pause) override;
diff --git a/engines/nancy/action/staticbitmapanim.cpp b/engines/nancy/action/staticbitmapanim.cpp
index cf6c54f416..9a7de966dc 100644
--- a/engines/nancy/action/staticbitmapanim.cpp
+++ b/engines/nancy/action/staticbitmapanim.cpp
@@ -42,7 +42,7 @@ void PlayStaticBitmapAnimation::init() {
     RenderObject::init();
 }
 
-uint16 PlayStaticBitmapAnimation::readData(Common::SeekableReadStream &stream) {
+void PlayStaticBitmapAnimation::readData(Common::SeekableReadStream &stream) {
     char name[10];
     stream.read(name, 10);
     imageName = Common::String(name);
@@ -57,6 +57,7 @@ uint16 PlayStaticBitmapAnimation::readData(Common::SeekableReadStream &stream) {
     loopLastFrame = stream.readUint16LE();
     frameTime = Common::Rational(1000, stream.readUint16LE()).toInt();
     zOrder = stream.readUint16LE();
+
     if (isInterruptible) {
         interruptCondition.label = stream.readSint16LE();
         interruptCondition.flag = (NancyFlag)stream.readUint16LE();
@@ -64,6 +65,7 @@ uint16 PlayStaticBitmapAnimation::readData(Common::SeekableReadStream &stream) {
         interruptCondition.label = -1;
         interruptCondition.flag = kFalse;
     }
+
     sceneChange.readData(stream);
     triggerFlags.readData(stream);
     sound.read(stream, SoundDescription::kNormal);
@@ -81,10 +83,6 @@ uint16 PlayStaticBitmapAnimation::readData(Common::SeekableReadStream &stream) {
         readRect(stream, rects.src);
         readRect(stream, rects.dest);
     }
-
-    uint baseSize = isInterruptible ? 0x76 : 0x72;
-
-    return baseSize + numViewportFrames * 0x22 + (loopLastFrame - firstFrame + 1) * 16;
 }
 
 void PlayStaticBitmapAnimation::execute() {
diff --git a/engines/nancy/action/staticbitmapanim.h b/engines/nancy/action/staticbitmapanim.h
index ab222d65c9..da0535abe7 100644
--- a/engines/nancy/action/staticbitmapanim.h
+++ b/engines/nancy/action/staticbitmapanim.h
@@ -45,7 +45,7 @@ public:
 
     virtual void init() override;
 
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void readData(Common::SeekableReadStream &stream) override;
     virtual void execute() override;
     virtual void onPause(bool pause) override;
 
diff --git a/engines/nancy/action/telephone.cpp b/engines/nancy/action/telephone.cpp
index 92ad6b81e5..42f42b4cc0 100644
--- a/engines/nancy/action/telephone.cpp
+++ b/engines/nancy/action/telephone.cpp
@@ -45,7 +45,7 @@ void Telephone::init() {
     NancySceneState.setShouldClearTextbox(false);
 }
 
-uint16 Telephone::readData(Common::SeekableReadStream &stream) {
+void Telephone::readData(Common::SeekableReadStream &stream) {
     char buf[10];
     stream.read(buf, 10);
     imageName = buf;
@@ -112,8 +112,6 @@ uint16 Telephone::readData(Common::SeekableReadStream &stream) {
         call.flag.label = stream.readSint16LE();
         call.flag.flag = (NancyFlag)stream.readUint16LE();
     }
-
-    return numCalls * 0xEB + 0x48C;
 }
 
 void Telephone::execute() {
diff --git a/engines/nancy/action/telephone.h b/engines/nancy/action/telephone.h
index a0223c3a85..4e7ae39c3a 100644
--- a/engines/nancy/action/telephone.h
+++ b/engines/nancy/action/telephone.h
@@ -58,7 +58,7 @@ public:
 
     virtual void init() override;
 
-    virtual uint16 readData(Common::SeekableReadStream &stream) override;
+    virtual void readData(Common::SeekableReadStream &stream) override;
     virtual void execute() override;
     virtual void handleInput(NancyInput &input) override;
 
diff --git a/engines/nancy/graphics.cpp b/engines/nancy/graphics.cpp
index 014e31921f..78deb41117 100644
--- a/engines/nancy/graphics.cpp
+++ b/engines/nancy/graphics.cpp
@@ -49,13 +49,6 @@ void GraphicsManager::init() {
     _screen.create(640, 480, screenPixelFormat);
     _screen.setTransparentColor(getTransColor());
 
-    auto formats = NanEngine._system->getSupportedFormats();
-
-    for (auto f : formats) {
-        debug(f.toString().c_str());
-
-    }
-
     Common::SeekableReadStream *ob = NanEngine.getBootChunkStream("OB0");
     ob->seek(0);
 


Commit: 45e5064effbd753f847f52345ec6525b04d61d62
    https://github.com/scummvm/scummvm/commit/45e5064effbd753f847f52345ec6525b04d61d62
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Correctly load early AVF type frames

AVF videos with chunk format 1 do not contain the frame size in bytes like previously assumed. The data that I previously thought was the size is now simply skipped, and the size itself gets calculated on the fly.

Changed paths:
    engines/nancy/video.cpp


diff --git a/engines/nancy/video.cpp b/engines/nancy/video.cpp
index 58db17a84d..0c97dcff6e 100644
--- a/engines/nancy/video.cpp
+++ b/engines/nancy/video.cpp
@@ -121,14 +121,12 @@ AVFDecoder::AVFVideoTrack::AVFVideoTrack(Common::SeekableReadStream *stream, uin
 			char buf[13];
 			stream->read(buf, 13);
 			info.name = buf;
-			info.size = stream->readUint32LE();
-
-			if (info.size == 0) {
-				info.size = _frameSize;
-			}
+			
+			stream->skip(4); // unknown
 			
 			info.offset = stream->readUint32LE();
 			info.compressedSize = stream->readUint32LE();
+			info.size = _frameSize;
 			info.type = 0;
 		} else if (formatHi == 2) {
 			info.index = stream->readUint16LE();


Commit: f24cc5ee24af8a676849dcc3da288f82f5229488
    https://github.com/scummvm/scummvm/commit/f24cc5ee24af8a676849dcc3da288f82f5229488
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add method for loading a palette from external file

Added the loadSurfacePalette() method to GraphicsManager, which loads a palette from an external .bmp file.

Changed paths:
    engines/nancy/graphics.cpp
    engines/nancy/graphics.h
    engines/nancy/ui/viewport.cpp


diff --git a/engines/nancy/graphics.cpp b/engines/nancy/graphics.cpp
index 78deb41117..c87342cc0c 100644
--- a/engines/nancy/graphics.cpp
+++ b/engines/nancy/graphics.cpp
@@ -133,6 +133,16 @@ void GraphicsManager::redrawAll() {
     }
 }
 
+void GraphicsManager::loadSurfacePalette(Graphics::ManagedSurface &inSurf, const Common::String paletteFilename) {
+    Common::File f;
+    if (f.open(paletteFilename + ".bmp")) {
+        Image::BitmapDecoder dec;
+        if (dec.loadStream(f)) {
+            inSurf.setPalette(dec.getPalette(), dec.getPaletteStartIndex(), dec.getPaletteColorCount());
+        }
+    }
+}
+
 const Graphics::PixelFormat &GraphicsManager::getInputPixelFormat() {
     if (NanEngine._gameDescription->desc.flags & NGF_8BITCOLOR) {
         return clut8Format;
diff --git a/engines/nancy/graphics.h b/engines/nancy/graphics.h
index 334a43a315..a1971cc68a 100644
--- a/engines/nancy/graphics.h
+++ b/engines/nancy/graphics.h
@@ -50,6 +50,8 @@ public:
 
     Font *getFont(uint id) { return id < _fonts.size() ? &_fonts[id] : nullptr; }
 
+    static void loadSurfacePalette(Graphics::ManagedSurface &inSurf, const Common::String paletteFilename);
+
     static const Graphics::PixelFormat &getInputPixelFormat();
     static uint getTransColor();
 
diff --git a/engines/nancy/ui/viewport.cpp b/engines/nancy/ui/viewport.cpp
index cbbd081f9d..0fd4ec17a3 100644
--- a/engines/nancy/ui/viewport.cpp
+++ b/engines/nancy/ui/viewport.cpp
@@ -29,10 +29,6 @@
 #include "engines/nancy/cursor.h"
 #include "engines/nancy/util.h"
 
-#include "common/file.h"
-
-#include "image/bmp.h"
-
 namespace Nancy {
 namespace UI {
 
@@ -194,14 +190,8 @@ void Viewport::loadVideo(const Common::String &filename, uint frameNr, uint vert
     setVerticalScroll(verticalScroll);
 
     if (palette.size()) {
-        Common::File paletteFile;
-        if (paletteFile.open(palette + ".bmp")) {
-            Image::BitmapDecoder b;
-            if (b.loadStream(paletteFile)) {
-                _drawSurface.setPalette(b.getPalette(), b.getPaletteStartIndex(), b.getPaletteColorCount() - 1);
-                _fullFrame.setPalette(b.getPalette(), b.getPaletteStartIndex(), b.getPaletteColorCount() - 1);
-            }
-        }
+        GraphicsManager::loadSurfacePalette(_drawSurface, palette);
+        GraphicsManager::loadSurfacePalette(_fullFrame, palette);
     }
 
     _movementLastFrame = 0;


Commit: 7220797ca45a611c922d60eb0e11a11bd1e57a36
    https://github.com/scummvm/scummvm/commit/7220797ca45a611c922d60eb0e11a11bd1e57a36
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Remove redundancy in transparency handling

Removed the BlitType enum and getBlitType() method from RenderObject, and replaced them with setTransparent(). This was done to reduce redundant code, since ManagedSurface can already pick between a transparent and regular blit depending on whether the provided source surface has a transparent color set. Also added the correct transparent color for The Vampire Diaries, and the transparent color for nancy1 now gets calculated on the fly.

Changed paths:
    engines/nancy/action/leverpuzzle.cpp
    engines/nancy/action/leverpuzzle.h
    engines/nancy/action/orderingpuzzle.cpp
    engines/nancy/action/orderingpuzzle.h
    engines/nancy/action/passwordpuzzle.cpp
    engines/nancy/action/passwordpuzzle.h
    engines/nancy/action/primaryvideo.h
    engines/nancy/action/recordtypes.h
    engines/nancy/action/rotatinglockpuzzle.cpp
    engines/nancy/action/rotatinglockpuzzle.h
    engines/nancy/action/secondarymovie.h
    engines/nancy/action/secondaryvideo.cpp
    engines/nancy/action/secondaryvideo.h
    engines/nancy/action/sliderpuzzle.cpp
    engines/nancy/action/sliderpuzzle.h
    engines/nancy/action/staticbitmapanim.cpp
    engines/nancy/action/staticbitmapanim.h
    engines/nancy/action/telephone.cpp
    engines/nancy/action/telephone.h
    engines/nancy/graphics.cpp
    engines/nancy/graphics.h
    engines/nancy/renderobject.cpp
    engines/nancy/renderobject.h
    engines/nancy/state/credits.cpp
    engines/nancy/state/credits.h
    engines/nancy/state/map.h
    engines/nancy/ui/button.h
    engines/nancy/ui/fullscreenimage.h
    engines/nancy/ui/inventorybox.cpp
    engines/nancy/ui/inventorybox.h
    engines/nancy/ui/scrollbar.cpp
    engines/nancy/ui/scrollbar.h
    engines/nancy/ui/textbox.cpp
    engines/nancy/ui/textbox.h
    engines/nancy/ui/viewport.h


diff --git a/engines/nancy/action/leverpuzzle.cpp b/engines/nancy/action/leverpuzzle.cpp
index 3265f91bd8..ac843ae4ab 100644
--- a/engines/nancy/action/leverpuzzle.cpp
+++ b/engines/nancy/action/leverpuzzle.cpp
@@ -35,6 +35,8 @@ namespace Action {
 void LeverPuzzle::init() {
     _drawSurface.create(_screenPosition.width(), _screenPosition.height(), GraphicsManager::getInputPixelFormat());
     _drawSurface.clear(GraphicsManager::getTransColor());
+    
+    setTransparent(true);
 
     NanEngine.resource->loadImage(imageName, image);
 }
diff --git a/engines/nancy/action/leverpuzzle.h b/engines/nancy/action/leverpuzzle.h
index 8aba5c63c1..5ee94684b1 100644
--- a/engines/nancy/action/leverpuzzle.h
+++ b/engines/nancy/action/leverpuzzle.h
@@ -73,7 +73,6 @@ protected:
     virtual Common::String getRecordTypeName() const override { return "LeverPuzzle"; }
 
     virtual uint16 getZOrder() const override { return 7; }
-    virtual BlitType getBlitType() const override { return kTrans; }
     virtual bool isViewportRelative() const override { return true; }
 
     void drawLever(uint id);
diff --git a/engines/nancy/action/orderingpuzzle.cpp b/engines/nancy/action/orderingpuzzle.cpp
index e5e3197527..5bd7f2b89d 100644
--- a/engines/nancy/action/orderingpuzzle.cpp
+++ b/engines/nancy/action/orderingpuzzle.cpp
@@ -42,6 +42,8 @@ void OrderingPuzzle::init() {
     // This is a hacky way to make this particular action record work with this implementation's graphics manager
     _drawSurface.create(_screenPosition.width(), _screenPosition.height(), GraphicsManager::getInputPixelFormat());
     clearAllElements();
+    
+    setTransparent(true);
 
     NanEngine.resource->loadImage(imageName, image);
 
diff --git a/engines/nancy/action/orderingpuzzle.h b/engines/nancy/action/orderingpuzzle.h
index e35a88a749..98d20cbf30 100644
--- a/engines/nancy/action/orderingpuzzle.h
+++ b/engines/nancy/action/orderingpuzzle.h
@@ -74,7 +74,6 @@ protected:
     virtual Common::String getRecordTypeName() const override { return "OrderingPuzzle"; }
     
     virtual uint16 getZOrder() const override { return 7; }
-    virtual BlitType getBlitType() const override { return kTrans; }
     virtual bool isViewportRelative() const override { return true; }
 
     void drawElement(uint id);
diff --git a/engines/nancy/action/passwordpuzzle.cpp b/engines/nancy/action/passwordpuzzle.cpp
index 23600a391e..b34fec836d 100644
--- a/engines/nancy/action/passwordpuzzle.cpp
+++ b/engines/nancy/action/passwordpuzzle.cpp
@@ -37,6 +37,8 @@ namespace Action {
 void PasswordPuzzle::init() {
     _drawSurface.create(_screenPosition.width(), _screenPosition.height(), GraphicsManager::getInputPixelFormat());
     _drawSurface.clear(GraphicsManager::getTransColor());
+    
+    setTransparent(true);
 
     RenderObject::init();
 }
diff --git a/engines/nancy/action/passwordpuzzle.h b/engines/nancy/action/passwordpuzzle.h
index 53150a75cf..d135cb36a4 100644
--- a/engines/nancy/action/passwordpuzzle.h
+++ b/engines/nancy/action/passwordpuzzle.h
@@ -80,7 +80,6 @@ protected:
     virtual Common::String getRecordTypeName() const override { return "PasswordPuzzle"; }
     
     virtual uint16 getZOrder() const override { return 7; }
-    virtual BlitType getBlitType() const override { return kTrans; }
     virtual bool isViewportRelative() const override { return true; }
 
     void drawText();
diff --git a/engines/nancy/action/primaryvideo.h b/engines/nancy/action/primaryvideo.h
index 794afec13a..11c7513463 100644
--- a/engines/nancy/action/primaryvideo.h
+++ b/engines/nancy/action/primaryvideo.h
@@ -117,7 +117,6 @@ protected:
     virtual Common::String getRecordTypeName() const override { return "PlayPrimaryVideoChan0"; }
     
     virtual uint16 getZOrder() const override { return 8; }
-    virtual BlitType getBlitType() const override { return kNoTrans; }
     virtual bool isViewportRelative() const override { return true; }
 };
 
diff --git a/engines/nancy/action/recordtypes.h b/engines/nancy/action/recordtypes.h
index 980c5fd577..de15567d17 100644
--- a/engines/nancy/action/recordtypes.h
+++ b/engines/nancy/action/recordtypes.h
@@ -346,7 +346,6 @@ protected:
     virtual Common::String getRecordTypeName() const override { return "ShowInventoryItem"; }
 
     virtual uint16 getZOrder() const override { return 9; }
-    virtual BlitType getBlitType() const override { return kNoTrans; }
     virtual bool isViewportRelative() const override { return true; }
 };
 
diff --git a/engines/nancy/action/rotatinglockpuzzle.cpp b/engines/nancy/action/rotatinglockpuzzle.cpp
index 8d93949c7a..7c5772741d 100644
--- a/engines/nancy/action/rotatinglockpuzzle.cpp
+++ b/engines/nancy/action/rotatinglockpuzzle.cpp
@@ -37,6 +37,8 @@ namespace Action {
 void RotatingLockPuzzle::init() {
     _drawSurface.create(_screenPosition.width(), _screenPosition.height(), GraphicsManager::getInputPixelFormat());
     _drawSurface.clear(GraphicsManager::getTransColor());
+    
+    setTransparent(true);
 
     NanEngine.resource->loadImage(imageName, image);
 }
diff --git a/engines/nancy/action/rotatinglockpuzzle.h b/engines/nancy/action/rotatinglockpuzzle.h
index 24e49fa7f6..3245424b40 100644
--- a/engines/nancy/action/rotatinglockpuzzle.h
+++ b/engines/nancy/action/rotatinglockpuzzle.h
@@ -75,7 +75,6 @@ protected:
     virtual Common::String getRecordTypeName() const override { return "RotatingLockPuzzle"; }
 
     virtual uint16 getZOrder() const override { return 7; }
-    virtual BlitType getBlitType() const override { return kTrans; }
     virtual bool isViewportRelative() const override { return true; }
 
     void drawDial(uint id);
diff --git a/engines/nancy/action/secondarymovie.h b/engines/nancy/action/secondarymovie.h
index 8a760d5b16..524ae2faa2 100644
--- a/engines/nancy/action/secondarymovie.h
+++ b/engines/nancy/action/secondarymovie.h
@@ -74,7 +74,6 @@ protected:
     virtual Common::String getRecordTypeName() const override { return "PlaySecondaryMovie"; }
 
     virtual uint16 getZOrder() const override { return 8; }
-    virtual BlitType getBlitType() const override { return kNoTrans; }
     virtual bool isViewportRelative() const override { return true; }
 
     AVFDecoder _decoder;
diff --git a/engines/nancy/action/secondaryvideo.cpp b/engines/nancy/action/secondaryvideo.cpp
index bd285be696..f2194339dc 100644
--- a/engines/nancy/action/secondaryvideo.cpp
+++ b/engines/nancy/action/secondaryvideo.cpp
@@ -53,6 +53,7 @@ void PlaySecondaryVideo::init() {
     _drawSurface.create(_decoder.getWidth(), _decoder.getHeight(), GraphicsManager::getInputPixelFormat());
 
     setVisible(false);
+    setTransparent(true);
 
     RenderObject::init();
 }
diff --git a/engines/nancy/action/secondaryvideo.h b/engines/nancy/action/secondaryvideo.h
index 5fa73480ec..389485bdf5 100644
--- a/engines/nancy/action/secondaryvideo.h
+++ b/engines/nancy/action/secondaryvideo.h
@@ -53,7 +53,7 @@ public:
     virtual void execute() override;
 
     Common::String filename;
-    //...
+    Common::String paletteFilename;
     uint16 loopFirstFrame = 0; // 0x1E
     uint16 loopLastFrame = 0; // 0x20
     uint16 onHoverFirstFrame = 0; // 0x22
@@ -68,7 +68,6 @@ protected:
     virtual Common::String getRecordTypeName() const override { return Common::String::format("PlaySecondaryVideoChan%i", channel); }
 
     virtual uint16 getZOrder() const override { return 8; }
-    virtual BlitType getBlitType() const override { return kTrans; }
     virtual bool isViewportRelative() const override { return true; }
 
     HoverState hoverState = kNoHover;
diff --git a/engines/nancy/action/sliderpuzzle.cpp b/engines/nancy/action/sliderpuzzle.cpp
index 31e6ef9ca4..304e3b55ee 100644
--- a/engines/nancy/action/sliderpuzzle.cpp
+++ b/engines/nancy/action/sliderpuzzle.cpp
@@ -40,6 +40,8 @@ bool SliderPuzzle::playerHasTriedPuzzle = false;
 void SliderPuzzle::init() {
     _drawSurface.create(_screenPosition.width(), _screenPosition.height(), GraphicsManager::getInputPixelFormat());
     _drawSurface.clear(GraphicsManager::getTransColor());
+    
+    setTransparent(true);
 
     NanEngine.resource->loadImage(imageName, image);
 }
diff --git a/engines/nancy/action/sliderpuzzle.h b/engines/nancy/action/sliderpuzzle.h
index b941754932..b33287eed6 100644
--- a/engines/nancy/action/sliderpuzzle.h
+++ b/engines/nancy/action/sliderpuzzle.h
@@ -80,7 +80,6 @@ protected:
     virtual Common::String getRecordTypeName() const override { return "SliderPuzzle"; }
 
     virtual uint16 getZOrder() const override { return 7; }
-    virtual BlitType getBlitType() const override { return kTrans; }
     virtual bool isViewportRelative() const override { return true; }
 
     void drawTile(int tileID, uint posX, uint posY);
diff --git a/engines/nancy/action/staticbitmapanim.cpp b/engines/nancy/action/staticbitmapanim.cpp
index 9a7de966dc..a06a013910 100644
--- a/engines/nancy/action/staticbitmapanim.cpp
+++ b/engines/nancy/action/staticbitmapanim.cpp
@@ -178,6 +178,9 @@ void PlayStaticBitmapAnimation::onPause(bool pause) {
 void PlayStaticBitmapAnimation::setFrame(uint frame) {
     currentFrame = frame;
     _drawSurface.create(_fullSurface, srcRects[frame]);
+    
+    setTransparent(isTransparent == kTrue);
+
     _needsRedraw = true;
 }
 
diff --git a/engines/nancy/action/staticbitmapanim.h b/engines/nancy/action/staticbitmapanim.h
index da0535abe7..cc702edf26 100644
--- a/engines/nancy/action/staticbitmapanim.h
+++ b/engines/nancy/action/staticbitmapanim.h
@@ -81,7 +81,6 @@ protected:
     virtual Common::String getRecordTypeName() const override { return isInterruptible ? "PlayIntStaticBitmapAnimation" : "PlayStaticBitmapAnimation"; }
 
     virtual uint16 getZOrder() const override { return zOrder; }
-    virtual BlitType getBlitType() const override { return isTransparent == kTrue ? kTrans : kNoTrans; }
     virtual bool isViewportRelative() const override { return true; }
 
     void setFrame(uint frame);
diff --git a/engines/nancy/action/telephone.cpp b/engines/nancy/action/telephone.cpp
index 42f42b4cc0..dc552e3cc4 100644
--- a/engines/nancy/action/telephone.cpp
+++ b/engines/nancy/action/telephone.cpp
@@ -39,6 +39,8 @@ namespace Action {
 void Telephone::init() {
     _drawSurface.create(_screenPosition.width(), _screenPosition.height(), GraphicsManager::getInputPixelFormat());
     _drawSurface.clear(GraphicsManager::getTransColor());
+    
+    setTransparent(true);
 
     NanEngine.resource->loadImage(imageName, image);
 
diff --git a/engines/nancy/action/telephone.h b/engines/nancy/action/telephone.h
index 4e7ae39c3a..4f138962ab 100644
--- a/engines/nancy/action/telephone.h
+++ b/engines/nancy/action/telephone.h
@@ -91,7 +91,6 @@ protected:
     virtual Common::String getRecordTypeName() const override { return "Telephone"; }
 
     virtual uint16 getZOrder() const override { return 7; }
-    virtual BlitType getBlitType() const override { return kTrans; }
     virtual bool isViewportRelative() const override { return true; }
 
     void drawButton(uint id);
diff --git a/engines/nancy/graphics.cpp b/engines/nancy/graphics.cpp
index c87342cc0c..5edcf7164f 100644
--- a/engines/nancy/graphics.cpp
+++ b/engines/nancy/graphics.cpp
@@ -42,7 +42,6 @@ namespace Nancy {
 const Graphics::PixelFormat GraphicsManager::inputPixelFormat = Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0);
 const Graphics::PixelFormat GraphicsManager::screenPixelFormat = Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0);
 const Graphics::PixelFormat GraphicsManager::clut8Format = Graphics::PixelFormat::createFormatCLUT8();
-const uint GraphicsManager::transColor = 0x3E0;
 
 void GraphicsManager::init() {
     initGraphics(640, 480, &screenPixelFormat);
@@ -79,7 +78,7 @@ void GraphicsManager::draw() {
                     blitToScreen(*current._redrawFrom, current.getPreviousScreenPosition());
                 }
 
-                if (current.getBlitType() == RenderObject::kTrans) {
+                if (current._drawSurface.hasTransparentColor()) {
                     // Redraw below if transparent
                     blitToScreen(*current._redrawFrom, current.getScreenPosition());
                 }
@@ -153,10 +152,9 @@ const Graphics::PixelFormat &GraphicsManager::getInputPixelFormat() {
 
 uint GraphicsManager::getTransColor() {
     if (NanEngine._gameDescription->desc.flags & NGF_8BITCOLOR) {
-        Graphics::PixelFormat format = Graphics::PixelFormat::createFormatCLUT8();
-        return format.RGBToColor(255, 0, 255);
+        return 1; // If this isn't correct, try picking the pixel at [0, 0] inside the palette bitmap
     } else {
-        return transColor;
+        return inputPixelFormat.ARGBToColor(0, 0, 255, 0);
     }
 }
 
@@ -173,11 +171,7 @@ void GraphicsManager::loadFonts() {
 // Draw a given screen-space rectangle to the screen
 void GraphicsManager::blitToScreen(const RenderObject &src, Common::Rect screenRect) {
     Common::Point pointDest(screenRect.left, screenRect.top);
-    if (src.getBlitType() == RenderObject::kNoTrans) {
-        _screen.blitFrom(src._drawSurface, src.convertToLocal(screenRect), pointDest);
-    } else if (src.getBlitType() == RenderObject::kTrans) {
-        _screen.transBlitFrom(src._drawSurface, src.convertToLocal(screenRect), pointDest, transColor);
-    }
+    _screen.blitFrom(src._drawSurface, src.convertToLocal(screenRect), pointDest);
 }
 
 int GraphicsManager::objectComparator(const void *a, const void *b) {
diff --git a/engines/nancy/graphics.h b/engines/nancy/graphics.h
index a1971cc68a..3627685466 100644
--- a/engines/nancy/graphics.h
+++ b/engines/nancy/graphics.h
@@ -65,8 +65,6 @@ private:
 
     static int objectComparator(const void *a, const void *b);
 
-    static const uint transColor;
-
     Common::SortedArray<RenderObject *> _objects;
 
     static const Graphics::PixelFormat inputPixelFormat;
diff --git a/engines/nancy/renderobject.cpp b/engines/nancy/renderobject.cpp
index bf9879663d..94363d26f5 100644
--- a/engines/nancy/renderobject.cpp
+++ b/engines/nancy/renderobject.cpp
@@ -58,6 +58,14 @@ void RenderObject::setVisible(bool visible) {
     _needsRedraw = true;
 }
 
+void RenderObject::setTransparent(bool isTransparent) {
+    if (isTransparent) {
+        _drawSurface.setTransparentColor(GraphicsManager::getTransColor());
+    } else {
+        _drawSurface.clearTransparentColor();
+    }
+}
+
 Common::Rect RenderObject::getScreenPosition() const {
     if (isViewportRelative()) {
         return NancySceneState.getViewport().convertViewportToScreen(_screenPosition);
diff --git a/engines/nancy/renderobject.h b/engines/nancy/renderobject.h
index 74180ae171..6393b77887 100644
--- a/engines/nancy/renderobject.h
+++ b/engines/nancy/renderobject.h
@@ -41,8 +41,6 @@ class NancyEngine;
 class RenderObject {
     friend class GraphicsManager;
 public:
-    enum BlitType { kNoTrans, kTrans };
-
     RenderObject() :
         _needsRedraw(true),
         _isVisible(true),
@@ -61,6 +59,7 @@ public:
 
     void moveTo(Common::Point position);
     void setVisible(bool visible);
+    void setTransparent(bool isTransparent);
 
     bool hasMoved() const { return _previousScreenPosition != _screenPosition; }
     Common::Rect getScreenPosition() const;
@@ -77,7 +76,6 @@ protected:
     // Z order and blit type are extracted directly from the corresponding
     // ZRenderStruct from the original engine
     virtual uint16 getZOrder() const =0;
-    virtual BlitType getBlitType() const = 0;
 
     // Needed for proper handling of objects inside the viewport
     virtual bool isViewportRelative() const { return false; }
diff --git a/engines/nancy/state/credits.cpp b/engines/nancy/state/credits.cpp
index 1a3fdfd3dd..9e73c047a9 100644
--- a/engines/nancy/state/credits.cpp
+++ b/engines/nancy/state/credits.cpp
@@ -71,6 +71,7 @@ void Credits::init() {
     Common::Rect src = _text._screenPosition;
     src.moveTo(Common::Point());
     _text._drawSurface.create(_fullTextSurface, src);
+    _text.setTransparent(true);
     _text.init();
 
     NanEngine.sound->loadSound(_sound);
diff --git a/engines/nancy/state/credits.h b/engines/nancy/state/credits.h
index 996298cb2f..beb00c9853 100644
--- a/engines/nancy/state/credits.h
+++ b/engines/nancy/state/credits.h
@@ -62,7 +62,6 @@ protected:
 
     protected:
         virtual uint16 getZOrder() const override { return 1; }
-        virtual BlitType getBlitType() const override { return kTrans; }
     };
 
     State _state;
diff --git a/engines/nancy/state/map.h b/engines/nancy/state/map.h
index 63edbc14d3..a8cd187a83 100644
--- a/engines/nancy/state/map.h
+++ b/engines/nancy/state/map.h
@@ -90,7 +90,6 @@ private:
 
     protected:
         virtual uint16 getZOrder() const override { return 7; }
-        virtual BlitType getBlitType() const override { return kNoTrans; }
 
         Map *_parent;
     };
diff --git a/engines/nancy/ui/button.h b/engines/nancy/ui/button.h
index a58ee378f0..0b0ba0ccac 100644
--- a/engines/nancy/ui/button.h
+++ b/engines/nancy/ui/button.h
@@ -42,8 +42,6 @@ public:
 
 protected:
     virtual uint16 getZOrder() const override { return 5; }
-    virtual BlitType getBlitType() const override { return kNoTrans; }
-
 };
 
 class MenuButton : public Button {
diff --git a/engines/nancy/ui/fullscreenimage.h b/engines/nancy/ui/fullscreenimage.h
index 1e3c44d0b1..b333536f9f 100644
--- a/engines/nancy/ui/fullscreenimage.h
+++ b/engines/nancy/ui/fullscreenimage.h
@@ -37,7 +37,6 @@ public:
 protected:
     virtual void init() override {}
     virtual uint16 getZOrder() const override { return 0; }
-    virtual BlitType getBlitType() const override { return kNoTrans; }
 };
 
 } // End of namespace UI
diff --git a/engines/nancy/ui/inventorybox.cpp b/engines/nancy/ui/inventorybox.cpp
index 30824868be..7564b983db 100644
--- a/engines/nancy/ui/inventorybox.cpp
+++ b/engines/nancy/ui/inventorybox.cpp
@@ -219,7 +219,7 @@ void InventoryBox::InventoryScrollbar::init() {
     
     _maxDist = _parent->getBounds().height() - _drawSurface.h;
     
-    RenderObject::init();
+    Scrollbar::init();
 }
 
 void InventoryBox::Shades::init() {
@@ -228,6 +228,8 @@ void InventoryBox::Shades::init() {
     _screenPosition = _parent->getScreenPosition();
     _nextFrameTime = 0;
     setAnimationFrame(_curFrame);
+    
+    setTransparent(true);
 
     RenderObject::init();
 }
diff --git a/engines/nancy/ui/inventorybox.h b/engines/nancy/ui/inventorybox.h
index 663b47dea3..769c73ea61 100644
--- a/engines/nancy/ui/inventorybox.h
+++ b/engines/nancy/ui/inventorybox.h
@@ -76,7 +76,6 @@ public:
 
 protected:
     virtual uint16 getZOrder() const override { return 6; }
-    virtual BlitType getBlitType() const override { return kNoTrans; }
 
     void onScrollbarMove();
 
@@ -114,7 +113,6 @@ private:
 
     protected:
         virtual uint16 getZOrder() const override { return 9; }
-        virtual BlitType getBlitType() const override { return kTrans; }
 
         void setAnimationFrame(uint frame);
 
diff --git a/engines/nancy/ui/scrollbar.cpp b/engines/nancy/ui/scrollbar.cpp
index b1149e5ab9..a6203b5a5b 100644
--- a/engines/nancy/ui/scrollbar.cpp
+++ b/engines/nancy/ui/scrollbar.cpp
@@ -34,6 +34,11 @@
 namespace Nancy {
 namespace UI {
 
+void Scrollbar::init() {
+    setTransparent(true);
+    RenderObject::init();
+}
+
 void Scrollbar::handleInput(NancyInput &input) {
     if (_screenPosition.contains(input.mousePos)) {
         NanEngine.cursorManager->setCursorType(CursorManager::kHotspotArrow);
diff --git a/engines/nancy/ui/scrollbar.h b/engines/nancy/ui/scrollbar.h
index 44783c5da9..125542a042 100644
--- a/engines/nancy/ui/scrollbar.h
+++ b/engines/nancy/ui/scrollbar.h
@@ -42,6 +42,8 @@ public:
         _maxDist(0) {}
     virtual ~Scrollbar() =default;
 
+    virtual void init() override;
+
     void handleInput(NancyInput &input);
 
     void resetPosition();
@@ -49,7 +51,6 @@ public:
 
 protected:
     virtual uint16 getZOrder() const override { return 9; }
-    virtual BlitType getBlitType() const override { return kTrans; }
 
     void calculatePosition();
 
diff --git a/engines/nancy/ui/textbox.cpp b/engines/nancy/ui/textbox.cpp
index 3339653f6a..d60cbd5e29 100644
--- a/engines/nancy/ui/textbox.cpp
+++ b/engines/nancy/ui/textbox.cpp
@@ -299,7 +299,7 @@ void Textbox::TextboxScrollbar::init() {
 
     _maxDist = _parent->getBounds().height() - _drawSurface.h;
 
-    RenderObject::init();
+    Scrollbar::init();
 }
 
 } // End of namespace UI
diff --git a/engines/nancy/ui/textbox.h b/engines/nancy/ui/textbox.h
index 88dd350cb2..bcb811e22d 100644
--- a/engines/nancy/ui/textbox.h
+++ b/engines/nancy/ui/textbox.h
@@ -68,7 +68,6 @@ public:
 
 protected:
     virtual uint16 getZOrder() const override { return 6; }
-    virtual BlitType getBlitType() const override { return kNoTrans; }
 
 private:
     uint16 getInnerHeight();
diff --git a/engines/nancy/ui/viewport.h b/engines/nancy/ui/viewport.h
index e4285c9732..66532bf1aa 100644
--- a/engines/nancy/ui/viewport.h
+++ b/engines/nancy/ui/viewport.h
@@ -76,7 +76,6 @@ public:
 
 protected:
     virtual uint16 getZOrder() const override { return 6; }
-    virtual BlitType getBlitType() const override { return kNoTrans; }
 
     Common::Rect _upHotspot;
     Common::Rect _downHotspot;


Commit: 3922a3dc30de47915f16bf26549078858d0e8b63
    https://github.com/scummvm/scummvm/commit/3922a3dc30de47915f16bf26549078858d0e8b63
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Load The Vampire Diaries secondary video

Added code to load The Vampire Diaries' secondary video format, which includes a palette file field and some unknown extra data.

Changed paths:
    engines/nancy/action/secondaryvideo.cpp


diff --git a/engines/nancy/action/secondaryvideo.cpp b/engines/nancy/action/secondaryvideo.cpp
index f2194339dc..e4d5940f98 100644
--- a/engines/nancy/action/secondaryvideo.cpp
+++ b/engines/nancy/action/secondaryvideo.cpp
@@ -35,7 +35,6 @@
 #include "engines/nancy/sound.h"
 
 #include "common/system.h"
-
 #include "common/events.h"
 
 namespace Nancy {
@@ -52,6 +51,10 @@ void PlaySecondaryVideo::init() {
     _decoder.addFrameTime(12);
     _drawSurface.create(_decoder.getWidth(), _decoder.getHeight(), GraphicsManager::getInputPixelFormat());
 
+    if (paletteFilename.size()) {
+        GraphicsManager::loadSurfacePalette(_drawSurface, paletteFilename);
+    }
+
     setVisible(false);
     setTransparent(true);
 
@@ -145,15 +148,28 @@ void PlaySecondaryVideo::readData(Common::SeekableReadStream &stream) {
     char buf[10];
     stream.read(buf, 10);
     filename = buf;
-    stream.skip(0x14);
+    stream.read(buf, 10);
+    paletteFilename = buf;
+    stream.skip(10);
+    
+    if (paletteFilename.size()) {
+        stream.skip(14); // unknown data
+    }
+
     loopFirstFrame = stream.readUint16LE();
     loopLastFrame = stream.readUint16LE();
     onHoverFirstFrame = stream.readUint16LE();
     onHoverLastFrame = stream.readUint16LE();
     onHoverEndFirstFrame = stream.readUint16LE();
     onHoverEndLastFrame = stream.readUint16LE();
+
     sceneChange.readData(stream);
-    stream.skip(1);
+
+    if (paletteFilename.size()) {
+        stream.skip(3);
+    } else {
+        stream.skip(1);
+    }
 
     uint16 numVideoDescs = stream.readUint16LE();
     for (uint i = 0; i < numVideoDescs; ++i) {


Commit: ba6a973d4f63e685ce5d1a9bc42a5f847cce7e1a
    https://github.com/scummvm/scummvm/commit/ba6a973d4f63e685ce5d1a9bc42a5f847cce7e1a
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Destroy Scene state on exit

The Scene state is a singleton, which means it needs to be manually destroyed on exit, or else it will stay in memory after returning to launcher.

Changed paths:
    engines/nancy/nancy.cpp


diff --git a/engines/nancy/nancy.cpp b/engines/nancy/nancy.cpp
index 9b0b57c4e9..13db920c8e 100644
--- a/engines/nancy/nancy.cpp
+++ b/engines/nancy/nancy.cpp
@@ -159,6 +159,8 @@ Common::Error NancyEngine::run() {
 		_system->delayMillis(16);
 	}
 
+	NancySceneState.destroy();
+
 	return Common::kNoError;
 }
 


Commit: da559b5fe0d662dfe63772887977a16fc6b3a7b1
    https://github.com/scummvm/scummvm/commit/da559b5fe0d662dfe63772887977a16fc6b3a7b1
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Display The Vampire Diaries video correctly

The Vampire Diaries' videos are all upside down after decompression, need a palette to be loaded from a separate bitmap file, and background videos are also quarter size (1/2 width and height). This commit adds a GraphicsManager function for loading a Surface or a raw buffer into a ManagedSurface, applying all transformations needed to display it correctly.

Changed paths:
    engines/nancy/action/secondaryvideo.cpp
    engines/nancy/graphics.cpp
    engines/nancy/graphics.h
    engines/nancy/resource.cpp
    engines/nancy/state/scene.cpp
    engines/nancy/ui/viewport.cpp
    engines/nancy/ui/viewport.h


diff --git a/engines/nancy/action/secondaryvideo.cpp b/engines/nancy/action/secondaryvideo.cpp
index e4d5940f98..efc2893fbf 100644
--- a/engines/nancy/action/secondaryvideo.cpp
+++ b/engines/nancy/action/secondaryvideo.cpp
@@ -114,7 +114,8 @@ void PlaySecondaryVideo::updateGraphics() {
         if (_decoder.needsUpdate() && !_screenPosition.isEmpty()) {
             for (uint i = 0; i < videoDescs.size(); ++i) {
                 if ((uint16)videoDescs[i].frameID == _currentViewportFrame) {
-                    _drawSurface.blitFrom(*_decoder.decodeNextFrame(), videoDescs[i].srcRect, Common::Point());
+                    // This ignores the srcRects for every frame
+                    GraphicsManager::copyToManaged(*_decoder.decodeNextFrame(), _drawSurface, paletteFilename.size() > 0);
                     break;
                 }
             }
diff --git a/engines/nancy/graphics.cpp b/engines/nancy/graphics.cpp
index 5edcf7164f..d69f96aac8 100644
--- a/engines/nancy/graphics.cpp
+++ b/engines/nancy/graphics.cpp
@@ -137,11 +137,93 @@ void GraphicsManager::loadSurfacePalette(Graphics::ManagedSurface &inSurf, const
     if (f.open(paletteFilename + ".bmp")) {
         Image::BitmapDecoder dec;
         if (dec.loadStream(f)) {
-            inSurf.setPalette(dec.getPalette(), dec.getPaletteStartIndex(), dec.getPaletteColorCount());
+            inSurf.setPalette(dec.getPalette(), dec.getPaletteStartIndex(), MIN<uint>(256, dec.getPaletteColorCount()));
         }
     }
 }
 
+void GraphicsManager::copyToManaged(const Graphics::Surface &src, Graphics::ManagedSurface &dst, bool verticalFlip, bool doubleSize) {
+    if (dst.w != doubleSize ? src.w * 2 : src.w || dst.h != doubleSize ? src.h * 2 : src.h) {
+        const uint32 *palette = dst.getPalette();
+        bool hasTransColor = dst.hasTransparentColor();
+        dst.create(doubleSize ? src.w * 2 : src.w, doubleSize ? src.h * 2 : src.h, src.format);
+
+        if (palette) {
+            // free() clears the _hasPalette flag but doesn't clear the palette itself, so
+            // we just set it to itself; hopefully this doesn't cause any issues
+            dst.setPalette(palette, 0, 256);
+        }
+
+        if (hasTransColor) {
+            // Do the same trick with the transparent color
+            dst.setTransparentColor(dst.getTransparentColor());
+        }
+    }
+
+    if (!verticalFlip && !doubleSize) {
+        dst.copyRectToSurface(src, 0, 0, Common::Rect(0, 0, src.w, src.h));
+        return;
+    }
+
+    for (uint y = 0; y < src.h; ++y) {
+        if (!doubleSize) {
+            // Copy single line bottom to top
+            memcpy(dst.getBasePtr(0, y), src.getBasePtr(0, src.h - y - 1), src.w * src.format.bytesPerPixel);
+        } else {
+            // Make four copies of each source pixel
+            for (uint x = 0; x < src.w; ++x) {
+                switch (src.format.bytesPerPixel) {
+                case 1: {
+                    const byte *srcP = (const byte *)src.getBasePtr(x, y);
+                    uint dstX = x * 2;
+                    uint dstY = verticalFlip ? (src.h - y - 1) * 2 : src.h - y - 1;
+                    *((byte *)dst.getBasePtr(dstX, dstY)) = *srcP;
+                    *((byte *)dst.getBasePtr(dstX + 1, dstY)) = *srcP;
+                    *((byte *)dst.getBasePtr(dstX, dstY + 1)) = *srcP;
+                    *((byte *)dst.getBasePtr(dstX + 1, dstY + 1)) = *srcP;
+                    break;
+                }
+                case 2: {
+                    const uint16 *srcP = (const uint16 *)src.getBasePtr(x, y);
+                    uint dstX = x * 2;
+                    uint dstY = verticalFlip ? (src.h - y - 1) * 2 : src.h - y - 1;
+                    *((uint16 *)dst.getBasePtr(dstX, dstY)) = *srcP;
+                    *((uint16 *)dst.getBasePtr(dstX + 1, dstY)) = *srcP;
+                    *((uint16 *)dst.getBasePtr(dstX, dstY + 1)) = *srcP;
+                    *((uint16 *)dst.getBasePtr(dstX + 1, dstY + 1)) = *srcP;
+                    break;
+                }
+                case 4: {
+                    const uint32 *srcP = (const uint32 *)src.getBasePtr(x, y);
+                    uint dstX = x * 2;
+                    uint dstY = verticalFlip ? (src.h - y - 1) * 2 : src.h - y - 1;
+                    *((uint32 *)dst.getBasePtr(dstX, dstY)) = *srcP;
+                    *((uint32 *)dst.getBasePtr(dstX + 1, dstY)) = *srcP;
+                    *((uint32 *)dst.getBasePtr(dstX, dstY + 1)) = *srcP;
+                    *((uint32 *)dst.getBasePtr(dstX + 1, dstY + 1)) = *srcP;
+                    break;
+                }
+                default:
+                    return;
+                }
+            }
+        }
+    }
+}
+
+void GraphicsManager::copyToManaged(void *src, Graphics::ManagedSurface &dst, uint srcW, uint srcH, const Graphics::PixelFormat &format, bool verticalFlip, bool doubleSize) {
+    // Do things the lazy way and simply create a Surface and pass it to the other overload
+    // We do NOT free the surface since it's a temporary object and does not own the pixels
+    Graphics::Surface surf;
+    surf.w = srcW;
+    surf.h = srcH;
+    surf.format = format;
+    surf.pitch = srcW * format.bytesPerPixel;
+    surf.setPixels(src);
+
+    copyToManaged(surf, dst, verticalFlip, doubleSize);
+}
+
 const Graphics::PixelFormat &GraphicsManager::getInputPixelFormat() {
     if (NanEngine._gameDescription->desc.flags & NGF_8BITCOLOR) {
         return clut8Format;
diff --git a/engines/nancy/graphics.h b/engines/nancy/graphics.h
index 3627685466..0e0d9d49a5 100644
--- a/engines/nancy/graphics.h
+++ b/engines/nancy/graphics.h
@@ -51,6 +51,8 @@ public:
     Font *getFont(uint id) { return id < _fonts.size() ? &_fonts[id] : nullptr; }
 
     static void loadSurfacePalette(Graphics::ManagedSurface &inSurf, const Common::String paletteFilename);
+    static void copyToManaged(const Graphics::Surface &src, Graphics::ManagedSurface &dst, bool verticalFlip = false, bool doubleSize = false);
+    static void copyToManaged(void *src, Graphics::ManagedSurface &dst, uint srcW, uint srcH, const Graphics::PixelFormat &format, bool verticalFlip = false, bool doubleSize = false);
 
     static const Graphics::PixelFormat &getInputPixelFormat();
     static uint getTransColor();
diff --git a/engines/nancy/resource.cpp b/engines/nancy/resource.cpp
index e179549312..4e9eff0b59 100644
--- a/engines/nancy/resource.cpp
+++ b/engines/nancy/resource.cpp
@@ -794,22 +794,22 @@ bool ResourceManager::loadImage(const Common::String &name, Graphics::Surface &s
 
 bool ResourceManager::loadImage(const Common::String &name, Graphics::ManagedSurface &surf) {
 	CifInfo info;
-	Image::BitmapDecoder dec;
-	Common::File f;
 	bool loadedFromBitmapFile = false;
 	surf.free();
 
-	Graphics::Surface tmpSurf;
-
 	byte *buf = getCifData(name, info);
 
 	if (!buf)  {
 		// Couldn't find image in a cif tree, try to open a .bmp file
 		// This is used by The Vampire Diaries
+		Common::File f;
 		loadedFromBitmapFile = f.open(name + ".bmp");
 		if (loadedFromBitmapFile) {
+			Image::BitmapDecoder dec;
 			if (dec.loadStream(f)) {
-				tmpSurf.copyFrom(*dec.getSurface());
+				GraphicsManager::copyToManaged(*dec.getSurface(), surf);
+				surf.setPalette(dec.getPalette(), dec.getPaletteStartIndex(), MIN<uint>(256, dec.getPaletteColorCount())); // LOGO.BMP reports 257 colors
+				return true;
 			} else {
 				return false;
 			}
@@ -829,23 +829,9 @@ bool ResourceManager::loadImage(const Common::String &name, Graphics::ManagedSur
 			return false;
 		}
 
-		tmpSurf.w = info.width;
-		tmpSurf.h = info.height;
-		tmpSurf.pitch = info.pitch;
-		tmpSurf.setPixels(buf);
-		tmpSurf.format = GraphicsManager::getInputPixelFormat();
-	}
-
-	surf.create(tmpSurf.w, tmpSurf.h, tmpSurf.format);
-	surf.blitFrom(tmpSurf);
-	
-	if (NanEngine._gameDescription->desc.flags & NGF_8BITCOLOR && loadedFromBitmapFile) {
-		surf.setPalette(dec.getPalette(), dec.getPaletteStartIndex(), dec.getPaletteColorCount() - 1);
+		GraphicsManager::copyToManaged(buf, surf, info.width, info.height, GraphicsManager::getInputPixelFormat());
+		return true;
 	}
-
-	tmpSurf.free();
-
-	return true;
 }
 
 void ResourceManager::list(const Common::String &treeName, Common::Array<Common::String> &nameList, uint type) {
diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index e982c35c24..a17f0c643d 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -360,7 +360,11 @@ void Scene::load() {
         _actionManager.addNewActionRecord(*actionRecordChunk);
     }
 
-    _viewport.loadVideo(_sceneState.summary.videoFile, _sceneState.nextScene.frameID, _sceneState.nextScene.verticalOffset, _sceneState.summary.videoPaletteFile);
+    _viewport.loadVideo(_sceneState.summary.videoFile,
+                        _sceneState.nextScene.frameID,
+                        _sceneState.nextScene.verticalOffset,
+                        _sceneState.summary.videoFormat,
+                        _sceneState.summary.videoPaletteFile);
 
     // TODO TEMPORARY
     _viewport.setEdgesSize(25, 25, 25, 25);
diff --git a/engines/nancy/ui/viewport.cpp b/engines/nancy/ui/viewport.cpp
index 0fd4ec17a3..3a105092c1 100644
--- a/engines/nancy/ui/viewport.cpp
+++ b/engines/nancy/ui/viewport.cpp
@@ -29,6 +29,8 @@
 #include "engines/nancy/cursor.h"
 #include "engines/nancy/util.h"
 
+#include "graphics/transparent_surface.h"
+
 namespace Nancy {
 namespace UI {
 
@@ -178,11 +180,13 @@ void Viewport::handleInput(NancyInput &input) {
     _movementLastFrame = direction;
 }
 
-void Viewport::loadVideo(const Common::String &filename, uint frameNr, uint verticalScroll, const Common::String &palette) {
+void Viewport::loadVideo(const Common::String &filename, uint frameNr, uint verticalScroll, uint16 format, const Common::String &palette) {
     if (_decoder.isVideoLoaded()) {
         _decoder.close();
     }
     _decoder.loadFile(filename + ".avf");
+
+    _videoFormat = format;
     
     enableEdges(kUp | kDown | kLeft | kRight);
     
@@ -203,11 +207,14 @@ void Viewport::setFrame(uint frameNr) {
 
     const Graphics::Surface *newFrame = _decoder.decodeFrame(frameNr);
 
-    if (_fullFrame.w != newFrame->w || _fullFrame.h != newFrame->h) {
-        _fullFrame.create(newFrame->w, newFrame->h, GraphicsManager::getInputPixelFormat());
+    if (_videoFormat == 2) {
+        // Format 2 uses full-size images
+        GraphicsManager::copyToManaged(*newFrame, _fullFrame);
+    } else if (_videoFormat == 1) {
+        // Format 1 uses quarter-size, upside-down images
+        GraphicsManager::copyToManaged(*newFrame, _fullFrame, true, true);
     }
 
-    _fullFrame.blitFrom(*newFrame);
     _needsRedraw = true;
 
     _currentFrame = frameNr;
diff --git a/engines/nancy/ui/viewport.h b/engines/nancy/ui/viewport.h
index 66532bf1aa..08ad64c281 100644
--- a/engines/nancy/ui/viewport.h
+++ b/engines/nancy/ui/viewport.h
@@ -49,7 +49,7 @@ public:
     virtual void init() override;
     void handleInput(NancyInput &input);
 
-    void loadVideo(const Common::String &filename, uint frameNr = 0, uint verticalScroll = 0, const Common::String &palette = Common::String());
+    void loadVideo(const Common::String &filename, uint frameNr = 0, uint verticalScroll = 0, uint16 format = 2, const Common::String &palette = Common::String());
     
     void setFrame(uint frameNr);
     void setNextFrame();
@@ -89,6 +89,7 @@ protected:
 
     AVFDecoder _decoder;
     uint16 _currentFrame;
+    uint16 _videoFormat;
     Graphics::ManagedSurface _fullFrame;
     Common::Rect _format1Bounds;
     Common::Rect _format2Bounds;


Commit: 653239aa265f20f50f2d0cc13c23f5c353cee96e
    https://github.com/scummvm/scummvm/commit/653239aa265f20f50f2d0cc13c23f5c353cee96e
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Clip viewport-relative objects

RenderObjects marked as viewport-relative are now constrained within the bounds of the viewport

Changed paths:
    engines/nancy/ui/viewport.cpp


diff --git a/engines/nancy/ui/viewport.cpp b/engines/nancy/ui/viewport.cpp
index 3a105092c1..2b43eb8d8c 100644
--- a/engines/nancy/ui/viewport.cpp
+++ b/engines/nancy/ui/viewport.cpp
@@ -269,6 +269,7 @@ Common::Rect Viewport::getBoundsByFormat(uint format) const {
 Common::Rect Viewport::convertViewportToScreen(const Common::Rect &viewportRect) const {
     Common::Rect ret = convertToScreen(viewportRect);
     ret.translate(0, -getCurVerticalScroll());
+    ret.clip(_screenPosition);
     return ret;
 }
 


Commit: 6f10d4155953fe7061a5efa4970e231fe82ec68a
    https://github.com/scummvm/scummvm/commit/6f10d4155953fe7061a5efa4970e231fe82ec68a
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Fix Secrets Can Kill textbox colors

Fixed the textbox colors in Secrets Can Kill, which got broken when adding support for multiple pixel formats.

Changed paths:
    engines/nancy/font.cpp


diff --git a/engines/nancy/font.cpp b/engines/nancy/font.cpp
index 9172109a4f..cc57a51173 100644
--- a/engines/nancy/font.cpp
+++ b/engines/nancy/font.cpp
@@ -96,11 +96,23 @@ void Font::drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 col
     
     for (uint curY = 0; curY < height; ++curY) {
         for (uint curX = 0; curX < width; ++curX) {
-            // Assumes 2 bytes per pixel
-            uint16 curByte = *(const uint16 *)_image.getBasePtr(srcRect.left + curX, srcRect.top +  curY);
-            
-            if (curByte != _transColor) {
-                *(uint16 *)dst->getBasePtr(x + curX, y + yOffset + curY) = curByte;
+            switch (GraphicsManager::getInputPixelFormat().bytesPerPixel) {
+            case 1:
+                // TODO
+                break;
+            case 2: {
+                uint16 curColor = *(const uint16 *)_image.getBasePtr(srcRect.left + curX, srcRect.top +  curY);
+                
+                if (curColor != _transColor) {
+                    uint8 r, g, b;
+                    _image.format.colorToRGB(curColor, r, g, b);
+                    *(uint16 *)dst->getBasePtr(x + curX, y + yOffset + curY) = dst->format.RGBToColor(r, g, b);
+                }
+
+                break;
+            }
+            default:
+                break;
             }
         }
     }


Commit: b7cdb2ee915c4c0dd0d6839c6a027cd2a05f262f
    https://github.com/scummvm/scummvm/commit/b7cdb2ee915c4c0dd0d6839c6a027cd2a05f262f
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Implement loading saves from launcher

Reordered some of the engine and scene boot code and implemented loading directly from the launcher.

Changed paths:
    engines/nancy/nancy.cpp
    engines/nancy/state/scene.cpp
    engines/nancy/state/scene.h


diff --git a/engines/nancy/nancy.cpp b/engines/nancy/nancy.cpp
index 13db920c8e..b2ec7a7ab3 100644
--- a/engines/nancy/nancy.cpp
+++ b/engines/nancy/nancy.cpp
@@ -111,34 +111,19 @@ Common::Platform NancyEngine::getPlatform() const {
 }
 
 Common::Error NancyEngine::run() {
-	const Common::FSNode gameDataDir(ConfMan.get("path"));
-	SearchMan.addSubDirectoryMatching(gameDataDir, "game");
-	SearchMan.addSubDirectoryMatching(gameDataDir, "datafiles");
-	SearchMan.addSubDirectoryMatching(gameDataDir, "hdsound");
-	SearchMan.addSubDirectoryMatching(gameDataDir, "cdsound");
-	SearchMan.addSubDirectoryMatching(gameDataDir, "hdvideo");
-	SearchMan.addSubDirectoryMatching(gameDataDir, "cdvideo");
-	SearchMan.addSubDirectoryMatching(gameDataDir, "iff");
-	SearchMan.addSubDirectoryMatching(gameDataDir, "art");
-	SearchMan.addSubDirectoryMatching(gameDataDir, "font");
+	// Boot the engine
+	setState(kBoot);
 	
-	Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember("data1.cab");
-	if (stream) {
-		Common::Archive *cab = Common::makeInstallShieldArchive(stream);
-		
-		if (cab) {
-			SearchMan.add("data1.hdr", cab);
+	// Check if we need to load a save state from the launcher
+	if (ConfMan.hasKey("save_slot")) {
+		int saveSlot = ConfMan.getInt("save_slot");
+		if (saveSlot >= 0 && saveSlot <= getMetaEngine().getMaximumSaveSlot()) {
+			// Set to Scene but do not do the loading yet
+			setState(kScene);
 		}
 	}
-	
-	resource = new ResourceManager();
-	resource->initialize();
-
-	// Setup mixer
-	syncSoundSettings();
-
-	setState(kBoot);
 
+	// Main loop
 	while (!shouldQuit()) {
 		cursorManager->setCursorType(CursorManager::kNormalArrow);
 		input->processEvents();
@@ -165,6 +150,34 @@ Common::Error NancyEngine::run() {
 }
 
 void NancyEngine::bootGameEngine() {
+	// Load paths
+	const Common::FSNode gameDataDir(ConfMan.get("path"));
+	SearchMan.addSubDirectoryMatching(gameDataDir, "game");
+	SearchMan.addSubDirectoryMatching(gameDataDir, "datafiles");
+	SearchMan.addSubDirectoryMatching(gameDataDir, "hdsound");
+	SearchMan.addSubDirectoryMatching(gameDataDir, "cdsound");
+	SearchMan.addSubDirectoryMatching(gameDataDir, "hdvideo");
+	SearchMan.addSubDirectoryMatching(gameDataDir, "cdvideo");
+	SearchMan.addSubDirectoryMatching(gameDataDir, "iff");
+	SearchMan.addSubDirectoryMatching(gameDataDir, "art");
+	SearchMan.addSubDirectoryMatching(gameDataDir, "font");
+	
+	// Load archive
+	Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember("data1.cab");
+	if (stream) {
+		Common::Archive *cab = Common::makeInstallShieldArchive(stream);
+		
+		if (cab) {
+			SearchMan.add("data1.hdr", cab);
+		}
+	}
+	
+	resource = new ResourceManager();
+	resource->initialize();
+
+	// Setup mixer
+	syncSoundSettings();
+
 	clearBootChunks();
 	IFF *boot = new IFF("boot");
 	if (!boot->load())
@@ -174,6 +187,8 @@ void NancyEngine::bootGameEngine() {
 	addBootChunk("BSUM", boot->getChunkStream("BSUM"));
 	readBootSummary(*boot);
 
+	// Data chunks found in BOOT. These get used in many places in the engine,
+	// so we always keep them in memory
 	Common::String names[] = {
 		"INTR", "HINT", "LOGO", "SPUZ", "INV",
 		"FONT", "MENU", "HELP", "CRED", "LOAD",
@@ -186,13 +201,7 @@ void NancyEngine::bootGameEngine() {
 		addBootChunk(n, boot->getChunkStream(n));
 	}
 
-	// The FR, LG and OB chunks get added here	
-
-	
-	// TODO reset some vars
-	// TODO reset some more vars
-
-	// These originally get loaded inside Logo
+	// Persistent sounds that are used across the engine. These originally get loaded inside Logo
 	SoundDescription desc;
 	desc.read(*NanEngine.getBootChunkStream("BUOK"), SoundDescription::kNormal);
 	NanEngine.sound->loadSound(desc);
@@ -208,6 +217,9 @@ void NancyEngine::bootGameEngine() {
 	NanEngine.sound->loadSound(desc);
 
 	delete boot;
+	
+	graphicsManager->init();
+	cursorManager->init();
 }
 
 State::State *NancyEngine::getStateObject(GameState state) {
@@ -358,8 +370,6 @@ void NancyEngine::setState(GameState state, GameState overridePrevious) {
 	switch (state) {
 	case kBoot:
 		bootGameEngine();
-		graphicsManager->init();
-		cursorManager->init();
 		setState(kLogo);
 		return;
 	case kMainMenu:
diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index a17f0c643d..9d1e93c180 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -39,6 +39,7 @@
 #include "common/rect.h"
 #include "common/func.h"
 #include "common/serializer.h"
+#include "common/config-manager.h"
 
 #include "graphics/surface.h"
 
@@ -53,9 +54,11 @@ void Scene::process() {
     switch (_state) {
     case kInit:
         init();
-        // fall through
-    case kInitStatic:
-        initStatic();
+
+        if (_state != kLoad) {
+            break;
+        }
+
         // fall through
     case kLoad:
         load();
@@ -182,7 +185,6 @@ void Scene::synchronize(Common::Serializer &ser) {
         ser.syncAsUint16LE(_sceneState.nextScene.verticalOffset);
         _sceneState._doNotStartSound = false;
 
-        initStatic();
         load();
     }
     
@@ -288,12 +290,25 @@ void Scene::init() {
 
     Action::SliderPuzzle::playerHasTriedPuzzle = false;
 
-    _state = kInitStatic;
+    initStaticData();
+
+    if (ConfMan.hasKey("save_slot")) {
+        // Load savefile directly from the launcher
+		int saveSlot = ConfMan.getInt("save_slot");
+		if (saveSlot >= 0 && saveSlot <= NanEngine.getMetaEngine().getMaximumSaveSlot()) {
+			// Set to Scene but do not do the loading yet
+			NanEngine.loadGameState(saveSlot);
+		}
+	} else {
+        // Normal boot, load default first scene
+        _state = kLoad;
+    }
 
     registerGraphics();
+    NanEngine.graphicsManager->redrawAll();
 }
 
-void Scene::initStatic() {
+void Scene::initStaticData() {
     Common::SeekableReadStream *chunk = NanEngine.getBootChunkStream("MAP");
     chunk->seek(0x8A);
     readRect(*chunk, _mapHotspot);
@@ -373,27 +388,25 @@ void Scene::load() {
         _viewport.disableEdges(kLeft | kRight);
     }
 
-    if (!hasLoadedFromSavefile) {
-        _sceneState.currentScene.verticalOffset = _sceneState.nextScene.verticalOffset;
-        _sceneState.currentScene.frameID = _sceneState.nextScene.frameID;
+    _sceneState.currentScene.verticalOffset = _sceneState.nextScene.verticalOffset;
+    _sceneState.currentScene.frameID = _sceneState.nextScene.frameID;
 
-        if (_sceneState.summary.videoFormat == 1) {
-            // TODO not sure this ever gets hit
-        } else if (_sceneState.summary.videoFormat == 2) {
-            // always start from the bottom
-            _sceneState.currentScene.verticalOffset = _viewport.getMaxScroll();
-        } else {
-            error("Unrecognized Scene summary chunk video file format");
-        }
+    if (_sceneState.summary.videoFormat == 1) {
+        // TODO not sure this ever gets hit
+    } else if (_sceneState.summary.videoFormat == 2) {
+        // always start from the bottom
+        _sceneState.currentScene.verticalOffset = _viewport.getMaxScroll();
+    } else {
+        error("Unrecognized Scene summary chunk video file format");
+    }
 
-        // Some checks against rFrame
+    // Some checks against rFrame
 
-        if (_sceneState.summary.videoFormat == 1) {
-            // TODO not sure this ever gets hit
-        } else if (_sceneState.summary.videoFormat == 2) {
-            if (_viewport.getMaxScroll() == 0) {
-                _viewport.disableEdges(kUp | kDown);
-            }
+    if (_sceneState.summary.videoFormat == 1) {
+        // TODO not sure this ever gets hit
+    } else if (_sceneState.summary.videoFormat == 2) {
+        if (_viewport.getMaxScroll() == 0) {
+            _viewport.disableEdges(kUp | kDown);
         }
     }
 
diff --git a/engines/nancy/state/scene.h b/engines/nancy/state/scene.h
index 18daa8392e..029f3da1ea 100644
--- a/engines/nancy/state/scene.h
+++ b/engines/nancy/state/scene.h
@@ -163,16 +163,16 @@ public:
 
 private:
     void init();
-    void initStatic();
     void load();
     void run();
 
+    void initStaticData();
+
     void clearSceneData();
 
 public:
     enum State {
         kInit,
-        kInitStatic,
         kLoad,
         kStartSound,
         kRun
@@ -253,7 +253,6 @@ protected:
     State _state;
 
     bool isComingFromMenu = true;
-    bool hasLoadedFromSavefile = false;
     bool _shouldClearTextbox = true;
 };
 


Commit: b3d06c327ee1d4cb1a27ff3e3fc2c980b7330c6c
    https://github.com/scummvm/scummvm/commit/b3d06c327ee1d4cb1a27ff3e3fc2c980b7330c6c
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add savefile version checks

Savefiles are now versioned (breaking previously made saves). Right now the savefile version is global across all supported games, but this will probably change as more games are added.

Changed paths:
    engines/nancy/nancy.cpp


diff --git a/engines/nancy/nancy.cpp b/engines/nancy/nancy.cpp
index b2ec7a7ab3..87626f2b60 100644
--- a/engines/nancy/nancy.cpp
+++ b/engines/nancy/nancy.cpp
@@ -292,23 +292,18 @@ void NancyEngine::clearBootChunks() {
 Common::Error NancyEngine::synchronize(Common::Serializer &ser) {
 	Common::SeekableReadStream *bsum = getBootChunkStream("BSUM");
 	bsum->seek(0);
-	
-	if (ser.isLoading()) {
-		byte buf[90];
-		byte bsumBuf[90];
-		ser.syncBytes(buf, 90);
-		bsum->read(bsumBuf, 90);
-		if (Common::String((char *)bsumBuf) != (char *)buf) {
-			return Common::kReadingFailed;
-		}
-	} else if (ser.isSaving()) {
-		byte buf[90];
-		bsum->read(buf, 90);
-		ser.syncBytes(buf, 90);
-	}
 
+	// Sync boot summary header, which includes full game title
+	ser.syncVersion(kSavegameVersion);
+	char buf[90];
+	bsum->read(buf, 90);
+	ser.matchBytes(buf, 90);
+
+	// Sync scene and action records
 	NancySceneState.synchronize(ser);
 	NancySceneState._actionManager.synchronize(ser);
+
+	// Sync any action record-related data
 	Action::SliderPuzzle::synchronize(ser);
 
 	return Common::kNoError;


Commit: a16c9b90ae06ea6b40d28e66be0a49c821a90dbb
    https://github.com/scummvm/scummvm/commit/a16c9b90ae06ea6b40d28e66be0a49c821a90dbb
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Clean up graphics loop

Removed some leftovers from an older version of the graphics manager and made the drawing code only loop once through all render objects instead of twice.

Changed paths:
    engines/nancy/graphics.cpp


diff --git a/engines/nancy/graphics.cpp b/engines/nancy/graphics.cpp
index d69f96aac8..06db2426fb 100644
--- a/engines/nancy/graphics.cpp
+++ b/engines/nancy/graphics.cpp
@@ -47,6 +47,7 @@ void GraphicsManager::init() {
     initGraphics(640, 480, &screenPixelFormat);
     _screen.create(640, 480, screenPixelFormat);
     _screen.setTransparentColor(getTransColor());
+    _screen.clear();
 
     Common::SeekableReadStream *ob = NanEngine.getBootChunkStream("OB0");
     ob->seek(0);
@@ -57,17 +58,10 @@ void GraphicsManager::init() {
 }
 
 void GraphicsManager::draw() {
-    // First go through all objects and update them
-    // Then add dirty rects to layers below if transparent
     for (auto it : _objects) {
         RenderObject &current = *it;
-        current.updateGraphics();
-    }
-
-    Common::Array<Common::Rect> drawn;
 
-    for (auto it : _objects) {
-        RenderObject &current = *it;
+        current.updateGraphics();
 
         if (current._isVisible && current._needsRedraw) {
             // object is visible and updated


Commit: a42de3037f13fe117424bc731f8caff7743df2b2
    https://github.com/scummvm/scummvm/commit/a42de3037f13fe117424bc731f8caff7743df2b2
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Wait for video end during dialogue

Primary video now waits for the NPC video to end before changing scenes/playing a response sound. This usually adds a small delay at the end of a dialogue line, making the conversation sound more natural.

Changed paths:
    engines/nancy/action/primaryvideo.cpp


diff --git a/engines/nancy/action/primaryvideo.cpp b/engines/nancy/action/primaryvideo.cpp
index c8edf82b11..e7cf335229 100644
--- a/engines/nancy/action/primaryvideo.cpp
+++ b/engines/nancy/action/primaryvideo.cpp
@@ -257,7 +257,7 @@ void PlayPrimaryVideoChan0::execute() {
             }
         }
 
-        if (!NanEngine.sound->isSoundPlaying(sound)) {
+        if (!NanEngine.sound->isSoundPlaying(sound) && _decoder.endOfVideo()) {
             NanEngine.sound->stopSound(sound);
             
             if (responses.size() == 0) {


Commit: 15153a55339e7b96d0f3efcffc9f6b40c66f56ab
    https://github.com/scummvm/scummvm/commit/15153a55339e7b96d0f3efcffc9f6b40c66f56ab
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Filter input between states

Added code to filter out inputs that began in a different state; e.g. if the mouse was pushed and held down in a previous state, the button up event won't fire. This fixes the issue where dismissing the intro logo would accidentally trigger a click inside the scene viewport.

Changed paths:
    engines/nancy/input.cpp
    engines/nancy/input.h
    engines/nancy/nancy.h


diff --git a/engines/nancy/input.cpp b/engines/nancy/input.cpp
index 6fe190efb7..dc3d7c25e5 100644
--- a/engines/nancy/input.cpp
+++ b/engines/nancy/input.cpp
@@ -52,6 +52,10 @@ void InputManager::processEvents() {
             }
             break;
         case EVENT_CUSTOM_ENGINE_ACTION_START:
+            if (_inputBeginState == nullptr) {
+                _inputBeginState = NanEngine.getState();
+            }
+            
             switch (event.customType) {
             case kNancyActionLeftClick:
                 _inputs |= NancyInput::kLeftMouseButtonDown;
@@ -121,17 +125,30 @@ void InputManager::processEvents() {
             break;
         }
     }
+
+    if (_inputs == 0 && _otherKbdInput.size() == 0) {
+        _inputBeginState = nullptr;
+    }
 }
 
 NancyInput InputManager::getInput() const {
     NancyInput ret;
-    ret.input = _inputs;
+
+    // Filter out inputs that began in other states; e.g. if the mouse was pushed and held down
+    // in a previous state, the button up event won't fire. Right now we simply block all events
+    // until everything's clear, but if that causes problems the fix should be easy.
+    if (_inputBeginState == NanEngine.getState()) {
+        ret.input = _inputs;
+        ret.otherKbdInput = _otherKbdInput;
+    } else {
+        ret.input = 0;
+    }
+
     if (_mouseEnabled) {
         ret.mousePos = NanEngine.getEventManager()->getMousePos();
     } else {
         ret.eatMouseInput();
     }
-    ret.otherKbdInput = _otherKbdInput;
     return ret;
 }
 
diff --git a/engines/nancy/input.h b/engines/nancy/input.h
index 7e13869e10..048b7359fb 100644
--- a/engines/nancy/input.h
+++ b/engines/nancy/input.h
@@ -37,6 +37,10 @@ namespace Nancy {
 
 class NancyEngine;
 
+namespace State {
+class State;
+}
+
 struct NancyInput {
     enum InputType : uint16 {
         kLeftMouseButtonDown    = 1 << 0,
@@ -103,6 +107,7 @@ private:
     uint16 _inputs;
     Common::Array<Common::KeyState> _otherKbdInput;
     bool _mouseEnabled;
+    State::State *_inputBeginState;
 };
 
 } // End of namespace Nancy
diff --git a/engines/nancy/nancy.h b/engines/nancy/nancy.h
index 3ccf7723ed..b58f4b8def 100644
--- a/engines/nancy/nancy.h
+++ b/engines/nancy/nancy.h
@@ -127,6 +127,7 @@ public:
 	void stopAndUnloadSpecificSounds();
 	
 	void setState(GameState state, GameState overridePrevious = kNone);
+	State::State *getState() { return _gameFlow.currentState; }
 	void setPreviousState();
 
 	void callCheatMenu(bool eventFlags) { setState(kCheat), _cheatTypeIsEventFlag = eventFlags; }


Commit: e324adf05f793a02026e2fed0dcc1d7fe53da0d5
    https://github.com/scummvm/scummvm/commit/e324adf05f793a02026e2fed0dcc1d7fe53da0d5
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Code cleanup

Did some cleanup of the engine, mostly focusing on the NancyEngine class. Reordered several classes' members, removed unused functions, edited comments, and renamed arfactory_v1.cpp to arfactory.cpp.

Changed paths:
  A engines/nancy/action/arfactory.cpp
  R engines/nancy/action/arfactory_v1.cpp
    engines/nancy/action/actionmanager.cpp
    engines/nancy/action/primaryvideo.cpp
    engines/nancy/action/recordtypes.cpp
    engines/nancy/action/rotatinglockpuzzle.cpp
    engines/nancy/cheat.h
    engines/nancy/graphics.cpp
    engines/nancy/input.h
    engines/nancy/metaengine.cpp
    engines/nancy/module.mk
    engines/nancy/nancy.cpp
    engines/nancy/nancy.h
    engines/nancy/renderobject.h
    engines/nancy/resource.h
    engines/nancy/sound.cpp
    engines/nancy/sound.h
    engines/nancy/state/logo.cpp
    engines/nancy/state/logo.h
    engines/nancy/state/scene.cpp
    engines/nancy/state/scene.h
    engines/nancy/state/state.h


diff --git a/engines/nancy/action/actionmanager.cpp b/engines/nancy/action/actionmanager.cpp
index 2bbdcd28a8..f57a7d1b60 100644
--- a/engines/nancy/action/actionmanager.cpp
+++ b/engines/nancy/action/actionmanager.cpp
@@ -322,19 +322,19 @@ void ActionManager::processActionRecords() {
                         // Also, I'm pretty sure it never gets used
                         switch (dep.milliseconds) {
                         case 1:
-                            if (dep.seconds < NancySceneState._sceneState.sceneHitCount[dep.hours]) {
+                            if (dep.seconds < NancySceneState._flags.sceneHitCount[dep.hours]) {
                                 dep.satisfied = true;
                             }
 
                             break;
                         case 2:
-                            if (dep.seconds > NancySceneState._sceneState.sceneHitCount[dep.hours]) {
+                            if (dep.seconds > NancySceneState._flags.sceneHitCount[dep.hours]) {
                                 dep.satisfied = true;
                             }
 
                             break;
                         case 3:
-                            if (dep.seconds == NancySceneState._sceneState.sceneHitCount[dep.hours]) {
+                            if (dep.seconds == NancySceneState._flags.sceneHitCount[dep.hours]) {
                                 dep.satisfied = true;
                             }
 
diff --git a/engines/nancy/action/arfactory_v1.cpp b/engines/nancy/action/arfactory.cpp
similarity index 100%
rename from engines/nancy/action/arfactory_v1.cpp
rename to engines/nancy/action/arfactory.cpp
diff --git a/engines/nancy/action/primaryvideo.cpp b/engines/nancy/action/primaryvideo.cpp
index e7cf335229..960cac82b4 100644
--- a/engines/nancy/action/primaryvideo.cpp
+++ b/engines/nancy/action/primaryvideo.cpp
@@ -370,7 +370,7 @@ void PlayPrimaryVideoChan0::addGoodbye() {
             newResponse.soundName = snd;
             newResponse.text = file.readString();
             // response is picked randomly
-            newResponse.sceneChange.sceneID = res.sceneIDs[NanEngine._rnd->getRandomNumber(3)];
+            newResponse.sceneChange.sceneID = res.sceneIDs[NanEngine.randomSource->getRandomNumber(3)];
             newResponse.sceneChange.doNotStartSound = true;
 
             file.close();
diff --git a/engines/nancy/action/recordtypes.cpp b/engines/nancy/action/recordtypes.cpp
index e431f839af..b65e18ce80 100644
--- a/engines/nancy/action/recordtypes.cpp
+++ b/engines/nancy/action/recordtypes.cpp
@@ -291,7 +291,7 @@ void LoseGame::readData(Common::SeekableReadStream &stream) {
 }
 
 void LoseGame::execute() {
-    NanEngine.stopAndUnloadSpecificSounds();
+    NanEngine.sound->stopAndUnloadSpecificSounds();
     NanEngine.setState(NancyEngine::kMainMenu);
     NancySceneState.resetStateToInit();
     isDone = true;
@@ -310,7 +310,7 @@ void WinGame::readData(Common::SeekableReadStream &stream) {
 }
 
 void WinGame::execute() {
-    NanEngine.stopAndUnloadSpecificSounds();
+    NanEngine.sound->stopAndUnloadSpecificSounds();
     NanEngine.setState(NancyEngine::kCredits, NancyEngine::kMainMenu);
     
     // TODO replace with destroy()?
diff --git a/engines/nancy/action/rotatinglockpuzzle.cpp b/engines/nancy/action/rotatinglockpuzzle.cpp
index 7c5772741d..467b5f04dc 100644
--- a/engines/nancy/action/rotatinglockpuzzle.cpp
+++ b/engines/nancy/action/rotatinglockpuzzle.cpp
@@ -109,7 +109,7 @@ void RotatingLockPuzzle::execute() {
         registerGraphics();
 
         for (uint i = 0; i < correctSequence.size(); ++i) {
-            currentSequence.push_back(NanEngine._rnd->getRandomNumber(9));
+            currentSequence.push_back(NanEngine.randomSource->getRandomNumber(9));
             drawDial(i);
         }
 
diff --git a/engines/nancy/cheat.h b/engines/nancy/cheat.h
index 4ef098df86..d7bd7f79e5 100644
--- a/engines/nancy/cheat.h
+++ b/engines/nancy/cheat.h
@@ -20,7 +20,6 @@
  *
  */
 
-
 #ifndef NANCY_CHEAT_H
 #define NANCY_CHEAT_H
 
diff --git a/engines/nancy/graphics.cpp b/engines/nancy/graphics.cpp
index 06db2426fb..b605f6c02e 100644
--- a/engines/nancy/graphics.cpp
+++ b/engines/nancy/graphics.cpp
@@ -219,7 +219,7 @@ void GraphicsManager::copyToManaged(void *src, Graphics::ManagedSurface &dst, ui
 }
 
 const Graphics::PixelFormat &GraphicsManager::getInputPixelFormat() {
-    if (NanEngine._gameDescription->desc.flags & NGF_8BITCOLOR) {
+    if (NanEngine.getGameFlags() & NGF_8BITCOLOR) {
         return clut8Format;
     } else {
         return inputPixelFormat;
@@ -227,7 +227,7 @@ const Graphics::PixelFormat &GraphicsManager::getInputPixelFormat() {
 }
 
 uint GraphicsManager::getTransColor() {
-    if (NanEngine._gameDescription->desc.flags & NGF_8BITCOLOR) {
+    if (NanEngine.getGameFlags() & NGF_8BITCOLOR) {
         return 1; // If this isn't correct, try picking the pixel at [0, 0] inside the palette bitmap
     } else {
         return inputPixelFormat.ARGBToColor(0, 0, 255, 0);
diff --git a/engines/nancy/input.h b/engines/nancy/input.h
index 048b7359fb..b4d348fdaf 100644
--- a/engines/nancy/input.h
+++ b/engines/nancy/input.h
@@ -44,10 +44,10 @@ class State;
 struct NancyInput {
     enum InputType : uint16 {
         kLeftMouseButtonDown    = 1 << 0,
-        kLeftMouseButtonHeld    = 1 << 1, // True while button is held
+        kLeftMouseButtonHeld    = 1 << 1,
         kLeftMouseButtonUp      = 1 << 2,
         kRightMouseButtonDown   = 1 << 3,
-        kRightMouseButtonHeld   = 1 << 4, // True while button is held
+        kRightMouseButtonHeld   = 1 << 4,
         kRightMouseButtonUp     = 1 << 5,
         kMoveUp                 = 1 << 6,
         kMoveDown               = 1 << 7,
@@ -70,6 +70,7 @@ struct NancyInput {
 // which can then be pulled by interested classes through getInput()
 class InputManager {
     friend class NancyConsole;
+
 enum NancyAction {
     kNancyActionMoveUp,
     kNancyActionMoveDown,
diff --git a/engines/nancy/metaengine.cpp b/engines/nancy/metaengine.cpp
index 3284e9bda3..f403227422 100644
--- a/engines/nancy/metaengine.cpp
+++ b/engines/nancy/metaengine.cpp
@@ -33,23 +33,6 @@
 
 #include "engines/advancedDetector.h"
 
-namespace Nancy {
-
-uint32 NancyEngine::getFeatures() const {
-	return _gameDescription->desc.flags;
-}
-
-const char *NancyEngine::getGameId() const {
-	return _gameDescription->desc.gameId;
-}
-
-void NancyEngine::initGame(const NancyGameDescription *gd) {
-	_gameType = gd->gameType;
-	_platform = gd->desc.platform;
-}
-
-} // End of namespace Nancy
-
 class NancyMetaEngine : public AdvancedMetaEngine {
 public:
     const char *getName() const override {
@@ -85,7 +68,6 @@ bool NancyMetaEngine::hasFeature(MetaEngineFeature f) const {
 Common::Error NancyMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *gd) const {
 	if (gd) {
 		*engine = Nancy::NancyEngine::create(((const Nancy::NancyGameDescription *)gd)->gameType, syst, (const Nancy::NancyGameDescription *)gd);
-		((Nancy::NancyEngine *)*engine)->initGame((const Nancy::NancyGameDescription *)gd);
 	}
 	if (gd) {
 		return Common::kNoError;
diff --git a/engines/nancy/module.mk b/engines/nancy/module.mk
index 0cc1fdc652..6a750e6dad 100644
--- a/engines/nancy/module.mk
+++ b/engines/nancy/module.mk
@@ -2,7 +2,7 @@ MODULE := engines/nancy
 
 MODULE_OBJS = \
   action/actionmanager.o \
-  action/arfactory_v1.o \
+  action/arfactory.o \
   action/leverpuzzle.o \
   action/orderingpuzzle.o \
   action/passwordpuzzle.o \
diff --git a/engines/nancy/nancy.cpp b/engines/nancy/nancy.cpp
index 87626f2b60..d4926b8cee 100644
--- a/engines/nancy/nancy.cpp
+++ b/engines/nancy/nancy.cpp
@@ -68,8 +68,8 @@ NancyEngine::NancyEngine(OSystem *syst, const NancyGameDescription *gd) : Engine
 	DebugMan.addDebugChannel(kDebugScene, "Scene", "Scene debug level");
 
 	_console = new NancyConsole();
-	_rnd = new Common::RandomSource("Nancy");
-	_rnd->setSeed(_rnd->getSeed());
+	randomSource = new Common::RandomSource("Nancy");
+	randomSource->setSeed(randomSource->getSeed());
 
 	input = new InputManager();
 	sound = new SoundManager();
@@ -83,17 +83,52 @@ NancyEngine::~NancyEngine() {
 	clearBootChunks();
 	DebugMan.clearAllDebugChannels();
 	delete _console;
-	delete _rnd;
+	delete randomSource;
 
 	delete graphicsManager;
 	delete input;
 	delete sound;
 }
 
+NancyEngine *NancyEngine::create(GameType type, OSystem *syst, const NancyGameDescription *gd) {
+	switch (type) {
+	case kGameTypeVampire:
+		return new NancyEngine(syst, gd);
+	case kGameTypeNancy1:
+		return new NancyEngine(syst, gd);
+	case kGameTypeNancy2:
+		return new NancyEngine(syst, gd);
+	case kGameTypeNancy3:
+		return new NancyEngine(syst, gd);
+	default:
+		error("Unknown GameType");
+	}
+}
+
 GUI::Debugger *NancyEngine::getDebugger() {
 	return _console;
 }
 
+Common::Error NancyEngine::loadGameStream(Common::SeekableReadStream *stream) {
+	Common::Serializer ser(stream, nullptr);
+	return synchronize(ser);
+}
+
+Common::Error NancyEngine::saveGameStream(Common::WriteStream *stream, bool isAutosave) {
+	Common::Serializer ser(nullptr, stream);
+
+	return synchronize(ser);
+}
+
+bool NancyEngine::canLoadGameStateCurrently()  {
+	return canSaveGameStateCurrently();
+}
+
+bool NancyEngine::canSaveGameStateCurrently() {
+	// TODO also disable during secondary movie
+	return Action::PlayPrimaryVideoChan0::activePrimaryVideo == nullptr;
+}
+
 bool NancyEngine::hasFeature(EngineFeature f) const {
 	return (f == kSupportsReturnToLauncher) || (f == kSupportsLoadingDuringRuntime) || (f == kSupportsSavingDuringRuntime);
 }
@@ -102,12 +137,101 @@ const char *NancyEngine::getCopyrightString() const {
 	return "Copyright 1989-1997 David P Gray, All Rights Reserved.";
 }
 
+uint32 NancyEngine::getGameFlags() const {
+	return _gameDescription->desc.flags;
+}
+
+const char *NancyEngine::getGameId() const {
+	return _gameDescription->desc.gameId;
+}
+
 GameType NancyEngine::getGameType() const {
-	return _gameType;
+	return _gameDescription->gameType;
 }
 
 Common::Platform NancyEngine::getPlatform() const {
-	return _platform;
+	return _gameDescription->desc.platform;
+}
+
+void NancyEngine::setState(GameState state, GameState overridePrevious) {
+	// Handle special cases first
+	switch (state) {
+	case kBoot:
+		bootGameEngine();
+		setState(kLogo);
+		return;
+	case kMainMenu:
+		if (_gameFlow.currentState) {
+			if (_gameFlow.currentState->onStateExit()) {
+				_gameFlow.currentState = nullptr;
+			}
+		}
+		
+		// TODO until the game's own menus are implemented we simply open the GMM
+		openMainMenuDialog();
+
+		if (shouldQuit()) {
+			return;
+		}
+
+		if (_gameFlow.currentState) {
+			_gameFlow.currentState->onStateEnter();
+		}
+
+		return;
+	case kCheat:
+		if (_cheatTypeIsEventFlag) {
+			EventFlagDialog *dialog = new EventFlagDialog();
+			runDialog(*dialog);
+			delete dialog;
+		} else {
+			CheatDialog *dialog = new CheatDialog();
+			runDialog(*dialog);
+			delete dialog;
+		}
+		input->forceCleanInput();
+		return;
+	default:
+		break;
+	}
+
+	graphicsManager->clearObjects();
+
+	_gameFlow.previousState = _gameFlow.currentState;
+	_gameFlow.currentState = getStateObject(state);
+
+	if (_gameFlow.previousState) {
+		_gameFlow.previousState->onStateExit();
+	}
+
+	if (_gameFlow.currentState) {
+		_gameFlow.currentState->onStateEnter();
+	}
+
+	if (overridePrevious != kNone) {
+		_gameFlow.previousState = getStateObject(state);
+	}
+}
+
+void NancyEngine::setPreviousState() {
+	if (_gameFlow.currentState) {
+		_gameFlow.currentState->onStateExit();
+	}
+
+	if (_gameFlow.previousState) {
+		_gameFlow.previousState->onStateEnter();
+	}
+
+	SWAP<Nancy::State::State *>(_gameFlow.currentState, _gameFlow.previousState);
+}
+
+void NancyEngine::setMouseEnabled(bool enabled) {
+	cursorManager->showCursor(enabled); input->setMouseInputEnabled(enabled);
+}
+
+void NancyEngine::callCheatMenu(bool eventFlags)
+{ 
+	setState(kCheat), _cheatTypeIsEventFlag = eventFlags;
 }
 
 Common::Error NancyEngine::run() {
@@ -253,35 +377,6 @@ Common::SeekableReadStream *NancyEngine::getBootChunkStream(const Common::String
 	else return nullptr;
 }
 
-void NancyEngine::stopAndUnloadSpecificSounds() {
-	// TODO missing if
-	
-	sound->stopSound(NancyLogoState.MSNDchannelID);
-
-	for (uint i = 0; i < 10; ++i) {
-		sound->stopSound(i);
-	}
-}
-void NancyEngine::setMouseEnabled(bool enabled) {
-	cursorManager->showCursor(enabled); input->setMouseInputEnabled(enabled);
-}
-
-Common::Error NancyEngine::loadGameStream(Common::SeekableReadStream *stream) {
-	Common::Serializer ser(stream, nullptr);
-	return synchronize(ser);
-}
-
-bool NancyEngine::canSaveGameStateCurrently() {
-	// TODO also disable during secondary movie
-	return Action::PlayPrimaryVideoChan0::activePrimaryVideo == nullptr;
-}
-
-Common::Error NancyEngine::saveGameStream(Common::WriteStream *stream, bool isAutosave) {
-	Common::Serializer ser(nullptr, stream);
-
-	return synchronize(ser);
-}
-
 void NancyEngine::clearBootChunks() {
 	for (auto const& i : _bootChunks) {
 		delete i._value;
@@ -289,26 +384,6 @@ void NancyEngine::clearBootChunks() {
 	_bootChunks.clear();
 }
 
-Common::Error NancyEngine::synchronize(Common::Serializer &ser) {
-	Common::SeekableReadStream *bsum = getBootChunkStream("BSUM");
-	bsum->seek(0);
-
-	// Sync boot summary header, which includes full game title
-	ser.syncVersion(kSavegameVersion);
-	char buf[90];
-	bsum->read(buf, 90);
-	ser.matchBytes(buf, 90);
-
-	// Sync scene and action records
-	NancySceneState.synchronize(ser);
-	NancySceneState._actionManager.synchronize(ser);
-
-	// Sync any action record-related data
-	Action::SliderPuzzle::synchronize(ser);
-
-	return Common::kNoError;
-}
-
 void NancyEngine::preloadCals(const IFF &boot) {
 	const byte *buf;
 	uint size;
@@ -338,19 +413,6 @@ void NancyEngine::preloadCals(const IFF &boot) {
 		debugC(1, kDebugEngine, "No PCAL chunk found");
 }
 
-void NancyEngine::syncSoundSettings() {
-	Engine::syncSoundSettings();
-
-//	_sound->syncVolume();
-}
-
-Common::String NancyEngine::readFilename(Common::ReadStream *stream) const {
-	char buf[kMaxFilenameLen + 1];
-	int read = stream->read(buf, getFilenameLen());
-	buf[read] = 0;
-	return Common::String(buf);
-}
-
 void NancyEngine::readChunkList(const IFF &boot, Common::Serializer &ser, const Common::String &prefix) {
 	byte numChunks;
 	ser.syncAsByte(numChunks);
@@ -360,78 +422,6 @@ void NancyEngine::readChunkList(const IFF &boot, Common::Serializer &ser, const
 	}
 }
 
-void NancyEngine::setState(GameState state, GameState overridePrevious) {
-	// Handle special cases first
-	switch (state) {
-	case kBoot:
-		bootGameEngine();
-		setState(kLogo);
-		return;
-	case kMainMenu:
-		if (_gameFlow.currentState) {
-			if (_gameFlow.currentState->onStateExit()) {
-				_gameFlow.currentState = nullptr;
-			}
-		}
-		
-		// TODO until the game's own menus are implemented we simply open the GMM
-		openMainMenuDialog();
-
-		if (shouldQuit()) {
-			return;
-		}
-
-		if (_gameFlow.currentState) {
-			_gameFlow.currentState->onStateEnter();
-		}
-
-		return;
-	case kCheat:
-		if (_cheatTypeIsEventFlag) {
-			EventFlagDialog *dialog = new EventFlagDialog();
-			runDialog(*dialog);
-			delete dialog;
-		} else {
-			CheatDialog *dialog = new CheatDialog();
-			runDialog(*dialog);
-			delete dialog;
-		}
-		input->forceCleanInput();
-		return;
-	default:
-		break;
-	}
-
-	graphicsManager->clearObjects();
-
-	_gameFlow.previousState = _gameFlow.currentState;
-	_gameFlow.currentState = getStateObject(state);
-
-	if (_gameFlow.previousState) {
-		_gameFlow.previousState->onStateExit();
-	}
-
-	if (_gameFlow.currentState) {
-		_gameFlow.currentState->onStateEnter();
-	}
-
-	if (overridePrevious != kNone) {
-		_gameFlow.previousState = getStateObject(state);
-	}
-}
-
-void NancyEngine::setPreviousState() {
-	if (_gameFlow.currentState) {
-		_gameFlow.currentState->onStateExit();
-	}
-
-	if (_gameFlow.previousState) {
-		_gameFlow.previousState->onStateEnter();
-	}
-
-	SWAP<Nancy::State::State *>(_gameFlow.currentState, _gameFlow.previousState);
-}
-
 void NancyEngine::readBootSummary(const IFF &boot) {
 	Common::SeekableReadStream *bsum = getBootChunkStream("BSUM");
 	bsum->seek(0);
@@ -463,7 +453,7 @@ void NancyEngine::readBootSummary(const IFF &boot) {
 	ser.skip(0x99, kGameTypeNancy1, kGameTypeNancy1);
 	int16 time = 0;
 	ser.syncAsSint16LE(time, kGameTypeNancy1, kGameTypeNancy1);
-	NancySceneState.playerTimeMinuteLength = time;
+	playerTimeMinuteLength = time;
 	ser.skip(2, kGameTypeNancy1, kGameTypeNancy1);
     ser.syncAsByte(overrideMovementTimeDeltas, kGameTypeNancy1, kGameTypeNancy1);
 
@@ -475,19 +465,24 @@ void NancyEngine::readBootSummary(const IFF &boot) {
     }
 }
 
-NancyEngine *NancyEngine::create(GameType type, OSystem *syst, const NancyGameDescription *gd) {
-	switch (type) {
-	case kGameTypeVampire:
-		return new NancyEngine(syst, gd);
-	case kGameTypeNancy1:
-		return new NancyEngine(syst, gd);
-	case kGameTypeNancy2:
-		return new NancyEngine(syst, gd);
-	case kGameTypeNancy3:
-		return new NancyEngine(syst, gd);
-	default:
-		error("Unknown GameType");
-	}
+Common::Error NancyEngine::synchronize(Common::Serializer &ser) {
+	Common::SeekableReadStream *bsum = getBootChunkStream("BSUM");
+	bsum->seek(0);
+
+	// Sync boot summary header, which includes full game title
+	ser.syncVersion(kSavegameVersion);
+	char buf[90];
+	bsum->read(buf, 90);
+	ser.matchBytes(buf, 90);
+
+	// Sync scene and action records
+	NancySceneState.synchronize(ser);
+	NancySceneState._actionManager.synchronize(ser);
+
+	// Sync any action record-related data
+	Action::SliderPuzzle::synchronize(ser);
+
+	return Common::kNoError;
 }
 
 } // End of namespace Nancy
diff --git a/engines/nancy/nancy.h b/engines/nancy/nancy.h
index b58f4b8def..9f30cde470 100644
--- a/engines/nancy/nancy.h
+++ b/engines/nancy/nancy.h
@@ -39,11 +39,16 @@ class Serializer;
 /**
  * This is the namespace of the Nancy engine.
  *
- * Status of this engine: ???
+ * Status of this engine:
+ * Nancy Drew: Secrets can kill is completable,
+ * with a few minor graphical glitches;
+ * The Vampire Diaries boots, but crashes almost immediately;
+ * every other game is untested but definitely unplayable
  *
  * Games using this engine:
- * - Nancy Drew 1
- * - ...
+ *  - The Vampire Diaries (1996)
+ *  - Almost every mainline Nancy Drew game by HeR Interactive,
+ * 	up to and including Nancy Drew: Sea of Darkness (2015)
  */
 namespace Nancy {
 
@@ -77,7 +82,6 @@ class Credits;
 class NancyEngine : public Engine {
 public:
 	friend class NancyConsole;
-	friend class State::Logo; // TODO
 
 	enum GameState {
 		kBoot,
@@ -103,41 +107,33 @@ public:
 	NancyEngine(OSystem *syst, const NancyGameDescription *gd);
 	~NancyEngine();
 
+	static NancyEngine *create(GameType type, OSystem *syst, const NancyGameDescription *gd);
+
 	GUI::Debugger *getDebugger();
 
-	uint32 getFeatures() const;
-	const char *getGameId() const;
+	virtual bool hasFeature(EngineFeature f) const override;
 
-	void initGame(const NancyGameDescription *gd);
+	virtual Common::Error loadGameStream(Common::SeekableReadStream *stream) override;
+	virtual Common::Error saveGameStream(Common::WriteStream *stream, bool isAutosave = false) override;
+	virtual bool canLoadGameStateCurrently() override;
+	virtual bool canSaveGameStateCurrently() override;
 
+	const char *getCopyrightString() const;
+	uint32 getGameFlags() const;
+	const char *getGameId() const;
 	GameType getGameType() const;
 	Common::Platform getPlatform() const;
 
-	bool hasFeature(EngineFeature f) const;
-	const char *getCopyrightString() const;
-
-	void syncSoundSettings();
-
-	static NancyEngine *create(GameType type, OSystem *syst, const NancyGameDescription *gd);
-
-	// Chunks found in BOOT get extracted and cached at startup, this function lets other classes access them
-	Common::SeekableReadStream *getBootChunkStream(const Common::String &name);
-
-	// Used for state switching
-	void stopAndUnloadSpecificSounds();
-	
 	void setState(GameState state, GameState overridePrevious = kNone);
 	State::State *getState() { return _gameFlow.currentState; }
 	void setPreviousState();
 
-	void callCheatMenu(bool eventFlags) { setState(kCheat), _cheatTypeIsEventFlag = eventFlags; }
-
+	// Chunks found in BOOT get extracted and cached at startup, this function lets other classes access them
+	Common::SeekableReadStream *getBootChunkStream(const Common::String &name);
+	
 	void setMouseEnabled(bool enabled);
 
-	virtual Common::Error loadGameStream(Common::SeekableReadStream *stream) override;
-	virtual Common::Error saveGameStream(Common::WriteStream *stream, bool isAutosave = false) override;
-	virtual bool canLoadGameStateCurrently() override { return canSaveGameStateCurrently(); };
-	virtual bool canSaveGameStateCurrently() override;
+	void callCheatMenu(bool eventFlags);
 
 	// Managers
 	ResourceManager *resource;
@@ -146,9 +142,8 @@ public:
 	InputManager *input;
 	SoundManager *sound;
 	
-	OSystem *_system;
-	Common::RandomSource *_rnd;
-	const NancyGameDescription *_gameDescription;
+	Common::RandomSource *randomSource;
+
 	bool launchConsole;
 	
 	uint16 firstSceneID;
@@ -157,9 +152,14 @@ public:
 	bool overrideMovementTimeDeltas;
 	Time slowMovementTimeDelta;
 	Time fastMovementTimeDelta;
+	Time playerTimeMinuteLength;
+
+private:
+	struct GameFlow {
+		State::State *currentState = nullptr;
+		State::State *previousState = nullptr;
+	};
 
-protected:
-	// Engine APIs
 	Common::Error run();
 
 	void bootGameEngine();
@@ -168,37 +168,22 @@ protected:
 
 	bool addBootChunk(const Common::String &name, Common::SeekableReadStream *stream);
 	void clearBootChunks();
+	
+	void preloadCals(const IFF &boot);
+	void readChunkList(const IFF &boot, Common::Serializer &ser, const Common::String &prefix);
 
-	Common::Error synchronize(Common::Serializer &serializer);
-
-	enum {
-		kMaxFilenameLen = 32
-	};
-
-	struct Image {
-		Common::String name;
-		uint16 width;
-		uint16 height;
-	};
+	void readBootSummary(const IFF &boot);
 
-	struct GameFlow {
-		State::State *currentState = nullptr;
-		State::State *previousState = nullptr;
-	};
+	Common::Error synchronize(Common::Serializer &serializer);	
 
 	bool _cheatTypeIsEventFlag;
 
-	void preloadCals(const IFF &boot);
-	void readChunkList(const IFF &boot, Common::Serializer &ser, const Common::String &prefix);
-	Common::String readFilename(Common::ReadStream *stream) const;
-
-	virtual uint getFilenameLen() const { return 9; };
-	virtual void readBootSummary(const IFF &boot);
-
-private:
 	GameFlow _gameFlow;
+	OSystem *_system;
 
 	NancyConsole *_console;
+
+	const NancyGameDescription *_gameDescription;
 	GameType _gameType;
 	Common::Platform _platform;
 
diff --git a/engines/nancy/renderobject.h b/engines/nancy/renderobject.h
index 6393b77887..221eaae754 100644
--- a/engines/nancy/renderobject.h
+++ b/engines/nancy/renderobject.h
@@ -53,8 +53,8 @@ public:
 
     virtual ~RenderObject();
 
-    virtual void init(); // Does _not_ get called automatically
-    virtual void registerGraphics();
+    virtual void init(); // Does not get called automatically
+    virtual void registerGraphics(); // Does not get called automatically
     virtual void updateGraphics() {}
 
     void moveTo(Common::Point position);
diff --git a/engines/nancy/resource.h b/engines/nancy/resource.h
index b635ee7a01..e6eb51e4f5 100644
--- a/engines/nancy/resource.h
+++ b/engines/nancy/resource.h
@@ -75,9 +75,8 @@ public:
 	byte *loadCif(const Common::String &treeName, const Common::String &name, uint &size);
 	bool exportCif(const Common::String &treeName, const Common::String &name);
 	Common::String getCifDescription(const Common::String &treeName, const Common::String &name);
-private:
-	Decompressor *_dec;
 
+private:
 	byte *getCifData(const Common::String &name, CifInfo &info, uint *size = nullptr);
 	byte *getCifData(const Common::String &treeName, const Common::String &name, CifInfo &info, uint *size = nullptr);
 	bool getCifInfo(const Common::String &name, CifInfo &info);
@@ -85,6 +84,7 @@ private:
 	const CifTree *findCifTree(const Common::String &name) const;
 
 	Common::Array<const CifTree *> _cifTrees;
+	Decompressor *_dec;
 };
 
 } // End of namespace Nancy
diff --git a/engines/nancy/sound.cpp b/engines/nancy/sound.cpp
index dd8f1e0873..407576f78e 100644
--- a/engines/nancy/sound.cpp
+++ b/engines/nancy/sound.cpp
@@ -177,7 +177,7 @@ Audio::SeekableAudioStream *SoundManager::makeHISStream(Common::SeekableReadStre
 }
 
 SoundManager::SoundManager() {
-	_mixer = NanEngine._system->getMixer();
+	_mixer = g_system->getMixer();
 
 	initSoundChannels();
 }
@@ -234,7 +234,7 @@ void SoundManager::pauseSound(uint16 channelID, bool pause) {
 		return;
 
 	if (isSoundPlaying(channelID)) {
-		NanEngine._system->getMixer()->pauseHandle(_channels[channelID].handle, pause);
+		g_system->getMixer()->pauseHandle(_channels[channelID].handle, pause);
 	}
 }
 
@@ -284,6 +284,14 @@ void SoundManager::stopAllSounds() {
 	}
 }
 
+void SoundManager::stopAndUnloadSpecificSounds() {
+	// TODO missing if
+
+	for (uint i = 0; i < 10; ++i) {
+		stopSound(i);
+	}
+}
+
 void SoundManager::initSoundChannels() {
 	// Original engine hardcoded these and so do we
 	_channels[7].type = Audio::Mixer::kSpeechSoundType;
diff --git a/engines/nancy/sound.h b/engines/nancy/sound.h
index ce53cfb25c..314beed56c 100644
--- a/engines/nancy/sound.h
+++ b/engines/nancy/sound.h
@@ -52,13 +52,19 @@ public:
 
     void playSound(uint16 channelID);
     void playSound(const SoundDescription &description);
+
     void pauseSound(uint16 channelID, bool pause);
     void pauseSound(const SoundDescription &description, bool pause);
+
     bool isSoundPlaying(uint16 channelID);
     bool isSoundPlaying(const SoundDescription &description);
+
     void stopSound(uint16 channelID);
     void stopSound(const SoundDescription &description);
     void stopAllSounds();
+    
+    // Used when changing scenes
+    void stopAndUnloadSpecificSounds();
 
     static Audio::SeekableAudioStream *makeHISStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse);
 
diff --git a/engines/nancy/state/logo.cpp b/engines/nancy/state/logo.cpp
index d3fa10c4cb..4b19259741 100644
--- a/engines/nancy/state/logo.cpp
+++ b/engines/nancy/state/logo.cpp
@@ -60,6 +60,12 @@ void Logo::process() {
 	}
 }
 
+bool Logo::onStateExit() {
+	NanEngine.sound->stopSound(_msnd);
+	destroy();
+	return true;
+}
+
 void Logo::init() {
 	Common::SeekableReadStream *lg = NanEngine.getBootChunkStream("LG0");
 	lg->seek(0);
@@ -71,19 +77,16 @@ void Logo::init() {
 }
 
 void Logo::startSound() {
-	SoundDescription desc;
-	desc.read(*NanEngine.getBootChunkStream("MSND"), SoundDescription::kMenu);
-	NanEngine.sound->loadSound(desc);
-	MSNDchannelID = desc.channelID;
-
-	NanEngine.sound->playSound(MSNDchannelID);
+	_msnd.read(*NanEngine.getBootChunkStream("MSND"), SoundDescription::kMenu);
+	NanEngine.sound->loadSound(_msnd);
+	NanEngine.sound->playSound(_msnd);
 
-	_startTicks = NanEngine._system->getMillis();
+	_startTicks = g_system->getMillis();
 	_state = kRun;
 }
 
 void Logo::run() {
-	if (NanEngine._system->getMillis() - _startTicks >= 7000 || (NanEngine.input->getInput().input & NancyInput::kLeftMouseButtonDown)) {
+	if (g_system->getMillis() - _startTicks >= 7000 || (NanEngine.input->getInput().input & NancyInput::kLeftMouseButtonDown)) {
 		_state = kStop;
 	}
 }
@@ -93,10 +96,9 @@ void Logo::stop() {
 	// For the N+C key combo it looks for some kind of cheat file
 	// to initialize the game state with.
 
-	NanEngine.sound->stopSound(MSNDchannelID);
+	NanEngine.sound->stopSound(_msnd);
 
 	NanEngine.setState(NancyEngine::kScene);
-	NanEngine._system->fillScreen(0);
 }
 
 } // End of namespace State
diff --git a/engines/nancy/state/logo.h b/engines/nancy/state/logo.h
index 19ac0365a3..30096c4b25 100644
--- a/engines/nancy/state/logo.h
+++ b/engines/nancy/state/logo.h
@@ -45,9 +45,7 @@ public:
 		
 	// State API
     virtual void process() override;
-    virtual bool onStateExit() override { destroy(); return true; };
-
-	uint MSNDchannelID; // This definitely shouldn't be here
+    virtual bool onStateExit() override;
 
 private:
 	void init();
@@ -65,6 +63,7 @@ private:
 	State _state;
 	uint _startTicks;
 	UI::FullScreenImage _logoImage;
+	SoundDescription _msnd;
 };
 
 #define NancyLogoState Nancy::State::Logo::instance()
diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index 9d1e93c180..6763b40720 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -48,7 +48,48 @@ DECLARE_SINGLETON(Nancy::State::Scene);
 }
 
 namespace Nancy {
-namespace State{
+namespace State {
+
+void Scene::SceneSummary::read(Common::SeekableReadStream &stream) {
+    char *buf = new char[0x32];
+
+    stream.seek(0);
+    Common::Serializer ser(&stream, nullptr);
+    ser.setVersion(NanEngine.getGameType());
+
+    ser.syncBytes((byte *)buf, 0x32);
+    description = Common::String(buf);
+
+    ser.syncBytes((byte *)buf, 10);
+    buf[9] = 0;
+    videoFile = Common::String(buf);
+
+    // skip 2 unknown bytes
+    ser.skip(2);
+    videoFormat = stream.readUint16LE();
+
+    // Load the palette data in The Vampire Diaries
+    ser.skip(4, kGameTypeVampire, kGameTypeVampire);
+    ser.syncBytes((byte *)buf, 10, kGameTypeVampire, kGameTypeVampire);
+    videoPaletteFile = buf;
+    ser.skip(0x14, kGameTypeVampire, kGameTypeVampire);
+
+    sound.read(stream, SoundDescription::kScene);
+
+    ser.skip(0x10);
+    ser.syncAsUint16LE(verticalScrollDelta);
+    ser.syncAsUint16LE(horizontalEdgeSize);
+    ser.syncAsUint16LE(verticalEdgeSize);
+    ser.syncAsUint16LE((uint32 &)slowMoveTimeDelta);
+    ser.syncAsUint16LE((uint32 &)fastMoveTimeDelta);
+
+    if (NanEngine.overrideMovementTimeDeltas) {
+        slowMoveTimeDelta = NanEngine.slowMovementTimeDelta;
+        fastMoveTimeDelta = NanEngine.fastMovementTimeDelta;
+    }
+
+    delete[] buf;
+}
 
 void Scene::process() {
     switch (_state) {
@@ -66,7 +107,7 @@ void Scene::process() {
     case kStartSound:
         _state = kRun;
         if (!_sceneState._doNotStartSound) {
-            NanEngine.stopAndUnloadSpecificSounds();
+            NanEngine.sound->stopAndUnloadSpecificSounds();
             NanEngine.sound->loadSound(_sceneState.summary.sound);
             NanEngine.sound->playSound(_sceneState.summary.sound);
         }
@@ -129,7 +170,7 @@ void Scene::popScene() {
 }
 
 void Scene::pauseSceneSpecificSounds() {
-    // TODO missing if, same condition as the one in NancyEngine::stopAndUnloadSpecificSounds
+    // TODO missing if, same condition as the one in SoundManager::stopAndUnloadSpecificSounds
 
     for (uint i = 0; i < 10; ++i) {
 		NanEngine.sound->pauseSound(i, true);
@@ -161,6 +202,57 @@ void Scene::removeItemFromInventory(uint16 id, bool pickUp) {
     _inventoryBox.removeItem(id);
 }
 
+void Scene::setEventFlag(int16 label, NancyFlag flag) {
+    if (label > -1) {
+        _flags.eventFlags[label] = flag;
+    }
+}
+
+void Scene::setEventFlag(EventFlagDescription eventFlag) {
+    setEventFlag(eventFlag.label, eventFlag.flag);
+}
+
+bool Scene::getEventFlag(int16 label, NancyFlag flag) const {
+    if (label > -1) {
+        return _flags.eventFlags[label] == flag;
+    } else {
+        return false;
+    }
+}
+
+bool Scene::getEventFlag(EventFlagDescription eventFlag) const {
+    return getEventFlag(eventFlag.label, eventFlag.flag);
+}
+
+void Scene::setLogicCondition(int16 label, NancyFlag flag) {
+    if (label > -1) {
+        _flags.logicConditions[label].flag = flag;
+        _flags.logicConditions[label].timestamp = NanEngine.getTotalPlayTime();
+    }
+}
+
+bool Scene::getLogicCondition(int16 label, NancyFlag flag) const {
+    if (label > -1) {
+        return _flags.logicConditions[label].flag == flag;
+    } else {
+        return false;
+    }
+}
+
+void Scene::clearLogicConditions() {
+    for (auto &cond : _flags.logicConditions) {
+        cond.flag = kFalse;
+        cond.timestamp = 0;
+    }
+}
+
+void Scene::useHint(int hintID, int hintWeight) {
+    if (_lastHint != hintID) {
+        _hintsRemaining[_difficulty] += hintWeight;
+        _lastHint = hintID;
+    }
+}
+
 void Scene::registerGraphics() {
     _frame.registerGraphics();
     _viewport.registerGraphics();
@@ -242,7 +334,7 @@ void Scene::synchronize(Common::Serializer &ser) {
 	// TODO hardcoded number of event flags
 	ser.syncArray(_flags.eventFlags, 168, Common::Serializer::Byte);
 	
-	ser.syncArray<uint16>(_sceneState.sceneHitCount, (uint16)2001, Common::Serializer::Uint16LE);
+	ser.syncArray<uint16>(_flags.sceneHitCount, (uint16)2001, Common::Serializer::Uint16LE);
 
 	ser.syncAsUint16LE(_difficulty);
 	ser.syncArray<uint16>(_hintsRemaining.data(), _hintsRemaining.size(), Common::Serializer::Uint16LE);
@@ -256,7 +348,7 @@ void Scene::init() {
 
     // Does this ever get used?
     for (uint i = 0; i < 2001; ++i) {
-        _sceneState.sceneHitCount[i] = 0;
+        _flags.sceneHitCount[i] = 0;
     }
 
     for (uint i = 0; i < 11; ++i) {
@@ -308,36 +400,6 @@ void Scene::init() {
     NanEngine.graphicsManager->redrawAll();
 }
 
-void Scene::initStaticData() {
-    Common::SeekableReadStream *chunk = NanEngine.getBootChunkStream("MAP");
-    chunk->seek(0x8A);
-    readRect(*chunk, _mapHotspot);
-
-    // Hardcoded by original engine
-    _mapAccessSceneIDs.clear();
-    _mapAccessSceneIDs.push_back(9);
-    _mapAccessSceneIDs.push_back(10);
-    _mapAccessSceneIDs.push_back(11);
-    _mapAccessSceneIDs.push_back(0x4B0);
-    _mapAccessSceneIDs.push_back(0x378);
-    _mapAccessSceneIDs.push_back(0x29A);
-    _mapAccessSceneIDs.push_back(0x4E2);
-    _mapAccessSceneIDs.push_back(0x682);
-
-    Common::SeekableReadStream *fr = NanEngine.getBootChunkStream("FR0");
-    fr->seek(0);
-
-    _frame.init(fr->readString());
-    _viewport.init();
-    _textbox.init();
-    _inventoryBox.init();
-    _menuButton.init();
-    _helpButton.init();
-    NanEngine.cursorManager->showCursor(true);
-
-    _state = kLoad;
-}
-
 void Scene::load() {
     clearSceneData();
 
@@ -392,7 +454,7 @@ void Scene::load() {
     _sceneState.currentScene.frameID = _sceneState.nextScene.frameID;
 
     if (_sceneState.summary.videoFormat == 1) {
-        // TODO not sure this ever gets hit
+        // TODO
     } else if (_sceneState.summary.videoFormat == 2) {
         // always start from the bottom
         _sceneState.currentScene.verticalOffset = _viewport.getMaxScroll();
@@ -400,10 +462,8 @@ void Scene::load() {
         error("Unrecognized Scene summary chunk video file format");
     }
 
-    // Some checks against rFrame
-
     if (_sceneState.summary.videoFormat == 1) {
-        // TODO not sure this ever gets hit
+        // TODO
     } else if (_sceneState.summary.videoFormat == 2) {
         if (_viewport.getMaxScroll() == 0) {
             _viewport.disableEdges(kUp | kDown);
@@ -417,11 +477,11 @@ void Scene::load() {
 }
 
 void Scene::run() {
-    if (isComingFromMenu) {
+    if (_isComingFromMenu) {
         // TODO
     }
 
-    isComingFromMenu = false;
+    _isComingFromMenu = false;
 
 
     if (_gameStateRequested != NancyEngine::kNone) {
@@ -444,7 +504,7 @@ void Scene::run() {
     // Calculate the in-game time (playerTime)
     if (currentPlayTime > _timers.playerTimeNextMinute) {
         _timers.playerTime += 60000; // Add a minute
-        _timers.playerTimeNextMinute = currentPlayTime + playerTimeMinuteLength; // Set when we're going to add the next minute
+        _timers.playerTimeNextMinute = currentPlayTime + NanEngine.playerTimeMinuteLength;
     }
 
     // Set the time of day according to playerTime
@@ -484,45 +544,34 @@ void Scene::run() {
     _actionManager.processActionRecords();
 }
 
-void Scene::SceneSummary::read(Common::SeekableReadStream &stream) {
-    char *buf = new char[0x32];
-
-    stream.seek(0);
-    Common::Serializer ser(&stream, nullptr);
-    ser.setVersion(NanEngine._gameDescription->gameType);
-
-    ser.syncBytes((byte *)buf, 0x32);
-    description = Common::String(buf);
-
-    ser.syncBytes((byte *)buf, 10);
-    buf[9] = 0;
-    videoFile = Common::String(buf);
-
-    // skip 2 unknown bytes
-    ser.skip(2);
-    videoFormat = stream.readUint16LE();
-
-    // Load the palette data in The Vampire Diaries
-    ser.skip(4, kGameTypeVampire, kGameTypeVampire);
-    ser.syncBytes((byte *)buf, 10, kGameTypeVampire, kGameTypeVampire);
-    videoPaletteFile = buf;
-    ser.skip(0x14, kGameTypeVampire, kGameTypeVampire);
+void Scene::initStaticData() {
+    Common::SeekableReadStream *chunk = NanEngine.getBootChunkStream("MAP");
+    chunk->seek(0x8A);
+    readRect(*chunk, _mapHotspot);
 
-    sound.read(stream, SoundDescription::kScene);
+    // Hardcoded by original engine
+    _mapAccessSceneIDs.clear();
+    _mapAccessSceneIDs.push_back(9);
+    _mapAccessSceneIDs.push_back(10);
+    _mapAccessSceneIDs.push_back(11);
+    _mapAccessSceneIDs.push_back(0x4B0);
+    _mapAccessSceneIDs.push_back(0x378);
+    _mapAccessSceneIDs.push_back(0x29A);
+    _mapAccessSceneIDs.push_back(0x4E2);
+    _mapAccessSceneIDs.push_back(0x682);
 
-    ser.skip(0x10);
-    ser.syncAsUint16LE(verticalScrollDelta);
-    ser.syncAsUint16LE(horizontalEdgeSize);
-    ser.syncAsUint16LE(verticalEdgeSize);
-    ser.syncAsUint16LE((uint32 &)slowMoveTimeDelta);
-    ser.syncAsUint16LE((uint32 &)fastMoveTimeDelta);
+    Common::SeekableReadStream *fr = NanEngine.getBootChunkStream("FR0");
+    fr->seek(0);
 
-    if (NanEngine.overrideMovementTimeDeltas) {
-        slowMoveTimeDelta = NanEngine.slowMovementTimeDelta;
-        fastMoveTimeDelta = NanEngine.fastMovementTimeDelta;
-    }
+    _frame.init(fr->readString());
+    _viewport.init();
+    _textbox.init();
+    _inventoryBox.init();
+    _menuButton.init();
+    _helpButton.init();
+    NanEngine.cursorManager->showCursor(true);
 
-    delete[] buf;
+    _state = kLoad;
 }
 
 void Scene::clearSceneData() {
@@ -543,56 +592,5 @@ void Scene::clearSceneData() {
     _actionManager.clearActionRecords();
 }
 
-void Scene::setEventFlag(int16 label, NancyFlag flag) {
-    if (label > -1) {
-        _flags.eventFlags[label] = flag;
-    }
-}
-
-void Scene::setEventFlag(EventFlagDescription eventFlag) {
-    setEventFlag(eventFlag.label, eventFlag.flag);
-}
-
-bool Scene::getEventFlag(int16 label, NancyFlag flag) const {
-    if (label > -1) {
-        return _flags.eventFlags[label] == flag;
-    } else {
-        return false;
-    }
-}
-
-bool Scene::getEventFlag(EventFlagDescription eventFlag) const {
-    return getEventFlag(eventFlag.label, eventFlag.flag);
-}
-
-void Scene::setLogicCondition(int16 label, NancyFlag flag) {
-    if (label > -1) {
-        _flags.logicConditions[label].flag = flag;
-        _flags.logicConditions[label].timestamp = NanEngine.getTotalPlayTime();
-    }
-}
-
-bool Scene::getLogicCondition(int16 label, NancyFlag flag) const {
-    if (label > -1) {
-        return _flags.logicConditions[label].flag == flag;
-    } else {
-        return false;
-    }
-}
-
-void Scene::clearLogicConditions() {
-    for (auto &cond : _flags.logicConditions) {
-        cond.flag = kFalse;
-        cond.timestamp = 0;
-    }
-}
-
-void Scene::useHint(int hintID, int hintWeight) {
-    if (_lastHint != hintID) {
-        _hintsRemaining[_difficulty] += hintWeight;
-        _lastHint = hintID;
-    }
-}
-
 } // End of namespace State
 } // End of namespace Nancy
diff --git a/engines/nancy/state/scene.h b/engines/nancy/state/scene.h
index 029f3da1ea..dfb4aa0113 100644
--- a/engines/nancy/state/scene.h
+++ b/engines/nancy/state/scene.h
@@ -64,13 +64,25 @@ struct SceneInfo {
     uint16 verticalOffset = 0;
 };
 
+// The game state that handles all of the gameplay
 class Scene : public State, public Common::Singleton<Scene> {
     friend class Nancy::Action::ActionRecord;
     friend class Nancy::Action::ActionManager;
     friend class Nancy::NancyConsole;
     friend class Nancy::NancyEngine;
     friend class Nancy::CheatDialog;
+
 public:
+    enum GameStateChange : byte {
+        kHelpMenu = 1 << 0,
+        kMainMenu = 1 << 1,
+        kSaveLoad = 1 << 2,
+        kReloadSave = 1 << 3,
+        kSetupMenu = 1 << 4,
+        kCredits = 1 << 5,
+        kMap = 1 << 6
+    };
+
     struct SceneSummary { // SSUM
         Common::String description;
         Common::String videoFile;
@@ -85,7 +97,6 @@ public:
         uint16 verticalEdgeSize;
         Time slowMoveTimeDelta;
         Time fastMoveTimeDelta;
-        // byte unknown7C enum with 4 values
         //
 
         void read(Common::SeekableReadStream &stream);
@@ -93,9 +104,9 @@ public:
 
     Scene() :
         _state (kInit),
-        _frame(),
         _lastHint(-1),
         _gameStateRequested(NancyEngine::kNone),
+        _frame(),
         _viewport(),
         _textbox(_frame),
         _inventoryBox(_frame),
@@ -116,8 +127,6 @@ public:
     void pauseSceneSpecificSounds();
     void unpauseSceneSpecificSounds();
 
-    void setShouldClearTextbox(bool shouldClear) { _shouldClearTextbox = shouldClear; }
-
     void addItemToInventory(uint16 id);
     void removeItemFromInventory(uint16 id, bool pickUp = true);
     int16 getHeldItem() const { return _flags.heldItem; }
@@ -151,6 +160,8 @@ public:
 
     void synchronize(Common::Serializer &serializer);
 
+    void setShouldClearTextbox(bool shouldClear) { _shouldClearTextbox = shouldClear; }
+
     UI::FullScreenImage &getFrame() { return _frame; }
     UI::Viewport &getViewport() { return _viewport; }
     UI::Textbox &getTextbox() { return _textbox; }
@@ -170,7 +181,6 @@ private:
 
     void clearSceneData();
 
-public:
     enum State {
         kInit,
         kLoad,
@@ -178,27 +188,12 @@ public:
         kRun
     };
 
-    enum GameStateChange : byte {
-        kHelpMenu = 1 << 0,
-        kMainMenu = 1 << 1,
-        kSaveLoad = 1 << 2,
-        kReloadSave = 1 << 3,
-        kSetupMenu = 1 << 4,
-        kCredits = 1 << 5,
-        kMap = 1 << 6
-    };
-    
-    // TODO move 
-    Time playerTimeMinuteLength;
-
-protected:
     struct SceneState {
         SceneSummary summary;
         SceneInfo currentScene;
         SceneInfo nextScene;
         SceneInfo pushedScene;
         bool isScenePushed;
-        uint16 sceneHitCount[2001];
 
         bool _doNotStartSound = false;
     };
@@ -223,12 +218,13 @@ protected:
 
         LogicCondition logicConditions[30];
         NancyFlag eventFlags[168];
+        uint16 sceneHitCount[2001];
         NancyFlag items[11];
         int16 heldItem = -1;
         int16 primaryVideoResponsePicked = -1;
     };
 
-    // RenderObjects
+    // UI
     UI::FullScreenImage _frame;
     UI::Viewport _viewport;
     UI::Textbox _textbox;
@@ -252,7 +248,7 @@ protected:
 
     State _state;
 
-    bool isComingFromMenu = true;
+    bool _isComingFromMenu = true;
     bool _shouldClearTextbox = true;
 };
 
diff --git a/engines/nancy/state/state.h b/engines/nancy/state/state.h
index 054c8389c2..7f538fcaf1 100644
--- a/engines/nancy/state/state.h
+++ b/engines/nancy/state/state.h
@@ -28,6 +28,7 @@
 namespace Nancy {
 namespace State {
 
+// Base class for all states. Provides a simple API for handling state switching
 class State {
 public:
     State() {}
@@ -35,9 +36,7 @@ public:
 
     virtual void process() =0;
     virtual void onStateEnter() {}
-
-    // Returns whether the object destroyed itself after exit
-    virtual bool onStateExit() { return true; }
+    virtual bool onStateExit() { return true; } // Returns whether the object destroyed itself after exit
 };
 
 } // End of namespace State


Commit: a286a287f201653d8b769cc7103d6a59308863bc
    https://github.com/scummvm/scummvm/commit/a286a287f201653d8b769cc7103d6a59308863bc
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Replace NanEngine macro

Replaced the nonstandard NanEngine macro with a proper pointer to the current NancyEngine instance.

Changed paths:
    engines/nancy/action/actionmanager.cpp
    engines/nancy/action/leverpuzzle.cpp
    engines/nancy/action/orderingpuzzle.cpp
    engines/nancy/action/passwordpuzzle.cpp
    engines/nancy/action/primaryvideo.cpp
    engines/nancy/action/recordtypes.cpp
    engines/nancy/action/rotatinglockpuzzle.cpp
    engines/nancy/action/secondarymovie.cpp
    engines/nancy/action/sliderpuzzle.cpp
    engines/nancy/action/staticbitmapanim.cpp
    engines/nancy/action/telephone.cpp
    engines/nancy/console.cpp
    engines/nancy/cursor.cpp
    engines/nancy/font.cpp
    engines/nancy/graphics.cpp
    engines/nancy/iff.cpp
    engines/nancy/input.cpp
    engines/nancy/nancy.cpp
    engines/nancy/nancy.h
    engines/nancy/renderobject.cpp
    engines/nancy/state/credits.cpp
    engines/nancy/state/help.cpp
    engines/nancy/state/logo.cpp
    engines/nancy/state/map.cpp
    engines/nancy/state/scene.cpp
    engines/nancy/state/scene.h
    engines/nancy/ui/button.cpp
    engines/nancy/ui/fullscreenimage.cpp
    engines/nancy/ui/inventorybox.cpp
    engines/nancy/ui/scrollbar.cpp
    engines/nancy/ui/textbox.cpp
    engines/nancy/ui/viewport.cpp


diff --git a/engines/nancy/action/actionmanager.cpp b/engines/nancy/action/actionmanager.cpp
index f57a7d1b60..567edf86f0 100644
--- a/engines/nancy/action/actionmanager.cpp
+++ b/engines/nancy/action/actionmanager.cpp
@@ -47,7 +47,7 @@ void ActionManager::handleInput(NancyInput &input) {
         }
 
         if (rec->isActive && rec->hasHotspot && NancySceneState.getViewport().convertViewportToScreen(rec->hotspot).contains(input.mousePos)) {
-            NanEngine.cursorManager->setCursorType(rec->getHoverCursor());
+            g_nancy->cursorManager->setCursorType(rec->getHoverCursor());
 
             if (input.input & NancyInput::kLeftMouseButtonUp) {
                 input.input &= ~NancyInput::kLeftMouseButtonUp;
@@ -69,7 +69,7 @@ void ActionManager::handleInput(NancyInput &input) {
                     }
 
                     if (!shouldTrigger) {
-                        NanEngine.sound->playSound(17); // Hardcoded by original engine
+                        g_nancy->sound->playSound(17); // Hardcoded by original engine
                     }
                 } else {
                     shouldTrigger = true;
diff --git a/engines/nancy/action/leverpuzzle.cpp b/engines/nancy/action/leverpuzzle.cpp
index ac843ae4ab..7cf69f7659 100644
--- a/engines/nancy/action/leverpuzzle.cpp
+++ b/engines/nancy/action/leverpuzzle.cpp
@@ -38,7 +38,7 @@ void LeverPuzzle::init() {
     
     setTransparent(true);
 
-    NanEngine.resource->loadImage(imageName, image);
+    g_nancy->resource->loadImage(imageName, image);
 }
 
 void LeverPuzzle::readData(Common::SeekableReadStream &stream) {
@@ -94,8 +94,8 @@ void LeverPuzzle::execute() {
     case kBegin:
         init();
         registerGraphics();
-        NanEngine.sound->loadSound(moveSound);
-        NanEngine.sound->loadSound(noMoveSound);
+        g_nancy->sound->loadSound(moveSound);
+        g_nancy->sound->loadSound(noMoveSound);
 
         for (uint i = 0; i < 3; ++i) {
             drawLever(i);
@@ -113,21 +113,21 @@ void LeverPuzzle::execute() {
             }
             
             NancySceneState.setEventFlag(flagOnSolve);
-            solveSoundPlayTime = NanEngine.getTotalPlayTime() + solveSoundDelay * 1000;
+            solveSoundPlayTime = g_nancy->getTotalPlayTime() + solveSoundDelay * 1000;
             solveState = kPlaySound;
             break;
         case kPlaySound:
-            if (NanEngine.getTotalPlayTime() <= solveSoundPlayTime) {
+            if (g_nancy->getTotalPlayTime() <= solveSoundPlayTime) {
                 break;
             }
 
-            NanEngine.sound->loadSound(solveSound);
-            NanEngine.sound->playSound(solveSound);
+            g_nancy->sound->loadSound(solveSound);
+            g_nancy->sound->playSound(solveSound);
             solveState = kWaitForSound;
             break;
         case kWaitForSound:
-            if (!NanEngine.sound->isSoundPlaying(solveSound)) {
-                NanEngine.sound->stopSound(solveSound);
+            if (!g_nancy->sound->isSoundPlaying(solveSound)) {
+                g_nancy->sound->stopSound(solveSound);
                 state = kActionTrigger;
             }
 
@@ -136,8 +136,8 @@ void LeverPuzzle::execute() {
 
         break;
     case kActionTrigger:
-        NanEngine.sound->stopSound(moveSound);
-        NanEngine.sound->stopSound(noMoveSound);
+        g_nancy->sound->stopSound(moveSound);
+        g_nancy->sound->stopSound(noMoveSound);
         
         if (solveState == kNotSolved) {
             NancySceneState.changeScene(exitScene);
@@ -156,7 +156,7 @@ void LeverPuzzle::handleInput(NancyInput &input) {
     }
 
     if (NancySceneState.getViewport().convertViewportToScreen(exitHotspot).contains(input.mousePos)) {
-        NanEngine.cursorManager->setCursorType(CursorManager::kExitArrow);
+        g_nancy->cursorManager->setCursorType(CursorManager::kExitArrow);
 
         if (input.input & NancyInput::kLeftMouseButtonUp) {
             state = kActionTrigger;
@@ -166,7 +166,7 @@ void LeverPuzzle::handleInput(NancyInput &input) {
 
     for (uint i = 0; i < 3; ++i) {
         if (NancySceneState.getViewport().convertViewportToScreen(destRects[i]).contains(input.mousePos)) {
-            NanEngine.cursorManager->setCursorType(CursorManager::kHotspot);
+            g_nancy->cursorManager->setCursorType(CursorManager::kHotspot);
             
             if (input.input & NancyInput::kLeftMouseButtonUp) {
                 bool isMoving = false;
@@ -190,7 +190,7 @@ void LeverPuzzle::handleInput(NancyInput &input) {
                 }
 
                 if (isMoving) {
-                    NanEngine.sound->playSound(moveSound);
+                    g_nancy->sound->playSound(moveSound);
 
                     if (leverDirection[i]) {
                         // Moving down
@@ -212,7 +212,7 @@ void LeverPuzzle::handleInput(NancyInput &input) {
 
                     drawLever(i);
                 } else {
-                    NanEngine.sound->playSound(noMoveSound);
+                    g_nancy->sound->playSound(noMoveSound);
                     return;
                 }
             }
diff --git a/engines/nancy/action/orderingpuzzle.cpp b/engines/nancy/action/orderingpuzzle.cpp
index 5bd7f2b89d..1d6ac71c23 100644
--- a/engines/nancy/action/orderingpuzzle.cpp
+++ b/engines/nancy/action/orderingpuzzle.cpp
@@ -45,7 +45,7 @@ void OrderingPuzzle::init() {
     
     setTransparent(true);
 
-    NanEngine.resource->loadImage(imageName, image);
+    g_nancy->resource->loadImage(imageName, image);
 
     setVisible(false);
 
@@ -106,8 +106,8 @@ void OrderingPuzzle::execute() {
     case kBegin:
         init();
         registerGraphics();
-        NanEngine.sound->loadSound(clickSound);
-        NanEngine.sound->loadSound(solveSound);
+        g_nancy->sound->loadSound(clickSound);
+        g_nancy->sound->loadSound(solveSound);
         state = kRun;
         // fall through
     case kRun:
@@ -124,19 +124,19 @@ void OrderingPuzzle::execute() {
             }
 
             NancySceneState.setEventFlag(flagOnSolve);
-            solveSoundPlayTime = NanEngine.getTotalPlayTime() + solveSoundDelay * 1000;
+            solveSoundPlayTime = g_nancy->getTotalPlayTime() + solveSoundDelay * 1000;
             solveState = kPlaySound;
             // fall through
         case kPlaySound:
-            if (NanEngine.getTotalPlayTime() <= solveSoundPlayTime) {
+            if (g_nancy->getTotalPlayTime() <= solveSoundPlayTime) {
                 break;
             }
 
-            NanEngine.sound->playSound(solveSound);
+            g_nancy->sound->playSound(solveSound);
             solveState = kWaitForSound;
             break;
         case kWaitForSound:
-            if (!NanEngine.sound->isSoundPlaying(solveSound)) {
+            if (!g_nancy->sound->isSoundPlaying(solveSound)) {
                 state = kActionTrigger;
             }
 
@@ -144,8 +144,8 @@ void OrderingPuzzle::execute() {
         }
         break;
     case kActionTrigger:
-        NanEngine.sound->stopSound(clickSound);
-        NanEngine.sound->stopSound(solveSound);
+        g_nancy->sound->stopSound(clickSound);
+        g_nancy->sound->stopSound(solveSound);
 
         if (solveState == kNotSolved) {
             NancySceneState.changeScene(exitScene);
@@ -165,7 +165,7 @@ void OrderingPuzzle::handleInput(NancyInput &input) {
     }
 
     if (NancySceneState.getViewport().convertViewportToScreen(exitHotspot).contains(input.mousePos)) {
-        NanEngine.cursorManager->setCursorType(CursorManager::kExitArrow);
+        g_nancy->cursorManager->setCursorType(CursorManager::kExitArrow);
 
         if (input.input & NancyInput::kLeftMouseButtonUp) {
             state = kActionTrigger;
@@ -175,10 +175,10 @@ void OrderingPuzzle::handleInput(NancyInput &input) {
 
     for (int i = 0; i < (int)destRects.size(); ++i) {
         if (NancySceneState.getViewport().convertViewportToScreen(destRects[i]).contains(input.mousePos)) {
-            NanEngine.cursorManager->setCursorType(CursorManager::kHotspot);
+            g_nancy->cursorManager->setCursorType(CursorManager::kHotspot);
 
             if (input.input & NancyInput::kLeftMouseButtonUp) {
-                NanEngine.sound->playSound(clickSound);
+                g_nancy->sound->playSound(clickSound);
                 
                 for (uint j = 0; j < clickedSequence.size(); ++j) {
                     if (clickedSequence[j] == i && drawnElements[i] == true) {
diff --git a/engines/nancy/action/passwordpuzzle.cpp b/engines/nancy/action/passwordpuzzle.cpp
index b34fec836d..5043810ef2 100644
--- a/engines/nancy/action/passwordpuzzle.cpp
+++ b/engines/nancy/action/passwordpuzzle.cpp
@@ -77,7 +77,7 @@ void PasswordPuzzle::execute() {
     case kBegin:
         init();
         registerGraphics();
-        nextBlinkTime = NanEngine.getTotalPlayTime() + cursorBlinkTime;
+        nextBlinkTime = g_nancy->getTotalPlayTime() + cursorBlinkTime;
         state = kRun;
         // fall through
     case kRun:
@@ -85,7 +85,7 @@ void PasswordPuzzle::execute() {
         case kNotSolved: {
             Common::String &activeField = passwordFieldIsActive ? playerPasswordInput : playerNameInput;
             Common::String &correctField = passwordFieldIsActive ? password : name;
-            Time currentTime = NanEngine.getTotalPlayTime();
+            Time currentTime = g_nancy->getTotalPlayTime();
 
             if (playerHasHitReturn) {
                 playerHasHitReturn = false;
@@ -99,13 +99,13 @@ void PasswordPuzzle::execute() {
                     if (!passwordFieldIsActive) {
                         passwordFieldIsActive = true;
                     } else {
-                        NanEngine.sound->loadSound(solveSound);
-                        NanEngine.sound->playSound(solveSound);
+                        g_nancy->sound->loadSound(solveSound);
+                        g_nancy->sound->playSound(solveSound);
                         solveState = kSolved;
                     }
                 } else {
-                    NanEngine.sound->loadSound(failSound);
-                    NanEngine.sound->playSound(failSound);
+                    g_nancy->sound->loadSound(failSound);
+                    g_nancy->sound->playSound(failSound);
                     solveState = kFailed;
                 }
                 
@@ -125,15 +125,15 @@ void PasswordPuzzle::execute() {
             break;
         }
         case kFailed:
-            if (!NanEngine.sound->isSoundPlaying(failSound)) {
-                NanEngine.sound->stopSound(failSound);
+            if (!g_nancy->sound->isSoundPlaying(failSound)) {
+                g_nancy->sound->stopSound(failSound);
                 state = kActionTrigger;
             }
 
             break;
         case kSolved:
-            if (!NanEngine.sound->isSoundPlaying(solveSound)) {
-                NanEngine.sound->stopSound(solveSound);
+            if (!g_nancy->sound->isSoundPlaying(solveSound)) {
+                g_nancy->sound->stopSound(solveSound);
                 state = kActionTrigger;
             }
 
@@ -167,7 +167,7 @@ void PasswordPuzzle::handleInput(NancyInput &input) {
     }
 
     if (NancySceneState.getViewport().convertViewportToScreen(exitHotspot).contains(input.mousePos)) {
-        NanEngine.cursorManager->setCursorType(CursorManager::kExitArrow);
+        g_nancy->cursorManager->setCursorType(CursorManager::kExitArrow);
 
         if (input.input & NancyInput::kLeftMouseButtonUp) {
             state = kActionTrigger;
@@ -218,7 +218,7 @@ void PasswordPuzzle::onPause(bool pause) {
 
 void PasswordPuzzle::drawText() {
     _drawSurface.clear(GraphicsManager::getTransColor());
-    Graphics::Font *font = NanEngine.graphicsManager->getFont(fontID);
+    Graphics::Font *font = g_nancy->graphicsManager->getFont(fontID);
 
     Common::Rect bounds = nameBounds;
     bounds = NancySceneState.getViewport().convertViewportToScreen(bounds);
diff --git a/engines/nancy/action/primaryvideo.cpp b/engines/nancy/action/primaryvideo.cpp
index 960cac82b4..9f0f28f129 100644
--- a/engines/nancy/action/primaryvideo.cpp
+++ b/engines/nancy/action/primaryvideo.cpp
@@ -228,8 +228,8 @@ void PlayPrimaryVideoChan0::execute() {
     case kBegin:
         init();
         registerGraphics();
-        NanEngine.sound->loadSound(sound);
-        NanEngine.sound->playSound(sound);
+        g_nancy->sound->loadSound(sound);
+        g_nancy->sound->playSound(sound);
         state = kRun;
         activePrimaryVideo = this;
         // fall through
@@ -257,8 +257,8 @@ void PlayPrimaryVideoChan0::execute() {
             }
         }
 
-        if (!NanEngine.sound->isSoundPlaying(sound) && _decoder.endOfVideo()) {
-            NanEngine.sound->stopSound(sound);
+        if (!g_nancy->sound->isSoundPlaying(sound) && _decoder.endOfVideo()) {
+            g_nancy->sound->stopSound(sound);
             
             if (responses.size() == 0) {
                 // NPC has finished talking with no responses available, auto-advance to next scene
@@ -276,8 +276,8 @@ void PlayPrimaryVideoChan0::execute() {
                     // Player has picked response, play sound file and change state
                     responseGenericSound.name = responses[pickedResponse].soundName;
                     // TODO this is probably not correct
-                    NanEngine.sound->loadSound(responseGenericSound);
-                    NanEngine.sound->playSound(responseGenericSound);
+                    g_nancy->sound->loadSound(responseGenericSound);
+                    g_nancy->sound->playSound(responseGenericSound);
                     state = kActionTrigger;
                 }
             }
@@ -296,8 +296,8 @@ void PlayPrimaryVideoChan0::execute() {
             NancySceneState.setEventFlag(responses[pickedResponse].flagDesc);
         }
 
-        if (!NanEngine.sound->isSoundPlaying(responseGenericSound)) {
-            NanEngine.sound->stopSound(responseGenericSound);
+        if (!g_nancy->sound->isSoundPlaying(responseGenericSound)) {
+            g_nancy->sound->stopSound(responseGenericSound);
             
             if (pickedResponse != -1) {
                 NancySceneState.changeScene(responses[pickedResponse].sceneChange);
@@ -370,7 +370,7 @@ void PlayPrimaryVideoChan0::addGoodbye() {
             newResponse.soundName = snd;
             newResponse.text = file.readString();
             // response is picked randomly
-            newResponse.sceneChange.sceneID = res.sceneIDs[NanEngine.randomSource->getRandomNumber(3)];
+            newResponse.sceneChange.sceneID = res.sceneIDs[g_nancy->randomSource->getRandomNumber(3)];
             newResponse.sceneChange.doNotStartSound = true;
 
             file.close();
diff --git a/engines/nancy/action/recordtypes.cpp b/engines/nancy/action/recordtypes.cpp
index b65e18ce80..4f8cec0445 100644
--- a/engines/nancy/action/recordtypes.cpp
+++ b/engines/nancy/action/recordtypes.cpp
@@ -291,8 +291,8 @@ void LoseGame::readData(Common::SeekableReadStream &stream) {
 }
 
 void LoseGame::execute() {
-    NanEngine.sound->stopAndUnloadSpecificSounds();
-    NanEngine.setState(NancyEngine::kMainMenu);
+    g_nancy->sound->stopAndUnloadSpecificSounds();
+    g_nancy->setState(NancyEngine::kMainMenu);
     NancySceneState.resetStateToInit();
     isDone = true;
 }
@@ -310,8 +310,8 @@ void WinGame::readData(Common::SeekableReadStream &stream) {
 }
 
 void WinGame::execute() {
-    NanEngine.sound->stopAndUnloadSpecificSounds();
-    NanEngine.setState(NancyEngine::kCredits, NancyEngine::kMainMenu);
+    g_nancy->sound->stopAndUnloadSpecificSounds();
+    g_nancy->setState(NancyEngine::kCredits, NancyEngine::kMainMenu);
     
     // TODO replace with destroy()?
     NancySceneState.resetStateToInit();
@@ -347,7 +347,7 @@ void DifficultyLevel::execute() {
 }
 
 void ShowInventoryItem::init() {
-    NanEngine.resource->loadImage(imageName, _fullSurface);
+    g_nancy->resource->loadImage(imageName, _fullSurface);
 
     _drawSurface.create(_fullSurface, bitmaps[0].src);
 
@@ -403,7 +403,7 @@ void ShowInventoryItem::execute() {
         break;
     }
     case kActionTrigger:
-        NanEngine.sound->playSound(24); // Hardcoded by original engine
+        g_nancy->sound->playSound(24); // Hardcoded by original engine
         NancySceneState.addItemToInventory(objectID);
         setVisible(false);
         hasHotspot = false;
@@ -429,12 +429,12 @@ void PlayDigiSoundAndDie::readData(Common::SeekableReadStream &stream) {
 void PlayDigiSoundAndDie::execute() {
     switch (state) {
     case kBegin:
-        NanEngine.sound->loadSound(sound);
-        NanEngine.sound->playSound(sound);
+        g_nancy->sound->loadSound(sound);
+        g_nancy->sound->playSound(sound);
         state = kRun;
         break;
     case kRun:
-        if (!NanEngine.sound->isSoundPlaying(sound)) {
+        if (!g_nancy->sound->isSoundPlaying(sound)) {
             state = kActionTrigger;
         }
 
@@ -445,7 +445,7 @@ void PlayDigiSoundAndDie::execute() {
         }
         
         NancySceneState.setEventFlag(flagOnTrigger);
-        NanEngine.sound->stopSound(sound);
+        g_nancy->sound->stopSound(sound);
 
         finishExecution();
         break;
@@ -491,8 +491,8 @@ void PlaySoundMultiHS::execute() {
         break;
     }
     case kActionTrigger:
-        NanEngine.sound->loadSound(sound);
-        NanEngine.sound->playSound(sound);
+        g_nancy->sound->loadSound(sound);
+        g_nancy->sound->playSound(sound);
         NancySceneState.changeScene(sceneChange);
         NancySceneState.setEventFlag(flag);
         finishExecution();
@@ -517,13 +517,13 @@ void HintSystem::execute() {
         NancySceneState.getTextbox().clear();
         NancySceneState.getTextbox().addTextLine(text);
 
-        NanEngine.sound->loadSound(genericSound);
-        NanEngine.sound->playSound(genericSound);
+        g_nancy->sound->loadSound(genericSound);
+        g_nancy->sound->playSound(genericSound);
         state = kRun;
         break;
     case kRun:
-        if (!NanEngine.sound->isSoundPlaying(genericSound)) {
-            NanEngine.sound->stopSound(genericSound);
+        if (!g_nancy->sound->isSoundPlaying(genericSound)) {
+            g_nancy->sound->stopSound(genericSound);
             state = kActionTrigger;
         } else {
             break;
diff --git a/engines/nancy/action/rotatinglockpuzzle.cpp b/engines/nancy/action/rotatinglockpuzzle.cpp
index 467b5f04dc..ea13af2bae 100644
--- a/engines/nancy/action/rotatinglockpuzzle.cpp
+++ b/engines/nancy/action/rotatinglockpuzzle.cpp
@@ -40,7 +40,7 @@ void RotatingLockPuzzle::init() {
     
     setTransparent(true);
 
-    NanEngine.resource->loadImage(imageName, image);
+    g_nancy->resource->loadImage(imageName, image);
 }
 
 void RotatingLockPuzzle::readData(Common::SeekableReadStream &stream) {
@@ -109,12 +109,12 @@ void RotatingLockPuzzle::execute() {
         registerGraphics();
 
         for (uint i = 0; i < correctSequence.size(); ++i) {
-            currentSequence.push_back(NanEngine.randomSource->getRandomNumber(9));
+            currentSequence.push_back(g_nancy->randomSource->getRandomNumber(9));
             drawDial(i);
         }
 
-        NanEngine.sound->loadSound(clickSound);
-        NanEngine.sound->loadSound(solveSound);
+        g_nancy->sound->loadSound(clickSound);
+        g_nancy->sound->loadSound(solveSound);
         state = kRun;
         // fall through
     case kRun:
@@ -127,19 +127,19 @@ void RotatingLockPuzzle::execute() {
             }
 
             NancySceneState.setEventFlag(flagOnSolve);
-            solveSoundPlayTime = NanEngine.getTotalPlayTime() + solveSoundDelay * 1000;
+            solveSoundPlayTime = g_nancy->getTotalPlayTime() + solveSoundDelay * 1000;
             solveState = kPlaySound;
             // fall through
         case kPlaySound:
-            if (NanEngine.getTotalPlayTime() <= solveSoundPlayTime) {
+            if (g_nancy->getTotalPlayTime() <= solveSoundPlayTime) {
                 break;
             }
 
-            NanEngine.sound->playSound(solveSound);
+            g_nancy->sound->playSound(solveSound);
             solveState = kWaitForSound;
             break;
         case kWaitForSound:
-            if (!NanEngine.sound->isSoundPlaying(solveSound)) {
+            if (!g_nancy->sound->isSoundPlaying(solveSound)) {
                 state = kActionTrigger;
             }
 
@@ -147,8 +147,8 @@ void RotatingLockPuzzle::execute() {
         }
         break;
     case kActionTrigger:
-        NanEngine.sound->stopSound(clickSound);
-        NanEngine.sound->stopSound(solveSound);
+        g_nancy->sound->stopSound(clickSound);
+        g_nancy->sound->stopSound(solveSound);
 
         if (solveState == kNotSolved) {
             NancySceneState.changeScene(exitScene);
@@ -167,7 +167,7 @@ void RotatingLockPuzzle::handleInput(NancyInput &input) {
     }
 
     if (NancySceneState.getViewport().convertViewportToScreen(exitHotspot).contains(input.mousePos)) {
-        NanEngine.cursorManager->setCursorType(CursorManager::kExitArrow);
+        g_nancy->cursorManager->setCursorType(CursorManager::kExitArrow);
 
         if (input.input & NancyInput::kLeftMouseButtonUp) {
             state = kActionTrigger;
@@ -178,10 +178,10 @@ void RotatingLockPuzzle::handleInput(NancyInput &input) {
 
     for (uint i = 0; i < upHotspots.size(); ++i) {
         if (NancySceneState.getViewport().convertViewportToScreen(upHotspots[i]).contains(input.mousePos)) {
-            NanEngine.cursorManager->setCursorType(CursorManager::kHotspot);
+            g_nancy->cursorManager->setCursorType(CursorManager::kHotspot);
 
             if (input.input & NancyInput::kLeftMouseButtonUp) {
-                NanEngine.sound->playSound(clickSound);
+                g_nancy->sound->playSound(clickSound);
                 
                 currentSequence[i] = ++currentSequence[i] > 9 ? 0 : currentSequence[i];
                 drawDial(i);
@@ -193,10 +193,10 @@ void RotatingLockPuzzle::handleInput(NancyInput &input) {
 
     for (uint i = 0; i < downHotspots.size(); ++i) {
         if (NancySceneState.getViewport().convertViewportToScreen(downHotspots[i]).contains(input.mousePos)) {
-            NanEngine.cursorManager->setCursorType(CursorManager::kHotspot);
+            g_nancy->cursorManager->setCursorType(CursorManager::kHotspot);
 
             if (input.input & NancyInput::kLeftMouseButtonUp) {
-                NanEngine.sound->playSound(clickSound);
+                g_nancy->sound->playSound(clickSound);
 
                 int8 n = currentSequence[i];
                 n = --n < 0 ? 9 : n;
diff --git a/engines/nancy/action/secondarymovie.cpp b/engines/nancy/action/secondarymovie.cpp
index 92d6abff7a..d84c23c42c 100644
--- a/engines/nancy/action/secondarymovie.cpp
+++ b/engines/nancy/action/secondarymovie.cpp
@@ -33,7 +33,7 @@ PlaySecondaryMovie::~PlaySecondaryMovie() {
     _decoder.close();
     
     if (hideMouse == kTrue && unknown == 5) {
-        NanEngine.setMouseEnabled(true);
+        g_nancy->setMouseEnabled(true);
     }
 }
 
@@ -115,8 +115,8 @@ void PlaySecondaryMovie::updateGraphics() {
 
 	if ((_decoder.getCurFrame() == lastFrame && isReverse == kFalse) ||
 	    (_decoder.getCurFrame() == firstFrame && isReverse == kTrue)) {
-		if (!NanEngine.sound->isSoundPlaying(sound)) {
-			NanEngine.sound->stopSound(sound);
+		if (!g_nancy->sound->isSoundPlaying(sound)) {
+			g_nancy->sound->stopSound(sound);
 			_decoder.stop();
             isFinished = true;
 			state = kActionTrigger;
@@ -139,11 +139,11 @@ void PlaySecondaryMovie::execute() {
     case kBegin:
         init();
         registerGraphics();
-        NanEngine.sound->loadSound(sound);
-        NanEngine.sound->playSound(sound);
+        g_nancy->sound->loadSound(sound);
+        g_nancy->sound->playSound(sound);
 
         if (hideMouse == kTrue) {
-            NanEngine.setMouseEnabled(false);
+            g_nancy->setMouseEnabled(false);
         }
 
         state = kRun;
@@ -178,7 +178,7 @@ void PlaySecondaryMovie::execute() {
         } else {
             // Not changing the scene so enable the mouse now
             if (hideMouse == kTrue) {
-                NanEngine.setMouseEnabled(true);
+                g_nancy->setMouseEnabled(true);
             }
         }
 
diff --git a/engines/nancy/action/sliderpuzzle.cpp b/engines/nancy/action/sliderpuzzle.cpp
index 304e3b55ee..7d7f012d5e 100644
--- a/engines/nancy/action/sliderpuzzle.cpp
+++ b/engines/nancy/action/sliderpuzzle.cpp
@@ -43,7 +43,7 @@ void SliderPuzzle::init() {
     
     setTransparent(true);
 
-    NanEngine.resource->loadImage(imageName, image);
+    g_nancy->resource->loadImage(imageName, image);
 }
 
 void SliderPuzzle::readData(Common::SeekableReadStream &stream) {
@@ -111,7 +111,7 @@ void SliderPuzzle::execute() {
         init();
         registerGraphics();
         if (!playerHasTriedPuzzle) {
-            Common::SeekableReadStream *spuz = NanEngine.getBootChunkStream("SPUZ");
+            Common::SeekableReadStream *spuz = g_nancy->getBootChunkStream("SPUZ");
             playerTileOrder.clear();
             spuz->seek(NancySceneState.getDifficulty() * 0x48);
             for (uint y = 0; y < height; ++y) {
@@ -133,7 +133,7 @@ void SliderPuzzle::execute() {
             }
         }
 
-        NanEngine.sound->loadSound(clickSound);
+        g_nancy->sound->loadSound(clickSound);
         state = kRun;
         // fall through
     case kRun:
@@ -147,13 +147,13 @@ void SliderPuzzle::execute() {
                 }
             }
 
-            NanEngine.sound->loadSound(solveSound);
-            NanEngine.sound->playSound(solveSound);
+            g_nancy->sound->loadSound(solveSound);
+            g_nancy->sound->playSound(solveSound);
             solveState = kWaitForSound;
             break;
         case kWaitForSound:
-            if (!NanEngine.sound->isSoundPlaying(solveSound)) {
-                NanEngine.sound->stopSound(solveSound);
+            if (!g_nancy->sound->isSoundPlaying(solveSound)) {
+                g_nancy->sound->stopSound(solveSound);
                 state = kActionTrigger;
             }
 
@@ -174,7 +174,7 @@ void SliderPuzzle::execute() {
             break;
         }
 
-        NanEngine.sound->stopSound(clickSound);
+        g_nancy->sound->stopSound(clickSound);
         finishExecution();
     }
 }
@@ -185,7 +185,7 @@ void SliderPuzzle::handleInput(NancyInput &input) {
     }
 
     if (NancySceneState.getViewport().convertViewportToScreen(exitHotspot).contains(input.mousePos)) {
-        NanEngine.cursorManager->setCursorType(CursorManager::kExitArrow);
+        g_nancy->cursorManager->setCursorType(CursorManager::kExitArrow);
 
         if (input.input & NancyInput::kLeftMouseButtonUp) {
             state = kActionTrigger;
@@ -241,10 +241,10 @@ void SliderPuzzle::handleInput(NancyInput &input) {
     }
 
     if (currentTileX != -1) {
-        NanEngine.cursorManager->setCursorType(CursorManager::kHotspot);
+        g_nancy->cursorManager->setCursorType(CursorManager::kHotspot);
 
         if (input.input & NancyInput::kLeftMouseButtonUp) {
-            NanEngine.sound->playSound(clickSound);
+            g_nancy->sound->playSound(clickSound);
             switch (direction) {
             case kUp: {
                 uint curTileID = playerTileOrder[currentTileY][currentTileX];
diff --git a/engines/nancy/action/staticbitmapanim.cpp b/engines/nancy/action/staticbitmapanim.cpp
index a06a013910..6ff2cd3ea8 100644
--- a/engines/nancy/action/staticbitmapanim.cpp
+++ b/engines/nancy/action/staticbitmapanim.cpp
@@ -35,7 +35,7 @@ namespace Nancy {
 namespace Action {
 
 void PlayStaticBitmapAnimation::init() {
-    NanEngine.resource->loadImage(imageName, _fullSurface);
+    g_nancy->resource->loadImage(imageName, _fullSurface);
 
     setFrame(0);
 
@@ -86,13 +86,13 @@ void PlayStaticBitmapAnimation::readData(Common::SeekableReadStream &stream) {
 }
 
 void PlayStaticBitmapAnimation::execute() {
-    uint32 currentFrameTime = NanEngine.getTotalPlayTime();
+    uint32 currentFrameTime = g_nancy->getTotalPlayTime();
     switch (state) {
     case kBegin:
         init();
         registerGraphics();
-        NanEngine.sound->loadSound(sound);
-        NanEngine.sound->playSound(sound);
+        g_nancy->sound->loadSound(sound);
+        g_nancy->sound->playSound(sound);
         state = kRun;
         // fall through
     case kRun: {
@@ -102,7 +102,7 @@ void PlayStaticBitmapAnimation::execute() {
             if (NancySceneState.getEventFlag(interruptCondition) ||
                 (   (((currentFrame == loopLastFrame) && (isReverse == kFalse) && (isLooping == kFalse)) ||
                     ((currentFrame == loopFirstFrame) && (isReverse == kTrue) && (isLooping == kFalse))) &&
-                        !NanEngine.sound->isSoundPlaying(sound))   ) {
+                        !g_nancy->sound->isSoundPlaying(sound))   ) {
                 
                 state = kActionTrigger;
 
@@ -110,8 +110,8 @@ void PlayStaticBitmapAnimation::execute() {
                 // nancy1's safe lock light not turning off.
                 setVisible(false);
     
-                if (!NanEngine.sound->isSoundPlaying(sound)) {
-                    NanEngine.sound->stopSound(sound);
+                if (!g_nancy->sound->isSoundPlaying(sound)) {
+                    g_nancy->sound->stopSound(sound);
                 }
             } else {
                 // Check if we've moved the viewport
diff --git a/engines/nancy/action/telephone.cpp b/engines/nancy/action/telephone.cpp
index dc552e3cc4..cb17be5f27 100644
--- a/engines/nancy/action/telephone.cpp
+++ b/engines/nancy/action/telephone.cpp
@@ -42,7 +42,7 @@ void Telephone::init() {
     
     setTransparent(true);
 
-    NanEngine.resource->loadImage(imageName, image);
+    g_nancy->resource->loadImage(imageName, image);
 
     NancySceneState.setShouldClearTextbox(false);
 }
@@ -121,8 +121,8 @@ void Telephone::execute() {
     case kBegin:
         init();
         registerGraphics();
-        NanEngine.sound->loadSound(dialToneSound);
-        NanEngine.sound->playSound(dialToneSound);
+        g_nancy->sound->loadSound(dialToneSound);
+        g_nancy->sound->playSound(dialToneSound);
         NancySceneState.getTextbox().clear();
         NancySceneState.getTextbox().addTextLine(addressBookString);
         state = kRun;
@@ -134,23 +134,23 @@ void Telephone::execute() {
             if (calledNumber.size() >= 11 || (calledNumber.size() >= 7 && (calledNumber[0] != 1))) {
                 NancySceneState.getTextbox().clear();
                 NancySceneState.getTextbox().addTextLine("ringing...<n><e>"); // Hardcoded in the original engine
-                NanEngine.sound->loadSound(ringSound);
-                NanEngine.sound->playSound(ringSound);
+                g_nancy->sound->loadSound(ringSound);
+                g_nancy->sound->playSound(ringSound);
                 callState = kRinging;
             }
 
             break;
         case kButtonPress:
-            if (!NanEngine.sound->isSoundPlaying(genericButtonSound)) {
-                NanEngine.sound->stopSound(genericButtonSound);
+            if (!g_nancy->sound->isSoundPlaying(genericButtonSound)) {
+                g_nancy->sound->stopSound(genericButtonSound);
                 undrawButton(selected);
                 callState = kWaiting;
             }
 
             break;
         case kRinging:
-            if (!NanEngine.sound->isSoundPlaying(ringSound)) {
-                NanEngine.sound->stopSound(ringSound);
+            if (!g_nancy->sound->isSoundPlaying(ringSound)) {
+                g_nancy->sound->stopSound(ringSound);
                 uint numberLength = calledNumber[0] == 1 ? 11 : 7;
 
                 for (uint i = 0; i < calls.size(); ++i) {
@@ -172,8 +172,8 @@ void Telephone::execute() {
                     NancySceneState.getTextbox().addTextLine(calls[i].text);
 
                     genericDialogueSound.name = calls[i].soundName;
-                    NanEngine.sound->loadSound(genericDialogueSound);
-                    NanEngine.sound->playSound(genericDialogueSound);
+                    g_nancy->sound->loadSound(genericDialogueSound);
+                    g_nancy->sound->playSound(genericDialogueSound);
                     selected = i;
                     callState = kCall;
 
@@ -183,32 +183,32 @@ void Telephone::execute() {
                 NancySceneState.getTextbox().clear();
                 NancySceneState.getTextbox().addTextLine(dialAgainString);
 
-                NanEngine.sound->loadSound(dialAgainSound);
-                NanEngine.sound->playSound(dialAgainSound);
+                g_nancy->sound->loadSound(dialAgainSound);
+                g_nancy->sound->playSound(dialAgainSound);
                 callState = kBadNumber;
                 return;
             }
 
             break;
         case kBadNumber:
-            if (!NanEngine.sound->isSoundPlaying(dialAgainSound)) {
-                NanEngine.sound->stopSound(dialAgainSound);
+            if (!g_nancy->sound->isSoundPlaying(dialAgainSound)) {
+                g_nancy->sound->stopSound(dialAgainSound);
 
                 state = kActionTrigger;
             }
 
             break;
         case kCall:
-            if (!NanEngine.sound->isSoundPlaying(genericDialogueSound)) {
-                NanEngine.sound->stopSound(genericDialogueSound);
+            if (!g_nancy->sound->isSoundPlaying(genericDialogueSound)) {
+                g_nancy->sound->stopSound(genericDialogueSound);
 
                 state = kActionTrigger;
             }
 
             break;
         case kHangUp:
-            if (!NanEngine.sound->isSoundPlaying(hangUpSound)) {
-                NanEngine.sound->stopSound(hangUpSound);
+            if (!g_nancy->sound->isSoundPlaying(hangUpSound)) {
+                g_nancy->sound->stopSound(hangUpSound);
 
                 state = kActionTrigger;
             }
@@ -254,7 +254,7 @@ void Telephone::handleInput(NancyInput &input) {
     // Cursor gets changed regardless of state
     for (uint i = 0; i < 12; ++i) {
         if (NancySceneState.getViewport().convertViewportToScreen(destRects[i]).contains(input.mousePos)) {
-            NanEngine.cursorManager->setCursorType(CursorManager::kHotspot);
+            g_nancy->cursorManager->setCursorType(CursorManager::kHotspot);
             buttonNr = i;
             break;
         }
@@ -265,11 +265,11 @@ void Telephone::handleInput(NancyInput &input) {
     }
 
     if (NancySceneState.getViewport().convertViewportToScreen(exitHotspot).contains(input.mousePos)) {
-        NanEngine.cursorManager->setCursorType(CursorManager::kExitArrow);
+        g_nancy->cursorManager->setCursorType(CursorManager::kExitArrow);
 
         if (input.input & NancyInput::kLeftMouseButtonUp) {
-            NanEngine.sound->loadSound(hangUpSound);
-            NanEngine.sound->playSound(hangUpSound);
+            g_nancy->sound->loadSound(hangUpSound);
+            g_nancy->sound->playSound(hangUpSound);
 
             callState = kHangUp;
         }
@@ -279,14 +279,14 @@ void Telephone::handleInput(NancyInput &input) {
 
     if (buttonNr != -1) {
         if (input.input & NancyInput::kLeftMouseButtonUp) {
-            if (NanEngine.sound->isSoundPlaying(dialToneSound)) {
-                NanEngine.sound->stopSound(dialToneSound);
+            if (g_nancy->sound->isSoundPlaying(dialToneSound)) {
+                g_nancy->sound->stopSound(dialToneSound);
             }
 
             calledNumber.push_back(buttonNr);
             genericButtonSound.name = buttonSoundNames[buttonNr];
-            NanEngine.sound->loadSound(genericButtonSound);
-            NanEngine.sound->playSound(genericButtonSound);
+            g_nancy->sound->loadSound(genericButtonSound);
+            g_nancy->sound->playSound(genericButtonSound);
 
             drawButton(buttonNr);
 
diff --git a/engines/nancy/console.cpp b/engines/nancy/console.cpp
index 43b96e1b47..59a09690a8 100644
--- a/engines/nancy/console.cpp
+++ b/engines/nancy/console.cpp
@@ -66,7 +66,7 @@ void NancyConsole::postEnter() {
 			dec->start();
 			g_system->fillScreen(0);
 			Common::EventManager *ev = g_system->getEventManager();
-			while (!NanEngine.shouldQuit() && !dec->endOfVideo()) {
+			while (!g_nancy->shouldQuit() && !dec->endOfVideo()) {
 				Common::Event event;
 				if (ev->pollEvent(event)) {
 					if (event.type == Common::EVENT_CUSTOM_ENGINE_ACTION_END && event.customType == Nancy::InputManager::kNancyActionLeftClick) {
@@ -85,7 +85,7 @@ void NancyConsole::postEnter() {
 				g_system->delayMillis(10);
 			}
 
-			NanEngine.graphicsManager->redrawAll();
+			g_nancy->graphicsManager->redrawAll();
 		} else {
 			debugPrintf("Failed to load '%s'\n", _videoFile.c_str());
 		}
@@ -96,14 +96,14 @@ void NancyConsole::postEnter() {
 
 	if (!_imageFile.empty()) {
 		Graphics::Surface surf;
-		if (NanEngine.resource->loadImage(_imageFile, surf)) {
+		if (g_nancy->resource->loadImage(_imageFile, surf)) {
 			g_system->fillScreen(0);
 			g_system->copyRectToScreen(surf.getPixels(), surf.pitch, 0, 0, surf.w > 640 ? 640 : surf.w, surf.h > 480 ? 480 : surf.h);
 			g_system->updateScreen();
 			surf.free();
 
 			Common::EventManager *ev = g_system->getEventManager();
-			while (!NanEngine.shouldQuit()) {
+			while (!g_nancy->shouldQuit()) {
 				Common::Event event;
 				if (ev->pollEvent(event)) {
 					if (event.type == Common::EVENT_CUSTOM_ENGINE_ACTION_END && event.customType == Nancy::InputManager::kNancyActionLeftClick) {
@@ -116,7 +116,7 @@ void NancyConsole::postEnter() {
 				g_system->delayMillis(10);
 			}
 			
-			NanEngine.graphicsManager->redrawAll();
+			g_nancy->graphicsManager->redrawAll();
 		} else {
 			debugPrintf("Failed to load image '%s'\n", _imageFile.c_str());
 		}
@@ -126,7 +126,7 @@ void NancyConsole::postEnter() {
 
 	// After calling the console, action end events get sent to it and the input manager
 	// can still think a keyboard button is being held when it is not; clearing all inputs fixes that
-	NanEngine.input->forceCleanInput();
+	g_nancy->input->forceCleanInput();
 }
 
 bool NancyConsole::Cmd_cifHexDump(int argc, const char **argv) {
@@ -137,7 +137,7 @@ bool NancyConsole::Cmd_cifHexDump(int argc, const char **argv) {
 	}
 
 	uint size;
-	byte *buf = NanEngine.resource->loadCif((argc == 2 ? "ciftree" : argv[2]), argv[1], size);
+	byte *buf = g_nancy->resource->loadCif((argc == 2 ? "ciftree" : argv[2]), argv[1], size);
 	if (!buf) {
 		debugPrintf("Failed to load resource '%s'\n", argv[1]);
 		return true;
@@ -155,7 +155,7 @@ bool NancyConsole::Cmd_cifExport(int argc, const char **argv) {
 		return true;
 	}
 
-	if (!NanEngine.resource->exportCif((argc == 2 ? "ciftree" : argv[2]), argv[1]))
+	if (!g_nancy->resource->exportCif((argc == 2 ? "ciftree" : argv[2]), argv[1]))
 		debugPrintf("Failed to export '%s'\n", argv[1]);
 
 	return true;
@@ -170,7 +170,7 @@ bool NancyConsole::Cmd_cifList(int argc, const char **argv) {
 	}
 
 	Common::Array<Common::String> list;
-	NanEngine.resource->list((argc == 2 ? "ciftree" : argv[2]), list, atoi(argv[1]));
+	g_nancy->resource->list((argc == 2 ? "ciftree" : argv[2]), list, atoi(argv[1]));
 	for (uint i = 0; i < list.size(); i++) {
 		debugPrintf("%-38s", list[i].c_str());
 		if ((i % 2) == 1 && i + 1 != list.size())
@@ -189,7 +189,7 @@ bool NancyConsole::Cmd_cifInfo(int argc, const char **argv) {
 		return true;
 	}
 
-	debugPrintf("%s", NanEngine.resource->getCifDescription((argc == 2 ? "ciftree" : argv[2]), argv[1]).c_str());
+	debugPrintf("%s", g_nancy->resource->getCifDescription((argc == 2 ? "ciftree" : argv[2]), argv[1]).c_str());
 	return true;
 }
 
@@ -272,7 +272,7 @@ bool NancyConsole::Cmd_loadCal(int argc, const char **argv) {
 		return true;
 	}
 
-	if (!NanEngine.resource->loadCifTree(argv[1], "cal"))
+	if (!g_nancy->resource->loadCifTree(argv[1], "cal"))
 		debugPrintf("Failed to load '%s.cal'\n", argv[1]);
 	return true;
 }
@@ -321,7 +321,7 @@ bool NancyConsole::Cmd_loadScene(int argc, const char **argv) {
 		return true;
 	}
 	
-	if (NanEngine._gameFlow.previousState != &NancySceneState) {
+	if (g_nancy->_gameFlow.previousState != &NancySceneState) {
 		debugPrintf("Not in the kScene state\n");
 		return true;
 	}
@@ -339,7 +339,7 @@ bool NancyConsole::Cmd_loadScene(int argc, const char **argv) {
 }
 
 bool NancyConsole::Cmd_sceneID(int argc, const char **argv) {
-	if (NanEngine._gameFlow.previousState != &NancySceneState) {
+	if (g_nancy->_gameFlow.previousState != &NancySceneState) {
 		debugPrintf("Not in the kScene state\n");
 		return true;
 	}
diff --git a/engines/nancy/cursor.cpp b/engines/nancy/cursor.cpp
index 3652ccfc63..12a42dc187 100644
--- a/engines/nancy/cursor.cpp
+++ b/engines/nancy/cursor.cpp
@@ -33,11 +33,11 @@
 namespace Nancy {
 
 void CursorManager::init() {
-    Common::SeekableReadStream *chunk = NanEngine.getBootChunkStream("INV");
+    Common::SeekableReadStream *chunk = g_nancy->getBootChunkStream("INV");
     chunk->seek(0x1D2); // TODO
     Common::String inventoryCursorsImageName = chunk->readString();
 
-    chunk = NanEngine.getBootChunkStream("CURS");
+    chunk = g_nancy->getBootChunkStream("CURS");
     for (uint i = 0; i < 56; ++i) {
         _cursors.push_back(Cursor());
         chunk->seek(i * 16, SEEK_SET);
@@ -51,7 +51,7 @@ void CursorManager::init() {
     _primaryVideoInitialPos.x = chunk->readUint16LE();
     _primaryVideoInitialPos.y = chunk->readUint16LE();
 
-    NanEngine.resource->loadImage(inventoryCursorsImageName, _invCursorsSurface);
+    g_nancy->resource->loadImage(inventoryCursorsImageName, _invCursorsSurface);
 
     setCursor(kNormalArrow, -1);
     showCursor(false);
@@ -106,7 +106,7 @@ void CursorManager::setCursor(CursorType type, int16 itemID) {
         surf = &_invCursorsSurface;
         
     } else {
-        surf = &NanEngine.graphicsManager->object0;
+        surf = &g_nancy->graphicsManager->object0;
     }
 
     // TODO this is ridiculous, figure out why just calling
diff --git a/engines/nancy/font.cpp b/engines/nancy/font.cpp
index cc57a51173..91674b775e 100644
--- a/engines/nancy/font.cpp
+++ b/engines/nancy/font.cpp
@@ -38,7 +38,7 @@ void Font::read(Common::SeekableReadStream &stream) {
     stream.read(name, 10);
     Common::String imageName = name;
 
-    NanEngine.resource->loadImage(name, _image);
+    g_nancy->resource->loadImage(name, _image);
 
     char desc[0x20];
     stream.read(desc, 0x20);
diff --git a/engines/nancy/graphics.cpp b/engines/nancy/graphics.cpp
index b605f6c02e..9de7a7b946 100644
--- a/engines/nancy/graphics.cpp
+++ b/engines/nancy/graphics.cpp
@@ -49,10 +49,10 @@ void GraphicsManager::init() {
     _screen.setTransparentColor(getTransColor());
     _screen.clear();
 
-    Common::SeekableReadStream *ob = NanEngine.getBootChunkStream("OB0");
+    Common::SeekableReadStream *ob = g_nancy->getBootChunkStream("OB0");
     ob->seek(0);
 
-    NanEngine.resource->loadImage(ob->readString(), object0);
+    g_nancy->resource->loadImage(ob->readString(), object0);
 
     loadFonts();
 }
@@ -219,7 +219,7 @@ void GraphicsManager::copyToManaged(void *src, Graphics::ManagedSurface &dst, ui
 }
 
 const Graphics::PixelFormat &GraphicsManager::getInputPixelFormat() {
-    if (NanEngine.getGameFlags() & NGF_8BITCOLOR) {
+    if (g_nancy->getGameFlags() & NGF_8BITCOLOR) {
         return clut8Format;
     } else {
         return inputPixelFormat;
@@ -227,7 +227,7 @@ const Graphics::PixelFormat &GraphicsManager::getInputPixelFormat() {
 }
 
 uint GraphicsManager::getTransColor() {
-    if (NanEngine.getGameFlags() & NGF_8BITCOLOR) {
+    if (g_nancy->getGameFlags() & NGF_8BITCOLOR) {
         return 1; // If this isn't correct, try picking the pixel at [0, 0] inside the palette bitmap
     } else {
         return inputPixelFormat.ARGBToColor(0, 0, 255, 0);
@@ -235,7 +235,7 @@ uint GraphicsManager::getTransColor() {
 }
 
 void GraphicsManager::loadFonts() {
-    Common::SeekableReadStream *chunk = NanEngine.getBootChunkStream("FONT");
+    Common::SeekableReadStream *chunk = g_nancy->getBootChunkStream("FONT");
     
     chunk->seek(0);
     while (chunk->pos() < chunk->size() - 1) {
diff --git a/engines/nancy/iff.cpp b/engines/nancy/iff.cpp
index df29201d67..f6e054dbf3 100644
--- a/engines/nancy/iff.cpp
+++ b/engines/nancy/iff.cpp
@@ -69,7 +69,7 @@ bool IFF::callback(Common::IFFChunk &c) {
 bool IFF::load() {
 	byte *data;
 	uint size;
-	data = NanEngine.resource->loadData(_name, size);
+	data = g_nancy->resource->loadData(_name, size);
 
 	if (!data) {
 		return false;
diff --git a/engines/nancy/input.cpp b/engines/nancy/input.cpp
index dc3d7c25e5..e5be3ae9a3 100644
--- a/engines/nancy/input.cpp
+++ b/engines/nancy/input.cpp
@@ -37,15 +37,15 @@ void InputManager::processEvents() {
     _inputs &= ~(NancyInput::kLeftMouseButtonDown | NancyInput::kLeftMouseButtonUp | NancyInput::kRightMouseButtonDown | NancyInput::kRightMouseButtonUp);
     _otherKbdInput.clear();
 
-    while (NanEngine.getEventManager()->pollEvent(event)) {
+    while (g_nancy->getEventManager()->pollEvent(event)) {
         switch (event.type) {
         case EVENT_KEYDOWN:
             if (event.kbd.keycode == KEYCODE_d && event.kbd.flags & Common::KBD_CTRL) {
                 // Launch debug console
-                NanEngine.launchConsole = true;
+                g_nancy->launchConsole = true;
             } else if (event.kbd.keycode == KEYCODE_q && event.kbd.flags & Common::KBD_CTRL) {
                 // Quit
-                NanEngine.quitGame();
+                g_nancy->quitGame();
             } else {
                 // Push all other keyboard events into an array and let getInput() callers handle them
                 _otherKbdInput.push_back(event.kbd);
@@ -53,7 +53,7 @@ void InputManager::processEvents() {
             break;
         case EVENT_CUSTOM_ENGINE_ACTION_START:
             if (_inputBeginState == nullptr) {
-                _inputBeginState = NanEngine.getState();
+                _inputBeginState = g_nancy->getState();
             }
             
             switch (event.customType) {
@@ -81,10 +81,10 @@ void InputManager::processEvents() {
                 _inputs |= NancyInput::kMoveFastModifier;
                 break;
             case kNancyActionRequestCheatMenu:
-                NanEngine.callCheatMenu(false);
+                g_nancy->callCheatMenu(false);
                 break;
             case kNancyActionRequestEventMenu:
-                NanEngine.callCheatMenu(true);
+                g_nancy->callCheatMenu(true);
                 break;
             default:
                 break;
@@ -137,7 +137,7 @@ NancyInput InputManager::getInput() const {
     // Filter out inputs that began in other states; e.g. if the mouse was pushed and held down
     // in a previous state, the button up event won't fire. Right now we simply block all events
     // until everything's clear, but if that causes problems the fix should be easy.
-    if (_inputBeginState == NanEngine.getState()) {
+    if (_inputBeginState == g_nancy->getState()) {
         ret.input = _inputs;
         ret.otherKbdInput = _otherKbdInput;
     } else {
@@ -145,7 +145,7 @@ NancyInput InputManager::getInput() const {
     }
 
     if (_mouseEnabled) {
-        ret.mousePos = NanEngine.getEventManager()->getMousePos();
+        ret.mousePos = g_nancy->getEventManager()->getMousePos();
     } else {
         ret.eatMouseInput();
     }
diff --git a/engines/nancy/nancy.cpp b/engines/nancy/nancy.cpp
index d4926b8cee..45ac0fcd01 100644
--- a/engines/nancy/nancy.cpp
+++ b/engines/nancy/nancy.cpp
@@ -62,7 +62,11 @@
 
 namespace Nancy {
 
+NancyEngine *g_nancy;
+
 NancyEngine::NancyEngine(OSystem *syst, const NancyGameDescription *gd) : Engine(syst), _gameDescription(gd), _system(syst) {
+	g_nancy = this;
+
 	DebugMan.addDebugChannel(kDebugEngine, "Engine", "Engine debug level");
 	DebugMan.addDebugChannel(kDebugActionRecord, "ActionRecord", "Action Record debug level");
 	DebugMan.addDebugChannel(kDebugScene, "Scene", "Scene debug level");
@@ -327,18 +331,18 @@ void NancyEngine::bootGameEngine() {
 
 	// Persistent sounds that are used across the engine. These originally get loaded inside Logo
 	SoundDescription desc;
-	desc.read(*NanEngine.getBootChunkStream("BUOK"), SoundDescription::kNormal);
-	NanEngine.sound->loadSound(desc);
-	desc.read(*NanEngine.getBootChunkStream("BUDE"), SoundDescription::kNormal);
-	NanEngine.sound->loadSound(desc);
-	desc.read(*NanEngine.getBootChunkStream("BULS"), SoundDescription::kNormal);
-	NanEngine.sound->loadSound(desc);
-	desc.read(*NanEngine.getBootChunkStream("GLOB"), SoundDescription::kNormal);
-	NanEngine.sound->loadSound(desc);
-	desc.read(*NanEngine.getBootChunkStream("CURT"), SoundDescription::kNormal);
-	NanEngine.sound->loadSound(desc);
-	desc.read(*NanEngine.getBootChunkStream("CANT"), SoundDescription::kNormal);
-	NanEngine.sound->loadSound(desc);
+	desc.read(*g_nancy->getBootChunkStream("BUOK"), SoundDescription::kNormal);
+	g_nancy->sound->loadSound(desc);
+	desc.read(*g_nancy->getBootChunkStream("BUDE"), SoundDescription::kNormal);
+	g_nancy->sound->loadSound(desc);
+	desc.read(*g_nancy->getBootChunkStream("BULS"), SoundDescription::kNormal);
+	g_nancy->sound->loadSound(desc);
+	desc.read(*g_nancy->getBootChunkStream("GLOB"), SoundDescription::kNormal);
+	g_nancy->sound->loadSound(desc);
+	desc.read(*g_nancy->getBootChunkStream("CURT"), SoundDescription::kNormal);
+	g_nancy->sound->loadSound(desc);
+	desc.read(*g_nancy->getBootChunkStream("CANT"), SoundDescription::kNormal);
+	g_nancy->sound->loadSound(desc);
 
 	delete boot;
 	
diff --git a/engines/nancy/nancy.h b/engines/nancy/nancy.h
index 9f30cde470..87195df7c1 100644
--- a/engines/nancy/nancy.h
+++ b/engines/nancy/nancy.h
@@ -190,7 +190,7 @@ private:
 	Common::HashMap<Common::String, Common::SeekableReadStream *> _bootChunks;
 };
 
-#define NanEngine (*((NancyEngine *)(g_engine)))
+extern NancyEngine *g_nancy;
 
 } // End of namespace Nancy
 
diff --git a/engines/nancy/renderobject.cpp b/engines/nancy/renderobject.cpp
index 94363d26f5..08e04ac6ef 100644
--- a/engines/nancy/renderobject.cpp
+++ b/engines/nancy/renderobject.cpp
@@ -37,11 +37,11 @@ void RenderObject::init() {
 }
 
 void RenderObject::registerGraphics() {
-    NanEngine.graphicsManager->addObject(this);
+    g_nancy->graphicsManager->addObject(this);
 }
 
 RenderObject::~RenderObject() {
-    NanEngine.graphicsManager->removeObject(this);
+    g_nancy->graphicsManager->removeObject(this);
     if (_drawSurface.getPixels()) {
         _drawSurface.free();
     }
diff --git a/engines/nancy/state/credits.cpp b/engines/nancy/state/credits.cpp
index 9e73c047a9..8b3ea46333 100644
--- a/engines/nancy/state/credits.cpp
+++ b/engines/nancy/state/credits.cpp
@@ -51,7 +51,7 @@ void Credits::process() {
 }
 
 void Credits::init() {
-    Common::SeekableReadStream *cred = NanEngine.getBootChunkStream("CRED");
+    Common::SeekableReadStream *cred = g_nancy->getBootChunkStream("CRED");
     cred->seek(0);
 
     char buf[10];
@@ -66,7 +66,7 @@ void Credits::init() {
     _pixelsToScroll = cred->readUint16LE();
     _sound.read(*cred, SoundDescription::kMenu);
 
-    NanEngine.resource->loadImage(buf, _fullTextSurface);
+    g_nancy->resource->loadImage(buf, _fullTextSurface);
     
     Common::Rect src = _text._screenPosition;
     src.moveTo(Common::Point());
@@ -74,29 +74,29 @@ void Credits::init() {
     _text.setTransparent(true);
     _text.init();
 
-    NanEngine.sound->loadSound(_sound);
-    NanEngine.sound->playSound(_sound);
+    g_nancy->sound->loadSound(_sound);
+    g_nancy->sound->playSound(_sound);
 
     _background.registerGraphics();
     _text.registerGraphics();
 
-    NanEngine.cursorManager->showCursor(false);
+    g_nancy->cursorManager->showCursor(false);
 
     _state = kRun;
 }
 
 void Credits::run() {
-    NancyInput input = NanEngine.input->getInput();
+    NancyInput input = g_nancy->input->getInput();
 
     if (input.input & NancyInput::kLeftMouseButtonDown) {
         _state = kInit;
-        NanEngine.sound->stopSound(_sound);
-        NanEngine.setState(NancyEngine::kMainMenu);
-        NanEngine.cursorManager->showCursor(true);
+        g_nancy->sound->stopSound(_sound);
+        g_nancy->setState(NancyEngine::kMainMenu);
+        g_nancy->cursorManager->showCursor(true);
         _fullTextSurface.free();
     }
 
-    Time currentTime = NanEngine.getTotalPlayTime();
+    Time currentTime = g_nancy->getTotalPlayTime();
     if (currentTime >= _nextUpdateTime) {
         _nextUpdateTime = currentTime + _updateTime;
 
diff --git a/engines/nancy/state/help.cpp b/engines/nancy/state/help.cpp
index 929e3a87d9..2ab7b33649 100644
--- a/engines/nancy/state/help.cpp
+++ b/engines/nancy/state/help.cpp
@@ -55,7 +55,7 @@ void Help::process() {
 }
 
 void Help::init() {
-    Common::SeekableReadStream *chunk = NanEngine.getBootChunkStream("HELP");
+    Common::SeekableReadStream *chunk = g_nancy->getBootChunkStream("HELP");
 
     chunk->seek(0);
     char buf[10];
@@ -68,7 +68,7 @@ void Help::init() {
     _hotspot.right = chunk->readUint16LE();
     _hotspot.bottom = chunk->readUint16LE();
     
-    chunk = NanEngine.getBootChunkStream("MSND");
+    chunk = g_nancy->getBootChunkStream("MSND");
     chunk->seek(0);
 	_sound.read(*chunk, SoundDescription::kMenu);
 
@@ -76,30 +76,30 @@ void Help::init() {
 }
 
 void Help::begin() {
-	NanEngine.sound->loadSound(_sound);
-	NanEngine.sound->playSound(_sound);
+	g_nancy->sound->loadSound(_sound);
+	g_nancy->sound->playSound(_sound);
     
     _image.registerGraphics();
     _image.setVisible(true);
 
-    NanEngine.cursorManager->setCursorType(CursorManager::kNormalArrow);
+    g_nancy->cursorManager->setCursorType(CursorManager::kNormalArrow);
     
     _state = kRun;
 }
 
 void Help::run() {
-    NancyInput input = NanEngine.input->getInput();
+    NancyInput input = g_nancy->input->getInput();
 
     if (_hotspot.contains(input.mousePos) && input.input & NancyInput::kLeftMouseButtonUp) {
-        NanEngine.sound->playSound(0x18); // Hardcoded by original engine
+        g_nancy->sound->playSound(0x18); // Hardcoded by original engine
         _state = kWaitForSound;
     }
 }
 
 void Help::waitForSound() {
-    if (!NanEngine.sound->isSoundPlaying(18)) {
-	    NanEngine.sound->stopSound(_sound);
-        NanEngine.setPreviousState();
+    if (!g_nancy->sound->isSoundPlaying(18)) {
+	    g_nancy->sound->stopSound(_sound);
+        g_nancy->setPreviousState();
     }
 }
 
diff --git a/engines/nancy/state/logo.cpp b/engines/nancy/state/logo.cpp
index 4b19259741..45c945d788 100644
--- a/engines/nancy/state/logo.cpp
+++ b/engines/nancy/state/logo.cpp
@@ -61,13 +61,13 @@ void Logo::process() {
 }
 
 bool Logo::onStateExit() {
-	NanEngine.sound->stopSound(_msnd);
+	g_nancy->sound->stopSound(_msnd);
 	destroy();
 	return true;
 }
 
 void Logo::init() {
-	Common::SeekableReadStream *lg = NanEngine.getBootChunkStream("LG0");
+	Common::SeekableReadStream *lg = g_nancy->getBootChunkStream("LG0");
 	lg->seek(0);
 
 	_logoImage.init(lg->readString());
@@ -77,16 +77,16 @@ void Logo::init() {
 }
 
 void Logo::startSound() {
-	_msnd.read(*NanEngine.getBootChunkStream("MSND"), SoundDescription::kMenu);
-	NanEngine.sound->loadSound(_msnd);
-	NanEngine.sound->playSound(_msnd);
+	_msnd.read(*g_nancy->getBootChunkStream("MSND"), SoundDescription::kMenu);
+	g_nancy->sound->loadSound(_msnd);
+	g_nancy->sound->playSound(_msnd);
 
 	_startTicks = g_system->getMillis();
 	_state = kRun;
 }
 
 void Logo::run() {
-	if (g_system->getMillis() - _startTicks >= 7000 || (NanEngine.input->getInput().input & NancyInput::kLeftMouseButtonDown)) {
+	if (g_system->getMillis() - _startTicks >= 7000 || (g_nancy->input->getInput().input & NancyInput::kLeftMouseButtonDown)) {
 		_state = kStop;
 	}
 }
@@ -96,9 +96,9 @@ void Logo::stop() {
 	// For the N+C key combo it looks for some kind of cheat file
 	// to initialize the game state with.
 
-	NanEngine.sound->stopSound(_msnd);
+	g_nancy->sound->stopSound(_msnd);
 
-	NanEngine.setState(NancyEngine::kScene);
+	g_nancy->setState(NancyEngine::kScene);
 }
 
 } // End of namespace State
diff --git a/engines/nancy/state/map.cpp b/engines/nancy/state/map.cpp
index f1f2efa913..667fa4519e 100644
--- a/engines/nancy/state/map.cpp
+++ b/engines/nancy/state/map.cpp
@@ -54,7 +54,7 @@ void Map::process() {
 }
 
 void Map::init() {
-    Common::SeekableReadStream *chunk = NanEngine.getBootChunkStream("MAP");
+    Common::SeekableReadStream *chunk = g_nancy->getBootChunkStream("MAP");
 
     _viewport.init();
     _label.init();
@@ -80,8 +80,8 @@ void Map::init() {
     chunk->seek(0x18 + _mapID * 0x20, SEEK_SET);
     SoundDescription sound;
     sound.read(*chunk, SoundDescription::kMenu);
-    NanEngine.sound->loadSound(sound);
-    NanEngine.sound->playSound(0x14);
+    g_nancy->sound->loadSound(sound);
+    g_nancy->sound->playSound(0x14);
 
     _locations.clear();
 
@@ -116,37 +116,37 @@ void Map::init() {
     }
 
     registerGraphics();
-    NanEngine.cursorManager->setCursorItemID(-1);
+    g_nancy->cursorManager->setCursorItemID(-1);
 
     _state = kRun;
 }
 
 void Map::run() {
-    if (!NanEngine.sound->isSoundPlaying(0x14) && !NanEngine.sound->isSoundPlaying(0x13)) {
-        NanEngine.sound->playSound(0x13);
+    if (!g_nancy->sound->isSoundPlaying(0x14) && !g_nancy->sound->isSoundPlaying(0x13)) {
+        g_nancy->sound->playSound(0x13);
     }
 
-    NancyInput input = NanEngine.input->getInput();
+    NancyInput input = g_nancy->input->getInput();
 
     _label.setLabel(-1);
 
     _button.handleInput(input);
 
     if (_mapButtonClicked) {
-        NanEngine.setState(NancyEngine::kScene);
+        g_nancy->setState(NancyEngine::kScene);
         return;
     }
 
     for (uint i = 0; i < 4; ++i) {
         auto &loc = _locations[i];
         if (loc.isActive && _viewport.convertToScreen(loc.hotspot).contains(input.mousePos)) {
-            NanEngine.cursorManager->setCursorType(CursorManager::kHotspotArrow);
+            g_nancy->cursorManager->setCursorType(CursorManager::kHotspotArrow);
 
             _label.setLabel(i);
 
             if (input.input & NancyInput::kLeftMouseButtonUp) {
                 _pickedLocationID = i;
-                NanEngine.setState(NancyEngine::kScene);
+                g_nancy->setState(NancyEngine::kScene);
             }
 
             return;
@@ -155,24 +155,24 @@ void Map::run() {
 }
 
 bool Map::onStateExit() {
-    Common::SeekableReadStream *chunk = NanEngine.getBootChunkStream("MAP");
+    Common::SeekableReadStream *chunk = g_nancy->getBootChunkStream("MAP");
     SoundDescription sound;
     chunk->seek(0x18 + _mapID * 0x20, SEEK_SET);
     sound.read(*chunk, SoundDescription::kMenu);
-    NanEngine.sound->stopSound(sound);
+    g_nancy->sound->stopSound(sound);
     
-    NanEngine.setState(NancyEngine::kScene);
+    g_nancy->setState(NancyEngine::kScene);
 
     if (_pickedLocationID != -1) {
         auto &loc = _locations[_pickedLocationID];
         NancySceneState.changeScene(loc.scenes[_mapID].sceneID, loc.scenes[_mapID].frameID, loc.scenes[_mapID].verticalOffset, false);
         _pickedLocationID = -1;
         
-        NanEngine.sound->playSound(0x18);
+        g_nancy->sound->playSound(0x18);
     }
     
     // The two sounds play at the same time if a location was picked
-    NanEngine.sound->playSound(0x14);
+    g_nancy->sound->playSound(0x14);
 
     _mapButtonClicked = false;
 
@@ -197,18 +197,18 @@ void Map::MapLabel::setLabel(int labelID) {
         setVisible(false);
     } else {
         _screenPosition = _parent->_locations[labelID].labelDest;
-        _drawSurface.create(NanEngine.graphicsManager->object0, _parent->_locations[labelID].labelSrc);
+        _drawSurface.create(g_nancy->graphicsManager->object0, _parent->_locations[labelID].labelSrc);
         setVisible(true);
     }
 }
 
 void Map::MapButton::init() {
-    Common::SeekableReadStream *map = NanEngine.getBootChunkStream("MAP");
+    Common::SeekableReadStream *map = g_nancy->getBootChunkStream("MAP");
 
     map->seek(0x7A, SEEK_SET);
     Common::Rect src;
     readRect(*map, src);
-    _drawSurface.create(NanEngine.graphicsManager->object0, src);
+    _drawSurface.create(g_nancy->graphicsManager->object0, src);
     readRect(*map, _screenPosition);
     setVisible(true);
 
diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index 6763b40720..5b4b7a9833 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -55,7 +55,7 @@ void Scene::SceneSummary::read(Common::SeekableReadStream &stream) {
 
     stream.seek(0);
     Common::Serializer ser(&stream, nullptr);
-    ser.setVersion(NanEngine.getGameType());
+    ser.setVersion(g_nancy->getGameType());
 
     ser.syncBytes((byte *)buf, 0x32);
     description = Common::String(buf);
@@ -83,9 +83,9 @@ void Scene::SceneSummary::read(Common::SeekableReadStream &stream) {
     ser.syncAsUint16LE((uint32 &)slowMoveTimeDelta);
     ser.syncAsUint16LE((uint32 &)fastMoveTimeDelta);
 
-    if (NanEngine.overrideMovementTimeDeltas) {
-        slowMoveTimeDelta = NanEngine.slowMovementTimeDelta;
-        fastMoveTimeDelta = NanEngine.fastMovementTimeDelta;
+    if (g_nancy->overrideMovementTimeDeltas) {
+        slowMoveTimeDelta = g_nancy->slowMovementTimeDelta;
+        fastMoveTimeDelta = g_nancy->fastMovementTimeDelta;
     }
 
     delete[] buf;
@@ -107,9 +107,9 @@ void Scene::process() {
     case kStartSound:
         _state = kRun;
         if (!_sceneState._doNotStartSound) {
-            NanEngine.sound->stopAndUnloadSpecificSounds();
-            NanEngine.sound->loadSound(_sceneState.summary.sound);
-            NanEngine.sound->playSound(_sceneState.summary.sound);
+            g_nancy->sound->stopAndUnloadSpecificSounds();
+            g_nancy->sound->loadSound(_sceneState.summary.sound);
+            g_nancy->sound->playSound(_sceneState.summary.sound);
         }
         // fall through
     case kRun:
@@ -123,19 +123,19 @@ void Scene::onStateEnter() {
         registerGraphics();
         _actionManager.onPause(false);
 
-        NanEngine.graphicsManager->redrawAll();
+        g_nancy->graphicsManager->redrawAll();
 
         // Run once to clear out the previous scene when coming from Map
         process();
 
-        NanEngine.setTotalPlayTime((uint32)_timers.pushedPlayTime);
+        g_nancy->setTotalPlayTime((uint32)_timers.pushedPlayTime);
 
         unpauseSceneSpecificSounds();
     }
 }
 
 bool Scene::onStateExit() {
-    _timers.pushedPlayTime = NanEngine.getTotalPlayTime();
+    _timers.pushedPlayTime = g_nancy->getTotalPlayTime();
     _actionManager.onPause(true);
     pauseSceneSpecificSounds();
     _gameStateRequested = NancyEngine::kNone;
@@ -173,13 +173,13 @@ void Scene::pauseSceneSpecificSounds() {
     // TODO missing if, same condition as the one in SoundManager::stopAndUnloadSpecificSounds
 
     for (uint i = 0; i < 10; ++i) {
-		NanEngine.sound->pauseSound(i, true);
+		g_nancy->sound->pauseSound(i, true);
 	}
 }
 
 void Scene::unpauseSceneSpecificSounds() {
     for (uint i = 0; i < 10; ++i) {
-		NanEngine.sound->pauseSound(i, false);
+		g_nancy->sound->pauseSound(i, false);
 	}
 }
 
@@ -227,7 +227,7 @@ bool Scene::getEventFlag(EventFlagDescription eventFlag) const {
 void Scene::setLogicCondition(int16 label, NancyFlag flag) {
     if (label > -1) {
         _flags.logicConditions[label].flag = flag;
-        _flags.logicConditions[label].timestamp = NanEngine.getTotalPlayTime();
+        _flags.logicConditions[label].timestamp = g_nancy->getTotalPlayTime();
     }
 }
 
@@ -319,7 +319,7 @@ void Scene::synchronize(Common::Serializer &ser) {
 	// TODO hardcoded inventory size
 	ser.syncArray(_flags.items, 11, Common::Serializer::Byte);
     ser.syncAsSint16LE(_flags.heldItem);
-    NanEngine.cursorManager->setCursorItemID(_flags.heldItem);
+    g_nancy->cursorManager->setCursorItemID(_flags.heldItem);
 
 	ser.syncAsUint32LE((uint32 &)_timers.lastTotalTime);
 	ser.syncAsUint32LE((uint32 &)_timers.sceneTime);
@@ -329,7 +329,7 @@ void Scene::synchronize(Common::Serializer &ser) {
 	ser.syncAsByte(_timers.timerIsActive);
     ser.syncAsByte(_timers.timeOfDay);
 
-    NanEngine.setTotalPlayTime((uint32)_timers.lastTotalTime);
+    g_nancy->setTotalPlayTime((uint32)_timers.lastTotalTime);
 
 	// TODO hardcoded number of event flags
 	ser.syncArray(_flags.eventFlags, 168, Common::Serializer::Byte);
@@ -356,7 +356,7 @@ void Scene::init() {
     }
 
     _timers.lastTotalTime = 0;
-    _timers.playerTime = NanEngine.startTimeHours * 3600000;
+    _timers.playerTime = g_nancy->startTimeHours * 3600000;
     _timers.sceneTime = 0;
     _timers.timerTime = 0;
     _timers.timerIsActive = false;
@@ -364,9 +364,9 @@ void Scene::init() {
     _timers.pushedPlayTime = 0;
     _timers.timeOfDay = Timers::kDay;
 
-    _sceneState.nextScene.sceneID = NanEngine.firstSceneID;
+    _sceneState.nextScene.sceneID = g_nancy->firstSceneID;
 
-    Common::SeekableReadStream *chunk = NanEngine.getBootChunkStream("HINT");
+    Common::SeekableReadStream *chunk = g_nancy->getBootChunkStream("HINT");
     
     if (chunk) {
         chunk->seek(0);
@@ -387,9 +387,9 @@ void Scene::init() {
     if (ConfMan.hasKey("save_slot")) {
         // Load savefile directly from the launcher
 		int saveSlot = ConfMan.getInt("save_slot");
-		if (saveSlot >= 0 && saveSlot <= NanEngine.getMetaEngine().getMaximumSaveSlot()) {
+		if (saveSlot >= 0 && saveSlot <= g_nancy->getMetaEngine().getMaximumSaveSlot()) {
 			// Set to Scene but do not do the loading yet
-			NanEngine.loadGameState(saveSlot);
+			g_nancy->loadGameState(saveSlot);
 		}
 	} else {
         // Normal boot, load default first scene
@@ -397,7 +397,7 @@ void Scene::init() {
     }
 
     registerGraphics();
-    NanEngine.graphicsManager->redrawAll();
+    g_nancy->graphicsManager->redrawAll();
 }
 
 void Scene::load() {
@@ -485,12 +485,12 @@ void Scene::run() {
 
 
     if (_gameStateRequested != NancyEngine::kNone) {
-        NanEngine.setState(_gameStateRequested);
+        g_nancy->setState(_gameStateRequested);
 
         return;
     }
 
-    Time currentPlayTime = NanEngine.getTotalPlayTime();
+    Time currentPlayTime = g_nancy->getTotalPlayTime();
 
     Time deltaTime = currentPlayTime - _timers.lastTotalTime;
     _timers.lastTotalTime = currentPlayTime;
@@ -504,7 +504,7 @@ void Scene::run() {
     // Calculate the in-game time (playerTime)
     if (currentPlayTime > _timers.playerTimeNextMinute) {
         _timers.playerTime += 60000; // Add a minute
-        _timers.playerTimeNextMinute = currentPlayTime + NanEngine.playerTimeMinuteLength;
+        _timers.playerTimeNextMinute = currentPlayTime + g_nancy->playerTimeMinuteLength;
     }
 
     // Set the time of day according to playerTime
@@ -517,7 +517,7 @@ void Scene::run() {
     }
 
     // Update the UI elements and handle input
-    NancyInput input = NanEngine.input->getInput();
+    NancyInput input = g_nancy->input->getInput();
     _viewport.handleInput(input);
     _menuButton.handleInput(input);
     _helpButton.handleInput(input);
@@ -532,7 +532,7 @@ void Scene::run() {
     for (uint i = 0; i < _mapAccessSceneIDs.size(); ++i) {
         if (_sceneState.currentScene.sceneID == _mapAccessSceneIDs[i]) {
             if (_mapHotspot.contains(input.mousePos)) {
-                NanEngine.cursorManager->setCursorType(CursorManager::kHotspotArrow);
+                g_nancy->cursorManager->setCursorType(CursorManager::kHotspotArrow);
 
                 if (input.input & NancyInput::kLeftMouseButtonUp) {
                     requestStateChange(NancyEngine::kMap);
@@ -545,7 +545,7 @@ void Scene::run() {
 }
 
 void Scene::initStaticData() {
-    Common::SeekableReadStream *chunk = NanEngine.getBootChunkStream("MAP");
+    Common::SeekableReadStream *chunk = g_nancy->getBootChunkStream("MAP");
     chunk->seek(0x8A);
     readRect(*chunk, _mapHotspot);
 
@@ -560,7 +560,7 @@ void Scene::initStaticData() {
     _mapAccessSceneIDs.push_back(0x4E2);
     _mapAccessSceneIDs.push_back(0x682);
 
-    Common::SeekableReadStream *fr = NanEngine.getBootChunkStream("FR0");
+    Common::SeekableReadStream *fr = g_nancy->getBootChunkStream("FR0");
     fr->seek(0);
 
     _frame.init(fr->readString());
@@ -569,7 +569,7 @@ void Scene::initStaticData() {
     _inventoryBox.init();
     _menuButton.init();
     _helpButton.init();
-    NanEngine.cursorManager->showCursor(true);
+    g_nancy->cursorManager->showCursor(true);
 
     _state = kLoad;
 }
diff --git a/engines/nancy/state/scene.h b/engines/nancy/state/scene.h
index dfb4aa0113..74924b221b 100644
--- a/engines/nancy/state/scene.h
+++ b/engines/nancy/state/scene.h
@@ -130,7 +130,7 @@ public:
     void addItemToInventory(uint16 id);
     void removeItemFromInventory(uint16 id, bool pickUp = true);
     int16 getHeldItem() const { return _flags.heldItem; }
-    void setHeldItem(int16 id) { _flags.heldItem = id; NanEngine.cursorManager->setCursorItemID(id); }
+    void setHeldItem(int16 id) { _flags.heldItem = id; g_nancy->cursorManager->setCursorItemID(id); }
     NancyFlag hasItem(int16 id) const { return _flags.items[id]; }
 
     void setEventFlag(int16 label, NancyFlag flag = kTrue);
diff --git a/engines/nancy/ui/button.cpp b/engines/nancy/ui/button.cpp
index 5dfd34d066..da26812ba9 100644
--- a/engines/nancy/ui/button.cpp
+++ b/engines/nancy/ui/button.cpp
@@ -36,7 +36,7 @@ namespace UI {
 
 void Button::handleInput(NancyInput &input) {
     if (_screenPosition.contains(input.mousePos)) {
-        NanEngine.cursorManager->setCursorType(CursorManager::kHotspotArrow);
+        g_nancy->cursorManager->setCursorType(CursorManager::kHotspotArrow);
 
         if (input.input & NancyInput::kLeftMouseButtonUp) {
             onClick();
@@ -45,12 +45,12 @@ void Button::handleInput(NancyInput &input) {
 }
 
 void MenuButton::init() {
-    Common::SeekableReadStream *bsum = NanEngine.getBootChunkStream("BSUM");
+    Common::SeekableReadStream *bsum = g_nancy->getBootChunkStream("BSUM");
 
     bsum->seek(0x184, SEEK_SET);
     Common::Rect src;
     readRect(*bsum, src);
-    _drawSurface.create(NanEngine.graphicsManager->object0, src);
+    _drawSurface.create(g_nancy->graphicsManager->object0, src);
     bsum->skip(16);
     readRect(*bsum, _screenPosition);
     setVisible(false);
@@ -60,17 +60,17 @@ void MenuButton::init() {
 
 void MenuButton::onClick() {
     NancySceneState.requestStateChange(NancyEngine::kMainMenu);
-    NanEngine.sound->playSound(0x18);
+    g_nancy->sound->playSound(0x18);
     setVisible(true);
 }
 
 void HelpButton::init() {
-    Common::SeekableReadStream *bsum = NanEngine.getBootChunkStream("BSUM");
+    Common::SeekableReadStream *bsum = g_nancy->getBootChunkStream("BSUM");
 
     bsum->seek(0x194, SEEK_SET);
     Common::Rect src;
     readRect(*bsum, src);
-    _drawSurface.create(NanEngine.graphicsManager->object0, src);
+    _drawSurface.create(g_nancy->graphicsManager->object0, src);
     bsum->skip(16);
     readRect(*bsum, _screenPosition);
     setVisible(false);
@@ -80,7 +80,7 @@ void HelpButton::init() {
 
 void HelpButton::onClick() {
     NancySceneState.requestStateChange(NancyEngine::kHelp);
-    NanEngine.sound->playSound(0x18);
+    g_nancy->sound->playSound(0x18);
     setVisible(true);
 }
 
diff --git a/engines/nancy/ui/fullscreenimage.cpp b/engines/nancy/ui/fullscreenimage.cpp
index 7597f08d71..e6dee5dce8 100644
--- a/engines/nancy/ui/fullscreenimage.cpp
+++ b/engines/nancy/ui/fullscreenimage.cpp
@@ -30,7 +30,7 @@ namespace Nancy {
 namespace UI {
 
 void FullScreenImage::init(Common::String imageName) {
-    NanEngine.resource->loadImage(imageName, _drawSurface);
+    g_nancy->resource->loadImage(imageName, _drawSurface);
 
     Common::Rect srcBounds = Common::Rect(0,0, _drawSurface.w, _drawSurface.h);
     _screenPosition = srcBounds;
diff --git a/engines/nancy/ui/inventorybox.cpp b/engines/nancy/ui/inventorybox.cpp
index 7564b983db..cfbc8336f5 100644
--- a/engines/nancy/ui/inventorybox.cpp
+++ b/engines/nancy/ui/inventorybox.cpp
@@ -36,7 +36,7 @@ namespace Nancy {
 namespace UI {
 
 void InventoryBox::init() {
-    Common::SeekableReadStream &stream = *NanEngine.getBootChunkStream("INV");
+    Common::SeekableReadStream &stream = *g_nancy->getBootChunkStream("INV");
     stream.seek(0, SEEK_SET);
 
     _order.clear();
@@ -72,7 +72,7 @@ void InventoryBox::init() {
         readRect(stream, _itemDescriptions[i].sourceRect);
     }
 
-    NanEngine.resource->loadImage(inventoryBoxIconsImageName, _iconsSurface);
+    g_nancy->resource->loadImage(inventoryBoxIconsImageName, _iconsSurface);
     
     uint numItems = 11; // TODO
     _fullInventorySurface.create(_screenPosition.width(), _screenPosition.height() * ((numItems / 4) + 1), GraphicsManager::screenPixelFormat);
@@ -116,16 +116,16 @@ void InventoryBox::handleInput(NancyInput &input) {
     for (uint i = 0; i < 4; ++i) {
         if (_itemHotspots[i].hotspot.contains(input.mousePos)) {
             if (NancySceneState.getHeldItem() != -1) {
-                NanEngine.cursorManager->setCursorType(CursorManager::kHotspotArrow);
+                g_nancy->cursorManager->setCursorType(CursorManager::kHotspotArrow);
                 if (input.input & NancyInput::kLeftMouseButtonUp) {
                     NancySceneState.addItemToInventory(NancySceneState.getHeldItem());
-                    NanEngine.sound->playSound(0x16);
+                    g_nancy->sound->playSound(0x16);
                 }                
             } else if (_itemHotspots[i].itemID != -1) {
-                NanEngine.cursorManager->setCursorType(CursorManager::kHotspotArrow);
+                g_nancy->cursorManager->setCursorType(CursorManager::kHotspotArrow);
                 if (input.input & NancyInput::kLeftMouseButtonUp) {
                     NancySceneState.removeItemFromInventory(_itemHotspots[i].itemID);
-                    NanEngine.sound->playSound(0x18);
+                    g_nancy->sound->playSound(0x18);
                 }
             }
             break;
@@ -209,7 +209,7 @@ void InventoryBox::InventoryScrollbar::init() {
     Common::Rect &srcBounds = _parent->_sliderSource;
     Common::Point &topPosition = _parent->_sliderDefaultDest;
 
-    _drawSurface.create(NanEngine.graphicsManager->object0, srcBounds);
+    _drawSurface.create(g_nancy->graphicsManager->object0, srcBounds);
 
     _startPosition = topPosition;
     _startPosition.x -= srcBounds.width() / 2;
@@ -235,7 +235,7 @@ void InventoryBox::Shades::init() {
 }
 
 void InventoryBox::Shades::updateGraphics() {
-    Time time = NanEngine.getTotalPlayTime();
+    Time time = g_nancy->getTotalPlayTime();
     if (_areOpen) {
         if (_curFrame < 7 && time > _nextFrameTime) {
             setAnimationFrame(++_curFrame);
@@ -243,7 +243,7 @@ void InventoryBox::Shades::updateGraphics() {
 
             if (!_soundTriggered) {
                 _soundTriggered = true;
-                NanEngine.sound->playSound(0x12);
+                g_nancy->sound->playSound(0x12);
             }
         }
     } else {
@@ -253,7 +253,7 @@ void InventoryBox::Shades::updateGraphics() {
 
             if (!_soundTriggered) {
                 _soundTriggered = true;
-                NanEngine.sound->playSound(0x12);
+                g_nancy->sound->playSound(0x12);
             }
         }
     }
@@ -264,7 +264,7 @@ void InventoryBox::Shades::updateGraphics() {
 }
 
 void InventoryBox::Shades::setAnimationFrame(uint frame) {
-    Graphics::ManagedSurface &object0 = NanEngine.graphicsManager->object0;
+    Graphics::ManagedSurface &object0 = g_nancy->graphicsManager->object0;
     Common::Rect srcRect;
     Common::Point destPoint;
 
diff --git a/engines/nancy/ui/scrollbar.cpp b/engines/nancy/ui/scrollbar.cpp
index a6203b5a5b..1346f21353 100644
--- a/engines/nancy/ui/scrollbar.cpp
+++ b/engines/nancy/ui/scrollbar.cpp
@@ -41,7 +41,7 @@ void Scrollbar::init() {
 
 void Scrollbar::handleInput(NancyInput &input) {
     if (_screenPosition.contains(input.mousePos)) {
-        NanEngine.cursorManager->setCursorType(CursorManager::kHotspotArrow);
+        g_nancy->cursorManager->setCursorType(CursorManager::kHotspotArrow);
 
         if (input.input & NancyInput::kLeftMouseButtonDown && !_isClicked) {
             // Begin click and hold
diff --git a/engines/nancy/ui/textbox.cpp b/engines/nancy/ui/textbox.cpp
index d60cbd5e29..ad9c87eafb 100644
--- a/engines/nancy/ui/textbox.cpp
+++ b/engines/nancy/ui/textbox.cpp
@@ -48,7 +48,7 @@ const char Textbox::tabToken[] = "<t>";
 const char Textbox::telephoneEndToken[] = "<e>";
 
 void Textbox::init() {    
-    Common::SeekableReadStream *chunk = NanEngine.getBootChunkStream("TBOX");
+    Common::SeekableReadStream *chunk = g_nancy->getBootChunkStream("TBOX");
     chunk->seek(0);
     readRect(*chunk, _scrollbarSourceBounds);
 
@@ -69,7 +69,7 @@ void Textbox::init() {
     chunk->seek(0x1FE, SEEK_SET);
     _fontID = chunk->readUint16LE();
 
-    chunk = NanEngine.getBootChunkStream("BSUM");
+    chunk = g_nancy->getBootChunkStream("BSUM");
     chunk->seek(0x164);
     readRect(*chunk, _screenPosition);
 
@@ -108,7 +108,7 @@ void Textbox::handleInput(NancyInput &input) {
         Common::Rect hotspot = _hotspots[i];
         hotspot.translate(0, -_drawSurface.getOffsetFromOwner().y);
         if (convertToScreen(hotspot).findIntersectingRect(_screenPosition).contains(input.mousePos)) {
-            NanEngine.cursorManager->setCursorType(CursorManager::kHotspotArrow);
+            g_nancy->cursorManager->setCursorType(CursorManager::kHotspotArrow);
 
             if (input.input & NancyInput::kLeftMouseButtonUp) {
                 input.input &= ~NancyInput::kLeftMouseButtonUp;
@@ -126,7 +126,7 @@ void Textbox::drawTextbox() {
 
     _numLines = 0;
 
-    Font *font = NanEngine.graphicsManager->getFont(_fontID);
+    Font *font = g_nancy->graphicsManager->getFont(_fontID);
 
     uint maxWidth = _fullSurface.w - _borderWidth * 2;
     uint lineDist = _lineHeight + _lineHeight / 4;
@@ -289,7 +289,7 @@ void Textbox::TextboxScrollbar::init() {
     Common::Rect &srcBounds = _parent->_scrollbarSourceBounds;
     Common::Point &topPosition = _parent->_scrollbarDefaultDest;
 
-    _drawSurface.create(NanEngine.graphicsManager->object0, srcBounds);
+    _drawSurface.create(g_nancy->graphicsManager->object0, srcBounds);
 
     _startPosition = topPosition;
     _startPosition.x -= srcBounds.width() / 2;
diff --git a/engines/nancy/ui/viewport.cpp b/engines/nancy/ui/viewport.cpp
index 2b43eb8d8c..ca28362c8f 100644
--- a/engines/nancy/ui/viewport.cpp
+++ b/engines/nancy/ui/viewport.cpp
@@ -36,7 +36,7 @@ namespace UI {
 
 // does NOT put the object in a valid state until loadVideo is called
 void Viewport::init() {
-    Common::SeekableReadStream *viewChunk = NanEngine.getBootChunkStream("VIEW");
+    Common::SeekableReadStream *viewChunk = g_nancy->getBootChunkStream("VIEW");
     viewChunk->seek(0);
 
     Common::Rect dest;
@@ -59,11 +59,11 @@ void Viewport::init() {
 }
 
 void Viewport::handleInput(NancyInput &input) {
-    Time playTime = NanEngine.getTotalPlayTime();
+    Time playTime = g_nancy->getTotalPlayTime();
     byte direction = 0;
 
     if (_screenPosition.contains(input.mousePos)) {
-        NanEngine.cursorManager->setCursorType(CursorManager::kNormal);
+        g_nancy->cursorManager->setCursorType(CursorManager::kNormal);
     }
 
     // Do not handle hotspots marked as incative and ignore diagonals if intersecting hotspots are not active
@@ -120,7 +120,7 @@ void Viewport::handleInput(NancyInput &input) {
     }
 
     if (direction) {
-        NanEngine.cursorManager->setCursorType(CursorManager::kMove);
+        g_nancy->cursorManager->setCursorType(CursorManager::kMove);
 
         if (input.input & NancyInput::kRightMouseButton) {
             direction |= kMoveFast;


Commit: a7373264f2dc45ed93f912a14a282cba9cfd2558
    https://github.com/scummvm/scummvm/commit/a7373264f2dc45ed93f912a14a282cba9cfd2558
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Pass string arguments by reference

Fixed a couple instances of string function arguments not being passed by reference.

Changed paths:
    engines/nancy/iff.cpp
    engines/nancy/iff.h
    engines/nancy/ui/fullscreenimage.cpp
    engines/nancy/ui/fullscreenimage.h


diff --git a/engines/nancy/iff.cpp b/engines/nancy/iff.cpp
index f6e054dbf3..2684691c9f 100644
--- a/engines/nancy/iff.cpp
+++ b/engines/nancy/iff.cpp
@@ -114,7 +114,7 @@ const byte *IFF::getChunk(uint32 id, uint &size, uint index) const {
 	return nullptr;
 }
 
-Common::SeekableReadStream *IFF::getChunkStream(Common::String id, uint index) const {
+Common::SeekableReadStream *IFF::getChunkStream(const Common::String &id, uint index) const {
 	uint size;
 	const byte *chunk = getChunk(stringToId(id), size, index);
 
diff --git a/engines/nancy/iff.h b/engines/nancy/iff.h
index 3f26d4b3e7..ca9093b4b9 100644
--- a/engines/nancy/iff.h
+++ b/engines/nancy/iff.h
@@ -45,7 +45,7 @@ public:
 
 	bool load();
 	const byte *getChunk(uint32 id, uint &size, uint index = 0) const;
-	Common::SeekableReadStream *getChunkStream(Common::String id, uint index = 0) const;
+	Common::SeekableReadStream *getChunkStream(const Common::String &id, uint index = 0) const;
 
 	// Debugger functions
 	void list(Common::Array<Common::String> &nameList);
diff --git a/engines/nancy/ui/fullscreenimage.cpp b/engines/nancy/ui/fullscreenimage.cpp
index e6dee5dce8..10b2ed056a 100644
--- a/engines/nancy/ui/fullscreenimage.cpp
+++ b/engines/nancy/ui/fullscreenimage.cpp
@@ -29,7 +29,7 @@
 namespace Nancy {
 namespace UI {
 
-void FullScreenImage::init(Common::String imageName) {
+void FullScreenImage::init(const Common::String &imageName) {
     g_nancy->resource->loadImage(imageName, _drawSurface);
 
     Common::Rect srcBounds = Common::Rect(0,0, _drawSurface.w, _drawSurface.h);
diff --git a/engines/nancy/ui/fullscreenimage.h b/engines/nancy/ui/fullscreenimage.h
index b333536f9f..8649472edd 100644
--- a/engines/nancy/ui/fullscreenimage.h
+++ b/engines/nancy/ui/fullscreenimage.h
@@ -33,7 +33,8 @@ public:
     FullScreenImage() : RenderObject() {}
     virtual ~FullScreenImage() =default;
 
-    void init(Common::String imageName);
+    void init(const Common::String &imageName);
+
 protected:
     virtual void init() override {}
     virtual uint16 getZOrder() const override { return 0; }


Commit: b6df945ae1af0128cc61ce522e893073fab5977e
    https://github.com/scummvm/scummvm/commit/b6df945ae1af0128cc61ce522e893073fab5977e
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Formatting fixes

Fixed a couple of poorly indented lines and a missing newline at the end of a file.

Changed paths:
    engines/nancy/console.cpp
    engines/nancy/ui/inventorybox.h
    engines/nancy/ui/viewport.cpp


diff --git a/engines/nancy/console.cpp b/engines/nancy/console.cpp
index 59a09690a8..f0e28cac49 100644
--- a/engines/nancy/console.cpp
+++ b/engines/nancy/console.cpp
@@ -327,7 +327,7 @@ bool NancyConsole::Cmd_loadScene(int argc, const char **argv) {
 	}
 
 	Common::String sceneName = Common::String::format("S%s", argv[1]);
-    IFF iff(sceneName);
+	IFF iff(sceneName);
 	if (!iff.load()) {
 		debugPrintf("Invalid scene S%s\n", argv[1]);
 		return true;
diff --git a/engines/nancy/ui/inventorybox.h b/engines/nancy/ui/inventorybox.h
index 769c73ea61..b0b75b6c71 100644
--- a/engines/nancy/ui/inventorybox.h
+++ b/engines/nancy/ui/inventorybox.h
@@ -44,7 +44,7 @@ class Scene;
 namespace UI {
 
 class InventoryBox : public RenderObject {
-	friend class InventoryScrollbar;
+    friend class InventoryScrollbar;
     friend class Shades;
     friend class Nancy::State::Scene;
 
diff --git a/engines/nancy/ui/viewport.cpp b/engines/nancy/ui/viewport.cpp
index ca28362c8f..cea93be76d 100644
--- a/engines/nancy/ui/viewport.cpp
+++ b/engines/nancy/ui/viewport.cpp
@@ -302,4 +302,4 @@ void Viewport::enableEdges(byte edges) {
 }
 
 } // End of namespace UI
-} // End of namespace Nancy
\ No newline at end of file
+} // End of namespace Nancy


Commit: 6be4617e2642a15a3e2ca69e405fa11454ed644d
    https://github.com/scummvm/scummvm/commit/6be4617e2642a15a3e2ca69e405fa11454ed644d
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Make Textbox::getInnerHeight() const

Textbox::getInnerHeight() makes no changes to its class so it has now been properly marked as const.

Changed paths:
    engines/nancy/ui/textbox.cpp
    engines/nancy/ui/textbox.h


diff --git a/engines/nancy/ui/textbox.cpp b/engines/nancy/ui/textbox.cpp
index ad9c87eafb..b0742d5d2e 100644
--- a/engines/nancy/ui/textbox.cpp
+++ b/engines/nancy/ui/textbox.cpp
@@ -280,7 +280,7 @@ void Textbox::onScrollbarMove() {
     _needsRedraw = true;
 }
 
-uint16 Textbox::getInnerHeight() {
+uint16 Textbox::getInnerHeight() const {
     uint lineDist = _lineHeight + _lineHeight / 4;
     return _numLines * lineDist + _firstLineOffset + lineDist / 2;
 }
diff --git a/engines/nancy/ui/textbox.h b/engines/nancy/ui/textbox.h
index bcb811e22d..59cbcf8705 100644
--- a/engines/nancy/ui/textbox.h
+++ b/engines/nancy/ui/textbox.h
@@ -70,7 +70,7 @@ protected:
     virtual uint16 getZOrder() const override { return 6; }
 
 private:
-    uint16 getInnerHeight();
+    uint16 getInnerHeight() const;
     void onScrollbarMove();
 
     struct Response {


Commit: 8cfd4dba235d416356677696ec67249e5cee9213
    https://github.com/scummvm/scummvm/commit/8cfd4dba235d416356677696ec67249e5cee9213
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Replace malloc with new

Replace an malloc used in iff.cpp with a new operator, fixing a warning about a potential nullptr return.

Changed paths:
    engines/nancy/iff.cpp


diff --git a/engines/nancy/iff.cpp b/engines/nancy/iff.cpp
index 2684691c9f..9c7392e233 100644
--- a/engines/nancy/iff.cpp
+++ b/engines/nancy/iff.cpp
@@ -119,7 +119,7 @@ Common::SeekableReadStream *IFF::getChunkStream(const Common::String &id, uint i
 	const byte *chunk = getChunk(stringToId(id), size, index);
 
 	if (chunk) {
-		byte *dup = (byte *)malloc(size);
+		byte *dup = new byte[size];
 		memcpy(dup, chunk, size);
 		return new Common::MemoryReadStream(dup, size, DisposeAfterUse::YES);
 	}


Commit: abe1d47137d259c1775945c455cb47e8772159ad
    https://github.com/scummvm/scummvm/commit/abe1d47137d259c1775945c455cb47e8772159ad
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Replace itoa with U32String::itoa

Replaced all instances of itoa with the implementation inside U32String, hopefully fixing a bunch of compiler errors.

Changed paths:
    engines/nancy/cheat.cpp


diff --git a/engines/nancy/cheat.cpp b/engines/nancy/cheat.cpp
index dba55e96b8..25bfc5fe51 100644
--- a/engines/nancy/cheat.cpp
+++ b/engines/nancy/cheat.cpp
@@ -29,6 +29,7 @@
 #include "common/winexe.h"
 #include "common/stream.h"
 #include "common/translation.h"
+#include "common/ustr.h"
 
 #include "gui/widgets/tab.h"
 #include "gui/widgets/edittext.h"
@@ -56,40 +57,40 @@ CheatDialog::CheatDialog() : GUI::Dialog(20, 20, 600, 440) {
     
     new GUI::StaticTextWidget(_tabs, 30, 20, 150, 20, _("Scene Data"), Graphics::kTextAlignLeft);
     _restartScene = new GUI::CheckboxWidget(_tabs, 35, 50, 150, 20, _("Restart the Scene"), _(""));
-    _scene = new GUI::EditTextWidget(_tabs, 35, 75, 45, 20, _(itoa(scene.sceneID, buf, 10)), _(""), kInputSceneNr, kInputSceneNr);
+    _scene = new GUI::EditTextWidget(_tabs, 35, 75, 45, 20, _(Common::U32String::itoa(scene.sceneID, buf, 10)), _(""), kInputSceneNr, kInputSceneNr);
     new GUI::StaticTextWidget(_tabs, 85, 75, 150, 20, _("Scene Number"), Graphics::kTextAlignLeft);
-    _frame = new GUI::EditTextWidget(_tabs, 35, 100, 45, 20, _(itoa(scene.frameID, buf, 10)), _(""), kInputFrameNr, kInputFrameNr);
+    _frame = new GUI::EditTextWidget(_tabs, 35, 100, 45, 20, _(Common::U32String::itoa(scene.frameID, buf, 10)), _(""), kInputFrameNr, kInputFrameNr);
     new GUI::StaticTextWidget(_tabs, 85, 100, 150, 20, _("Frame Number"), Graphics::kTextAlignLeft);
-    _offset = new GUI::EditTextWidget(_tabs, 35, 125, 45, 20, _(itoa(scene.verticalOffset, buf, 10)), _(""), kInputScroll, kInputScroll);
+    _offset = new GUI::EditTextWidget(_tabs, 35, 125, 45, 20, _(Common::U32String::itoa(scene.verticalOffset, buf, 10)), _(""), kInputScroll, kInputScroll);
     new GUI::StaticTextWidget(_tabs, 85, 125, 150, 20, _("Background Top (Y)"), Graphics::kTextAlignLeft);
 
     new GUI::StaticTextWidget(_tabs, 30, 160, 150, 20, _("Hints Remaining"), Graphics::kTextAlignLeft);
     new GUI::StaticTextWidget(_tabs, 35, 185, 45, 20, _("Easy"), Graphics::kTextAlignLeft);
-    _hintsRemainingEasy = new GUI::EditTextWidget(_tabs, 35, 205, 45, 20, _(itoa(NancySceneState._hintsRemaining[0], buf, 10)), _(""), kInputHintsEasy, kInputHintsEasy);
+    _hintsRemainingEasy = new GUI::EditTextWidget(_tabs, 35, 205, 45, 20, _(Common::U32String::itoa(NancySceneState._hintsRemaining[0], buf, 10)), _(""), kInputHintsEasy, kInputHintsEasy);
     new GUI::StaticTextWidget(_tabs, 85, 185, 45, 20, _("Medium"), Graphics::kTextAlignLeft);
-    _hintsRemainingMedium = new GUI::EditTextWidget(_tabs, 85, 205, 45, 20, _(itoa(NancySceneState._hintsRemaining[1], buf, 10)), _(""), kInputHintsMedium, kInputHintsMedium);
+    _hintsRemainingMedium = new GUI::EditTextWidget(_tabs, 85, 205, 45, 20, _(Common::U32String::itoa(NancySceneState._hintsRemaining[1], buf, 10)), _(""), kInputHintsMedium, kInputHintsMedium);
     new GUI::StaticTextWidget(_tabs, 135, 185, 45, 20, _("Hard"), Graphics::kTextAlignLeft);
-    _hintsRemainingHard = new GUI::EditTextWidget(_tabs, 135, 205, 45, 20, _(itoa(NancySceneState._hintsRemaining[2], buf, 10)), _(""), kInputHintsHard, kInputHintsHard);
+    _hintsRemainingHard = new GUI::EditTextWidget(_tabs, 135, 205, 45, 20, _(Common::U32String::itoa(NancySceneState._hintsRemaining[2], buf, 10)), _(""), kInputHintsHard, kInputHintsHard);
     
     new GUI::StaticTextWidget(_tabs, 250, 20, 150, 20, _("Player Data"), Graphics::kTextAlignLeft);
     new GUI::StaticTextWidget(_tabs, 255, 50, 150, 20, _("Player Time:"), Graphics::kTextAlignLeft);
-    _playerTimeDays = new GUI::EditTextWidget(_tabs, 255, 75, 35, 20, _(itoa(playerTime.getDays(), buf, 10)), _(""), kInputPlayerTime, kInputPlayerTime);
+    _playerTimeDays = new GUI::EditTextWidget(_tabs, 255, 75, 35, 20, _(Common::U32String::itoa(playerTime.getDays(), buf, 10)), _(""), kInputPlayerTime, kInputPlayerTime);
     new GUI::StaticTextWidget(_tabs, 295, 75, 40, 20, _("Days"), Graphics::kTextAlignLeft);
-    _playerTimeHours =new GUI::EditTextWidget(_tabs, 335, 75, 35, 20, _(itoa(playerTime.getHours(), buf, 10)), _(""), kInputPlayerTime, kInputPlayerTime);
+    _playerTimeHours =new GUI::EditTextWidget(_tabs, 335, 75, 35, 20, _(Common::U32String::itoa(playerTime.getHours(), buf, 10)), _(""), kInputPlayerTime, kInputPlayerTime);
     new GUI::StaticTextWidget(_tabs, 375, 75, 40, 20, _("Hours"), Graphics::kTextAlignLeft);
-    _playerTimeMinutes =new GUI::EditTextWidget(_tabs, 415, 75, 35, 20, _(itoa(playerTime.getMinutes(), buf, 10)), _(""), kInputPlayerTime, kInputPlayerTime);
+    _playerTimeMinutes =new GUI::EditTextWidget(_tabs, 415, 75, 35, 20, _(Common::U32String::itoa(playerTime.getMinutes(), buf, 10)), _(""), kInputPlayerTime, kInputPlayerTime);
     new GUI::StaticTextWidget(_tabs, 455, 75, 50, 20, _("Minutes"), Graphics::kTextAlignLeft);
-    _difficulty = new GUI::EditTextWidget(_tabs, 255, 105, 35, 20, _(itoa(NancySceneState._difficulty, buf, 10)), _(""), kInputDifficulty, kInputDifficulty);
+    _difficulty = new GUI::EditTextWidget(_tabs, 255, 105, 35, 20, _(Common::U32String::itoa(NancySceneState._difficulty, buf, 10)), _(""), kInputDifficulty, kInputDifficulty);
     new GUI::StaticTextWidget(_tabs, 295, 105, 150, 20, _("Player Difficulty Level"), Graphics::kTextAlignLeft);
 
     new GUI::StaticTextWidget(_tabs, 250, 140, 150, 20, _("Player Data"), Graphics::kTextAlignLeft);
     _timerOn = new GUI::CheckboxWidget(_tabs, 255, 170, 150, 20, _("Timer On"), _(""));
     _timerOn->setState(timerIsActive);
-    _timerHours = new GUI::EditTextWidget(_tabs, 255, 195, 35, 20, _(itoa(timerTime.getTotalHours(), buf, 10)), _(""), kInputTimer, kInputTimer);
+    _timerHours = new GUI::EditTextWidget(_tabs, 255, 195, 35, 20, _(Common::U32String::itoa(timerTime.getTotalHours(), buf, 10)), _(""), kInputTimer, kInputTimer);
     new GUI::StaticTextWidget(_tabs, 295, 195, 40, 20, _("Hours"), Graphics::kTextAlignLeft);
-    _timerMinutes = new GUI::EditTextWidget(_tabs, 335, 195, 35, 20, _(itoa(timerTime.getMinutes(), buf, 10)), _(""), kInputTimer, kInputTimer);
+    _timerMinutes = new GUI::EditTextWidget(_tabs, 335, 195, 35, 20, _(Common::U32String::itoa(timerTime.getMinutes(), buf, 10)), _(""), kInputTimer, kInputTimer);
     new GUI::StaticTextWidget(_tabs, 375, 195, 50, 20, _("Minutes"), Graphics::kTextAlignLeft);
-    _timerSeconds = new GUI::EditTextWidget(_tabs, 425, 195, 35, 20, _(itoa(timerTime.getSeconds(), buf, 10)), _(""), kInputTimer, kInputTimer);
+    _timerSeconds = new GUI::EditTextWidget(_tabs, 425, 195, 35, 20, _(Common::U32String::itoa(timerTime.getSeconds(), buf, 10)), _(""), kInputTimer, kInputTimer);
     new GUI::StaticTextWidget(_tabs, 465, 195, 50, 20, _("Seconds"), Graphics::kTextAlignLeft);
 
     _tabs->addTab(_("Inventory"), _("Cheat.Inventory"));
@@ -248,7 +249,7 @@ void CheatDialog::sanitizeInput(GUI::EditTextWidget *textWidget, int maxValue) {
         int number = atoi(Common::String(str).c_str());
         if (number > maxValue) {
             char *buf = new char[str.size() + 1];
-            textWidget->setEditString(_(itoa(maxValue, buf, 10)));
+            textWidget->setEditString(_(Common::U32String::itoa(maxValue, buf, 10)));
             delete[] buf;
         }
     }


Commit: 3e3a8cd5c772d34cc5f20678906c33d3f01a168c
    https://github.com/scummvm/scummvm/commit/3e3a8cd5c772d34cc5f20678906c33d3f01a168c
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Properly mark overriden virtual functions

Added the override keyword to a couple of virtual functions that were missing it.

Changed paths:
    engines/nancy/nancy.h
    engines/nancy/video.h


diff --git a/engines/nancy/nancy.h b/engines/nancy/nancy.h
index 87195df7c1..f00bee29f5 100644
--- a/engines/nancy/nancy.h
+++ b/engines/nancy/nancy.h
@@ -109,7 +109,7 @@ public:
 
 	static NancyEngine *create(GameType type, OSystem *syst, const NancyGameDescription *gd);
 
-	GUI::Debugger *getDebugger();
+	virtual GUI::Debugger *getDebugger() override;
 
 	virtual bool hasFeature(EngineFeature f) const override;
 
@@ -160,7 +160,7 @@ private:
 		State::State *previousState = nullptr;
 	};
 
-	Common::Error run();
+	virtual Common::Error run() override;
 
 	void bootGameEngine();
 
diff --git a/engines/nancy/video.h b/engines/nancy/video.h
index afaf15257c..ce747c351a 100644
--- a/engines/nancy/video.h
+++ b/engines/nancy/video.h
@@ -61,7 +61,7 @@ private:
 		virtual int getCurFrame() const override { return _curFrame; }
 		virtual int getFrameCount() const override { return _frameCount; }
 		virtual bool isSeekable() const override { return true; }
-		virtual bool seek(const Audio::Timestamp &time);
+		virtual bool seek(const Audio::Timestamp &time) override;
 		virtual bool setReverse(bool reverse) override;
 		virtual bool isReversed() const override { return _reversed; }
 		virtual bool endOfTrack() const override;


Commit: 6728c96e68867450df2d491655db5c31510221aa
    https://github.com/scummvm/scummvm/commit/6728c96e68867450df2d491655db5c31510221aa
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add underscore to class member names

Fixed many instances of class member names not beginning with an underscore.

Changed paths:
    engines/nancy/action/actionmanager.cpp
    engines/nancy/action/actionrecord.h
    engines/nancy/action/leverpuzzle.cpp
    engines/nancy/action/leverpuzzle.h
    engines/nancy/action/orderingpuzzle.cpp
    engines/nancy/action/orderingpuzzle.h
    engines/nancy/action/passwordpuzzle.cpp
    engines/nancy/action/passwordpuzzle.h
    engines/nancy/action/primaryvideo.cpp
    engines/nancy/action/primaryvideo.h
    engines/nancy/action/recordtypes.cpp
    engines/nancy/action/recordtypes.h
    engines/nancy/action/rotatinglockpuzzle.cpp
    engines/nancy/action/rotatinglockpuzzle.h
    engines/nancy/action/secondarymovie.cpp
    engines/nancy/action/secondarymovie.h
    engines/nancy/action/secondaryvideo.cpp
    engines/nancy/action/secondaryvideo.h
    engines/nancy/action/sliderpuzzle.cpp
    engines/nancy/action/sliderpuzzle.h
    engines/nancy/action/staticbitmapanim.cpp
    engines/nancy/action/staticbitmapanim.h
    engines/nancy/action/telephone.cpp
    engines/nancy/action/telephone.h
    engines/nancy/console.cpp
    engines/nancy/cursor.cpp
    engines/nancy/font.cpp
    engines/nancy/graphics.cpp
    engines/nancy/graphics.h
    engines/nancy/iff.cpp
    engines/nancy/input.cpp
    engines/nancy/nancy.cpp
    engines/nancy/nancy.h
    engines/nancy/renderobject.cpp
    engines/nancy/state/credits.cpp
    engines/nancy/state/help.cpp
    engines/nancy/state/logo.cpp
    engines/nancy/state/map.cpp
    engines/nancy/state/scene.cpp
    engines/nancy/state/scene.h
    engines/nancy/ui/button.cpp
    engines/nancy/ui/fullscreenimage.cpp
    engines/nancy/ui/inventorybox.cpp
    engines/nancy/ui/scrollbar.cpp
    engines/nancy/ui/textbox.cpp
    engines/nancy/ui/textbox.h
    engines/nancy/ui/viewport.cpp


diff --git a/engines/nancy/action/actionmanager.cpp b/engines/nancy/action/actionmanager.cpp
index 567edf86f0..25803659f7 100644
--- a/engines/nancy/action/actionmanager.cpp
+++ b/engines/nancy/action/actionmanager.cpp
@@ -41,48 +41,48 @@ namespace Action {
 
 void ActionManager::handleInput(NancyInput &input) {
     for (auto &rec : _records) {
-        if (rec->isActive) {
+        if (rec->_isActive) {
             // Send input to all active records
             rec->handleInput(input);
         }
 
-        if (rec->isActive && rec->hasHotspot && NancySceneState.getViewport().convertViewportToScreen(rec->hotspot).contains(input.mousePos)) {
-            g_nancy->cursorManager->setCursorType(rec->getHoverCursor());
+        if (rec->_isActive && rec->_hasHotspot && NancySceneState.getViewport().convertViewportToScreen(rec->_hotspot).contains(input.mousePos)) {
+            g_nancy->_cursorManager->setCursorType(rec->getHoverCursor());
 
             if (input.input & NancyInput::kLeftMouseButtonUp) {
                 input.input &= ~NancyInput::kLeftMouseButtonUp;
 
                 bool shouldTrigger = false;
                 int16 heldItem = NancySceneState.getHeldItem();
-                if (rec->itemRequired != -1) {
-                    if (heldItem == -1 && rec->itemRequired == -2) {
+                if (rec->_itemRequired != -1) {
+                    if (heldItem == -1 && rec->_itemRequired == -2) {
                         shouldTrigger = true;
                     } else {
-                        if (rec->itemRequired <= 100) {
-                            if (heldItem == rec->itemRequired) {
+                        if (rec->_itemRequired <= 100) {
+                            if (heldItem == rec->_itemRequired) {
                                 shouldTrigger = true;
                             }
-                        } else if (rec->itemRequired <= 110 && rec->itemRequired - 100 != heldItem) {
+                        } else if (rec->_itemRequired <= 110 && rec->_itemRequired - 100 != heldItem) {
                             // IDs 100 - 110 mean the record will activate when the object is _not_ the specified one
                             shouldTrigger = true;
                         }
                     }
 
                     if (!shouldTrigger) {
-                        g_nancy->sound->playSound(17); // Hardcoded by original engine
+                        g_nancy->_sound->playSound(17); // Hardcoded by original engine
                     }
                 } else {
                     shouldTrigger = true;
                 }
                 if (shouldTrigger) {
-                    rec->state = ActionRecord::ExecutionState::kActionTrigger;
+                    rec->_state = ActionRecord::ExecutionState::kActionTrigger;
                     
-                    if (rec->itemRequired > 100 && rec->itemRequired <= 110) {
-                        rec->itemRequired -= 100;
+                    if (rec->_itemRequired > 100 && rec->_itemRequired <= 110) {
+                        rec->_itemRequired -= 100;
                     }
 
                     // Re-add the object to the inventory unless it's marked as a one-time use
-                    if (rec->itemRequired == heldItem && rec->itemRequired != -1) {
+                    if (rec->_itemRequired == heldItem && rec->_itemRequired != -1) {
                         if (NancySceneState.getInventoryBox().getItemDescription(heldItem).oneTimeUse != 0) {
                             NancySceneState.getInventoryBox().addItem(heldItem);
                         }
@@ -105,11 +105,11 @@ bool ActionManager::addNewActionRecord(Common::SeekableReadStream &inputData) {
     inputData.seek(0);
     char *descBuf = new char[0x30];
     inputData.read(descBuf, 0x30);
-    newRecord->description = Common::String(descBuf);
+    newRecord->_description = Common::String(descBuf);
     delete[] descBuf;
 
-    newRecord->type = inputData.readByte(); // redundant
-    newRecord->execType = (ActionRecord::ExecutionType)inputData.readByte();
+    newRecord->_type = inputData.readByte(); // redundant
+    newRecord->_execType = (ActionRecord::ExecutionType)inputData.readByte();
 
     uint16 localChunkSize = inputData.pos();
     newRecord->readData(inputData);
@@ -128,8 +128,8 @@ bool ActionManager::addNewActionRecord(Common::SeekableReadStream &inputData) {
         // Initialize the dependencies data
         inputData.seek(localChunkSize);
         for (uint16 i = 0; i < numDependencies; ++i) {
-            newRecord->dependencies.push_back(DependencyRecord());
-            DependencyRecord &dep = newRecord->dependencies.back();
+            newRecord->_dependencies.push_back(DependencyRecord());
+            DependencyRecord &dep = newRecord->_dependencies.back();
 
             dep.type = (DependencyType)inputData.readByte();
             dep.label = inputData.readByte();
@@ -146,7 +146,7 @@ bool ActionManager::addNewActionRecord(Common::SeekableReadStream &inputData) {
         }
     } else {
         // Set new record to active if it doesn't depend on anything
-        newRecord->isActive = true;
+        newRecord->_isActive = true;
     }
 
     _records.push_back(newRecord);
@@ -154,68 +154,68 @@ bool ActionManager::addNewActionRecord(Common::SeekableReadStream &inputData) {
     debugC(1, kDebugActionRecord, "Loaded action record %i, type %s, typeID %i, description \"%s\", execType == %s",
             _records.size() - 1,
             newRecord->getRecordTypeName().c_str(),
-            newRecord->type,
-            newRecord->description.c_str(),
-            newRecord->execType == ActionRecord::kRepeating ? "kRepeating" : "kOneShot");
-    for (uint i = 0; i < newRecord->dependencies.size(); ++i) {
+            newRecord->_type,
+            newRecord->_description.c_str(),
+            newRecord->_execType == ActionRecord::kRepeating ? "kRepeating" : "kOneShot");
+    for (uint i = 0; i < newRecord->_dependencies.size(); ++i) {
         debugCN(1, kDebugActionRecord, "\tDependency %i: type ", i);
-        switch (newRecord->dependencies[i].type) {
+        switch (newRecord->_dependencies[i].type) {
         case kNone : 
             debugCN(1, kDebugActionRecord, "kNone");
             break;
         case kInventory :
             debugCN(1, kDebugActionRecord, "kInventory, item ID %i %s",
-                        newRecord->dependencies[i].label,
-                        newRecord->dependencies[i].condition == kTrue ? "is in possession" : "is not in possession");
+                        newRecord->_dependencies[i].label,
+                        newRecord->_dependencies[i].condition == kTrue ? "is in possession" : "is not in possession");
             break;
         case kEventFlag :
             debugCN(1, kDebugActionRecord, "kEventFlag, flag ID %i == %s",
-                        newRecord->dependencies[i].label,
-                        newRecord->dependencies[i].condition == kTrue ? "true" : "false");
+                        newRecord->_dependencies[i].label,
+                        newRecord->_dependencies[i].condition == kTrue ? "true" : "false");
             break;
         case kLogicCondition :
             debugCN(1, kDebugActionRecord, "kLogicCondition, logic condition ID %i == %s",
-                        newRecord->dependencies[i].label,
-                        newRecord->dependencies[i].condition == kTrue ? "true" : "false");
+                        newRecord->_dependencies[i].label,
+                        newRecord->_dependencies[i].condition == kTrue ? "true" : "false");
             break;
         case kTotalTime :
             debugCN(1, kDebugActionRecord, "kTotalTime, %i hours, %i minutes, %i seconds, %i milliseconds",
-                        newRecord->dependencies[i].hours,
-                        newRecord->dependencies[i].minutes,
-                        newRecord->dependencies[i].seconds,
-                        newRecord->dependencies[i].milliseconds);
+                        newRecord->_dependencies[i].hours,
+                        newRecord->_dependencies[i].minutes,
+                        newRecord->_dependencies[i].seconds,
+                        newRecord->_dependencies[i].milliseconds);
             break;
         case kSceneTime :
             debugCN(1, kDebugActionRecord, "kSceneTime, %i hours, %i minutes, %i seconds, %i milliseconds",
-                        newRecord->dependencies[i].hours,
-                        newRecord->dependencies[i].minutes,
-                        newRecord->dependencies[i].seconds,
-                        newRecord->dependencies[i].milliseconds);
+                        newRecord->_dependencies[i].hours,
+                        newRecord->_dependencies[i].minutes,
+                        newRecord->_dependencies[i].seconds,
+                        newRecord->_dependencies[i].milliseconds);
             break;
         case kPlayerTime :
             debugCN(1, kDebugActionRecord, "kPlayerTime, %i days, %i hours, %i minutes, %i seconds",
-                        newRecord->dependencies[i].hours,
-                        newRecord->dependencies[i].minutes,
-                        newRecord->dependencies[i].seconds,
-                        newRecord->dependencies[i].milliseconds);
+                        newRecord->_dependencies[i].hours,
+                        newRecord->_dependencies[i].minutes,
+                        newRecord->_dependencies[i].seconds,
+                        newRecord->_dependencies[i].milliseconds);
             break;
         case kSceneCount :
             debugCN(1, kDebugActionRecord, "kSceneCount, scene ID %i, hit count %s %i",
-                        newRecord->dependencies[i].hours,
-                        newRecord->dependencies[i].milliseconds == 1 ? ">" : newRecord->dependencies[i].milliseconds == 2 ? "<" : "==",
-                        newRecord->dependencies[i].seconds);
+                        newRecord->_dependencies[i].hours,
+                        newRecord->_dependencies[i].milliseconds == 1 ? ">" : newRecord->_dependencies[i].milliseconds == 2 ? "<" : "==",
+                        newRecord->_dependencies[i].seconds);
             break;
         case kResetOnNewDay :
             debugCN(1, kDebugActionRecord, "kResetOnNewDay");
             break;
         case kUseItem :
             debugCN(1, kDebugActionRecord, "kUseItem, item ID %i %s",
-                        newRecord->dependencies[i].label,
-                        newRecord->dependencies[i].condition == kTrue ? "is held" : "is not held");
+                        newRecord->_dependencies[i].label,
+                        newRecord->_dependencies[i].condition == kTrue ? "is held" : "is not held");
             break;
         case kTimeOfDay :
             debugCN(1, kDebugActionRecord, "kTimeOfDay, %s",
-                        newRecord->dependencies[i].label == 0 ? "day" : newRecord->dependencies[i].label == 1 ? "night" : "dusk/dawn");
+                        newRecord->_dependencies[i].label == 0 ? "day" : newRecord->_dependencies[i].label == 1 ? "night" : "dusk/dawn");
             break;
         case kTimerNotDone :
             debugCN(1, kDebugActionRecord, "kTimerNotDone");
@@ -224,13 +224,13 @@ bool ActionManager::addNewActionRecord(Common::SeekableReadStream &inputData) {
             debugCN(1, kDebugActionRecord, "kTimerDone");
             break;
         case kDifficultyLevel :
-            debugCN(1, kDebugActionRecord, "kDifficultyLevel, level %i", newRecord->dependencies[i].condition);
+            debugCN(1, kDebugActionRecord, "kDifficultyLevel, level %i", newRecord->_dependencies[i].condition);
             break;
         default:
             debugCN(1, kDebugActionRecord, "unknown");
             break;
         }
-        debugC(1, kDebugActionRecord, ", orFlag == %s", newRecord->dependencies[i].orFlag == true ? "true" : "false");
+        debugC(1, kDebugActionRecord, ", orFlag == %s", newRecord->_dependencies[i].orFlag == true ? "true" : "false");
     }
 
     return true;
@@ -238,13 +238,13 @@ bool ActionManager::addNewActionRecord(Common::SeekableReadStream &inputData) {
 
 void ActionManager::processActionRecords() {    
     for (auto record : _records) {
-        if (record->isDone) {
+        if (record->_isDone) {
             continue;
         }
 
-        if (!record->isActive) {
-            for (uint i = 0; i < record->dependencies.size(); ++i) {
-                DependencyRecord &dep = record->dependencies[i];
+        if (!record->_isActive) {
+            for (uint i = 0; i < record->_dependencies.size(); ++i) {
+                DependencyRecord &dep = record->_dependencies[i];
 
                 if (!dep.satisfied) {
                     switch (dep.type) {
@@ -343,17 +343,17 @@ void ActionManager::processActionRecords() {
 
                         break;
                     case kResetOnNewDay:
-                        if (record->days == -1) {
-                            record->days = NancySceneState._timers.playerTime.getDays();
+                        if (record->_days == -1) {
+                            record->_days = NancySceneState._timers.playerTime.getDays();
                             dep.satisfied = true;
                             break;
                         }
 
-                        if (record->days < NancySceneState._timers.playerTime.getDays()) {
-                            record->days = NancySceneState._timers.playerTime.getDays();
-                            for (uint j = 0; j < record->dependencies.size(); ++j) {
-                                if (record->dependencies[j].type == kPlayerTime) {
-                                    record->dependencies[j].satisfied = false;
+                        if (record->_days < NancySceneState._timers.playerTime.getDays()) {
+                            record->_days = NancySceneState._timers.playerTime.getDays();
+                            for (uint j = 0; j < record->_dependencies.size(); ++j) {
+                                if (record->_dependencies[j].type == kPlayerTime) {
+                                    record->_dependencies[j].satisfied = false;
                                 }
                             }
                         }
@@ -361,8 +361,8 @@ void ActionManager::processActionRecords() {
                         break;
                     case kUseItem: {
                         bool hasUnsatisfiedDeps = false;
-                        for (uint j = 0; j < record->dependencies.size(); ++j) {
-                            if (j != i && record->dependencies[j].satisfied == false) {
+                        for (uint j = 0; j < record->_dependencies.size(); ++j) {
+                            if (j != i && record->_dependencies[j].satisfied == false) {
                                 hasUnsatisfiedDeps = true;
                             }
                         }
@@ -371,10 +371,10 @@ void ActionManager::processActionRecords() {
                             break;
                         }
 
-                        record->itemRequired = dep.label;
+                        record->_itemRequired = dep.label;
 
                         if (dep.condition == 1) {
-                            record->itemRequired += 100;
+                            record->_itemRequired += 100;
                         }
                         
                         dep.satisfied = true;
@@ -412,28 +412,28 @@ void ActionManager::processActionRecords() {
 
             // An orFlag marks that its corresponding dependency and the one after it
             // mutually satisfy each other; if one is satisfied, so is the other
-            for (uint i = 1; i < record->dependencies.size(); ++i) {
-                if (record->dependencies[i-1].orFlag) {
-                    if (record->dependencies[i-1].satisfied)
-                        record->dependencies[i].satisfied = true;
-                    if (record->dependencies[i].satisfied)
-                        record->dependencies[i-1].satisfied = true;
+            for (uint i = 1; i < record->_dependencies.size(); ++i) {
+                if (record->_dependencies[i-1].orFlag) {
+                    if (record->_dependencies[i-1].satisfied)
+                        record->_dependencies[i].satisfied = true;
+                    if (record->_dependencies[i].satisfied)
+                        record->_dependencies[i-1].satisfied = true;
                 }
             }
 
             // Check if all dependencies have been satisfied, and activate the record if they have
             uint satisfied = 0;
-            for (uint i = 0; i < record->dependencies.size(); ++i) {
-                if (record->dependencies[i].satisfied)
+            for (uint i = 0; i < record->_dependencies.size(); ++i) {
+                if (record->_dependencies[i].satisfied)
                     ++satisfied;
             }
 
-            if (satisfied == record->dependencies.size())
-                record->isActive = true;
+            if (satisfied == record->_dependencies.size())
+                record->_isActive = true;
         
         }
 
-        if (record->isActive) {
+        if (record->_isActive) {
             record->execute();
         }
     }
@@ -448,7 +448,7 @@ void ActionManager::clearActionRecords() {
 
 void ActionManager::onPause(bool pause) {
     for (auto &r : _records) {
-        if (r->isActive && !r->isDone) {
+        if (r->_isActive && !r->_isDone) {
             r->onPause(pause);
         }
     }
@@ -457,8 +457,8 @@ void ActionManager::onPause(bool pause) {
 void ActionManager::synchronize(Common::Serializer &ser) {
     // When loading, the records should already have been initialized by scene
     for (auto &rec : _records) {
-        ser.syncAsByte(rec->isActive);
-        ser.syncAsByte(rec->isDone);
+        ser.syncAsByte(rec->_isActive);
+        ser.syncAsByte(rec->_isDone);
     }
 }
 
diff --git a/engines/nancy/action/actionrecord.h b/engines/nancy/action/actionrecord.h
index ad49259c99..f7e801a19e 100644
--- a/engines/nancy/action/actionrecord.h
+++ b/engines/nancy/action/actionrecord.h
@@ -83,14 +83,14 @@ public:
     enum ExecutionState { kBegin, kRun, kActionTrigger };
     enum ExecutionType { kOneShot = 1, kRepeating = 2 };
     ActionRecord() :
-        type(0),
-        execType(kOneShot),
-        isActive(false),
-        isDone(false),
-        hasHotspot(false),
-        state(ExecutionState::kBegin),
-        days(-1),
-        itemRequired(-1) {}
+        _type(0),
+        _execType(kOneShot),
+        _isActive(false),
+        _isDone(false),
+        _hasHotspot(false),
+        _state(ExecutionState::kBegin),
+        _days(-1),
+        _itemRequired(-1) {}
     virtual ~ActionRecord() {}
 
     virtual void readData(Common::SeekableReadStream &stream) =0;
@@ -102,23 +102,23 @@ public:
 
 protected:   
     void finishExecution() {
-        switch (execType) {
+        switch (_execType) {
         case kOneShot:
-            isDone = true;
-            state = kBegin;
+            _isDone = true;
+            _state = kBegin;
             break;
         case kRepeating:
-            isDone = false;
-            isActive = false;
-            state = kBegin;
+            _isDone = false;
+            _isActive = false;
+            _state = kBegin;
 
-            for (uint i = 0; i < dependencies.size(); ++i) {
-                dependencies[i].satisfied = false;
+            for (uint i = 0; i < _dependencies.size(); ++i) {
+                _dependencies[i].satisfied = false;
             }
 
             break;
         default:
-            state = kBegin;
+            _state = kBegin;
             break;
         }
     }
@@ -127,22 +127,22 @@ protected:
     virtual Common::String getRecordTypeName() const =0;
 
 public:
-    Common::String description;                     // 0x00
-    byte type;                                      // 0x30
-    ExecutionType execType;                         // 0x31
+    Common::String _description;                    // 0x00
+    byte _type;                                     // 0x30
+    ExecutionType _execType;                        // 0x31
     // 0x32 data
-    Common::Array<DependencyRecord> dependencies;   // 0x36
+    Common::Array<DependencyRecord> _dependencies;  // 0x36
     // 0x3A numDependencies
-    bool isActive;                                  // 0x3B
+    bool _isActive;                                 // 0x3B
     // 0x3C satisfiedDependencies[] 
     // 0x48 timers[]
     // 0x78 orFlags[]
-    bool isDone;                                    // 0x84
-    bool hasHotspot;                                // 0x85
-    Common::Rect hotspot;                           // 0x89
-    ExecutionState state;                           // 0x91
-    int16 days;                                     // 0x95
-    int8 itemRequired;                              // 0x97
+    bool _isDone;                                   // 0x84
+    bool _hasHotspot;                               // 0x85
+    Common::Rect _hotspot;                          // 0x89
+    ExecutionState _state;                          // 0x91
+    int16 _days;                                    // 0x95
+    int8 _itemRequired;                             // 0x97
 };
 
 } // End of namespace Action
diff --git a/engines/nancy/action/leverpuzzle.cpp b/engines/nancy/action/leverpuzzle.cpp
index 7cf69f7659..d6799e772c 100644
--- a/engines/nancy/action/leverpuzzle.cpp
+++ b/engines/nancy/action/leverpuzzle.cpp
@@ -38,97 +38,97 @@ void LeverPuzzle::init() {
     
     setTransparent(true);
 
-    g_nancy->resource->loadImage(imageName, image);
+    g_nancy->_resource->loadImage(_imageName, _image);
 }
 
 void LeverPuzzle::readData(Common::SeekableReadStream &stream) {
     char buf[10];
     stream.read(buf, 10);
-    imageName = buf;
+    _imageName = buf;
 
     for (uint leverID = 0; leverID < 3; ++leverID) {
-        srcRects.push_back(Common::Array<Common::Rect>());
+        _srcRects.push_back(Common::Array<Common::Rect>());
         for (uint i = 0; i < 4; ++i) {
-            srcRects.back().push_back(Common::Rect());
-            readRect(stream, srcRects.back().back());
+            _srcRects.back().push_back(Common::Rect());
+            readRect(stream, _srcRects.back().back());
         }
     }
 
     for (uint leverID = 0; leverID < 3; ++leverID) {
-        destRects.push_back(Common::Rect());
-        readRect(stream, destRects.back());
+        _destRects.push_back(Common::Rect());
+        readRect(stream, _destRects.back());
 
         if (leverID == 0) {
-            _screenPosition = destRects.back();
+            _screenPosition = _destRects.back();
         } else {
-            _screenPosition.extend(destRects.back());
+            _screenPosition.extend(_destRects.back());
         }
     }
 
     for (uint leverID = 0; leverID < 3; ++leverID) {
-        playerSequence.push_back(stream.readByte());
-        leverDirection.push_back(true);
+        _playerSequence.push_back(stream.readByte());
+        _leverDirection.push_back(true);
     }
 
     for (uint leverID = 0; leverID < 3; ++leverID) {
-        correctSequence.push_back(stream.readByte());
+        _correctSequence.push_back(stream.readByte());
     }
 
-    moveSound.read(stream, SoundDescription::kNormal);
-    noMoveSound.read(stream, SoundDescription::kNormal);
-    solveExitScene.readData(stream);
+    _moveSound.read(stream, SoundDescription::kNormal);
+    _noMoveSound.read(stream, SoundDescription::kNormal);
+    _solveExitScene.readData(stream);
     stream.skip(2);
-    flagOnSolve.label = stream.readSint16LE();
-    flagOnSolve.flag = (NancyFlag)stream.readByte();
-    solveSoundDelay = stream.readUint16LE();
-    solveSound.read(stream, SoundDescription::kNormal);
-    exitScene.readData(stream);
+    _flagOnSolve.label = stream.readSint16LE();
+    _flagOnSolve.flag = (NancyFlag)stream.readByte();
+    _solveSoundDelay = stream.readUint16LE();
+    _solveSound.read(stream, SoundDescription::kNormal);
+    _exitScene.readData(stream);
     stream.skip(2);
-    flagOnExit.label = stream.readSint16LE();
-    flagOnExit.flag = (NancyFlag)stream.readByte();
-    readRect(stream, exitHotspot);
+    _flagOnExit.label = stream.readSint16LE();
+    _flagOnExit.flag = (NancyFlag)stream.readByte();
+    readRect(stream, _exitHotspot);
 }
 
 void LeverPuzzle::execute() {
-    switch (state) {
+    switch (_state) {
     case kBegin:
         init();
         registerGraphics();
-        g_nancy->sound->loadSound(moveSound);
-        g_nancy->sound->loadSound(noMoveSound);
+        g_nancy->_sound->loadSound(_moveSound);
+        g_nancy->_sound->loadSound(_noMoveSound);
 
         for (uint i = 0; i < 3; ++i) {
             drawLever(i);
         }
 
-        state = kRun;
+        _state = kRun;
         // fall through
     case kRun:
-        switch (solveState) {
+        switch (_solveState) {
         case kNotSolved:
             for (uint i = 0; i < 3; ++i) {
-                if (playerSequence[i] != correctSequence[i]) {
+                if (_playerSequence[i] != _correctSequence[i]) {
                     return;
                 }
             }
             
-            NancySceneState.setEventFlag(flagOnSolve);
-            solveSoundPlayTime = g_nancy->getTotalPlayTime() + solveSoundDelay * 1000;
-            solveState = kPlaySound;
+            NancySceneState.setEventFlag(_flagOnSolve);
+            _solveSoundPlayTime = g_nancy->getTotalPlayTime() + _solveSoundDelay * 1000;
+            _solveState = kPlaySound;
             break;
         case kPlaySound:
-            if (g_nancy->getTotalPlayTime() <= solveSoundPlayTime) {
+            if (g_nancy->getTotalPlayTime() <= _solveSoundPlayTime) {
                 break;
             }
 
-            g_nancy->sound->loadSound(solveSound);
-            g_nancy->sound->playSound(solveSound);
-            solveState = kWaitForSound;
+            g_nancy->_sound->loadSound(_solveSound);
+            g_nancy->_sound->playSound(_solveSound);
+            _solveState = kWaitForSound;
             break;
         case kWaitForSound:
-            if (!g_nancy->sound->isSoundPlaying(solveSound)) {
-                g_nancy->sound->stopSound(solveSound);
-                state = kActionTrigger;
+            if (!g_nancy->_sound->isSoundPlaying(_solveSound)) {
+                g_nancy->_sound->stopSound(_solveSound);
+                _state = kActionTrigger;
             }
 
             break;
@@ -136,14 +136,14 @@ void LeverPuzzle::execute() {
 
         break;
     case kActionTrigger:
-        g_nancy->sound->stopSound(moveSound);
-        g_nancy->sound->stopSound(noMoveSound);
+        g_nancy->_sound->stopSound(_moveSound);
+        g_nancy->_sound->stopSound(_noMoveSound);
         
-        if (solveState == kNotSolved) {
-            NancySceneState.changeScene(exitScene);
-            NancySceneState.setEventFlag(flagOnExit);
+        if (_solveState == kNotSolved) {
+            NancySceneState.changeScene(_exitScene);
+            NancySceneState.setEventFlag(_flagOnExit);
         } else {
-            NancySceneState.changeScene(solveExitScene);
+            NancySceneState.changeScene(_solveExitScene);
         }
 
         finishExecution();
@@ -151,22 +151,22 @@ void LeverPuzzle::execute() {
 }
 
 void LeverPuzzle::handleInput(NancyInput &input) {
-    if (solveState != kNotSolved) {
+    if (_solveState != kNotSolved) {
         return;
     }
 
-    if (NancySceneState.getViewport().convertViewportToScreen(exitHotspot).contains(input.mousePos)) {
-        g_nancy->cursorManager->setCursorType(CursorManager::kExitArrow);
+    if (NancySceneState.getViewport().convertViewportToScreen(_exitHotspot).contains(input.mousePos)) {
+        g_nancy->_cursorManager->setCursorType(CursorManager::kExitArrow);
 
         if (input.input & NancyInput::kLeftMouseButtonUp) {
-            state = kActionTrigger;
+            _state = kActionTrigger;
         }
         return;
     }
 
     for (uint i = 0; i < 3; ++i) {
-        if (NancySceneState.getViewport().convertViewportToScreen(destRects[i]).contains(input.mousePos)) {
-            g_nancy->cursorManager->setCursorType(CursorManager::kHotspot);
+        if (NancySceneState.getViewport().convertViewportToScreen(_destRects[i]).contains(input.mousePos)) {
+            g_nancy->_cursorManager->setCursorType(CursorManager::kHotspot);
             
             if (input.input & NancyInput::kLeftMouseButtonUp) {
                 bool isMoving = false;
@@ -176,13 +176,13 @@ void LeverPuzzle::handleInput(NancyInput &input) {
                     isMoving = true;
                     break;
                 case 1:
-                    if (playerSequence[0] == 1) {
+                    if (_playerSequence[0] == 1) {
                         isMoving = true;
                     }
 
                     break;
                 case 2:
-                    if (playerSequence[0] == 2) {
+                    if (_playerSequence[0] == 2) {
                         isMoving = true;
                     }
                     
@@ -190,29 +190,29 @@ void LeverPuzzle::handleInput(NancyInput &input) {
                 }
 
                 if (isMoving) {
-                    g_nancy->sound->playSound(moveSound);
+                    g_nancy->_sound->playSound(_moveSound);
 
-                    if (leverDirection[i]) {
+                    if (_leverDirection[i]) {
                         // Moving down
-                        if (playerSequence[i] == 3) {
-                            --playerSequence[i];
-                            leverDirection[i] = false;
+                        if (_playerSequence[i] == 3) {
+                            --_playerSequence[i];
+                            _leverDirection[i] = false;
                         } else {
-                            ++playerSequence[i];
+                            ++_playerSequence[i];
                         }
                     } else {
                         // Moving up
-                        if (playerSequence[i] == 0) {
-                            ++playerSequence[i];
-                            leverDirection[i] = true;
+                        if (_playerSequence[i] == 0) {
+                            ++_playerSequence[i];
+                            _leverDirection[i] = true;
                         } else {
-                            --playerSequence[i];
+                            --_playerSequence[i];
                         }
                     }
 
                     drawLever(i);
                 } else {
-                    g_nancy->sound->playSound(noMoveSound);
+                    g_nancy->_sound->playSound(_noMoveSound);
                     return;
                 }
             }
@@ -227,8 +227,8 @@ void LeverPuzzle::onPause(bool pause) {
 }
 
 void LeverPuzzle::drawLever(uint id) {
-    Common::Point destPoint(destRects[id].left - _screenPosition.left, destRects[id].top - _screenPosition.top);
-    _drawSurface.blitFrom(image, srcRects[id][playerSequence[id]], destPoint);
+    Common::Point destPoint(_destRects[id].left - _screenPosition.left, _destRects[id].top - _screenPosition.top);
+    _drawSurface.blitFrom(_image, _srcRects[id][_playerSequence[id]], destPoint);
     
     _needsRedraw = true;
 }
diff --git a/engines/nancy/action/leverpuzzle.h b/engines/nancy/action/leverpuzzle.h
index 5ee94684b1..78e08c0a62 100644
--- a/engines/nancy/action/leverpuzzle.h
+++ b/engines/nancy/action/leverpuzzle.h
@@ -49,25 +49,25 @@ public:
     virtual void handleInput(NancyInput &input) override;
     virtual void onPause(bool pause) override;
 
-    Common::String imageName; // 0x0
-    Common::Array<Common::Array<Common::Rect>> srcRects; // 0xA, 0xC0 bytes
-    Common::Array<Common::Rect> destRects; // 0xCA, 0x30 bytes
-    Common::Array<byte> correctSequence; // 0xFA, 3 bytes
-    SoundDescription moveSound; // 0x100
-    SoundDescription noMoveSound; // 0x122
-    SceneChangeDescription solveExitScene; // 0x144
-    EventFlagDescription flagOnSolve; // 0x14E
-    uint16 solveSoundDelay; // 0x151
-    SoundDescription solveSound; // 0x153
-    SceneChangeDescription exitScene; // 0x175
-    EventFlagDescription flagOnExit; // 0x17F
-    Common::Rect exitHotspot; // 0x182
-
-    Common::Array<byte> playerSequence;
-    Common::Array<bool> leverDirection;
-    Graphics::ManagedSurface image;
-    Time solveSoundPlayTime;
-    SolveState solveState = kNotSolved;
+    Common::String _imageName; // 0x0
+    Common::Array<Common::Array<Common::Rect>> _srcRects; // 0xA, 0xC0 bytes
+    Common::Array<Common::Rect> _destRects; // 0xCA, 0x30 bytes
+    Common::Array<byte> _correctSequence; // 0xFA, 3 bytes
+    SoundDescription _moveSound; // 0x100
+    SoundDescription _noMoveSound; // 0x122
+    SceneChangeDescription _solveExitScene; // 0x144
+    EventFlagDescription _flagOnSolve; // 0x14E
+    uint16 _solveSoundDelay; // 0x151
+    SoundDescription _solveSound; // 0x153
+    SceneChangeDescription _exitScene; // 0x175
+    EventFlagDescription _flagOnExit; // 0x17F
+    Common::Rect _exitHotspot; // 0x182
+
+    Common::Array<byte> _playerSequence;
+    Common::Array<bool> _leverDirection;
+    Graphics::ManagedSurface _image;
+    Time _solveSoundPlayTime;
+    SolveState _solveState = kNotSolved;
 
 protected:
     virtual Common::String getRecordTypeName() const override { return "LeverPuzzle"; }
diff --git a/engines/nancy/action/orderingpuzzle.cpp b/engines/nancy/action/orderingpuzzle.cpp
index 1d6ac71c23..4957bc85ea 100644
--- a/engines/nancy/action/orderingpuzzle.cpp
+++ b/engines/nancy/action/orderingpuzzle.cpp
@@ -45,7 +45,7 @@ void OrderingPuzzle::init() {
     
     setTransparent(true);
 
-    g_nancy->resource->loadImage(imageName, image);
+    g_nancy->_resource->loadImage(_imageName, _image);
 
     setVisible(false);
 
@@ -56,102 +56,102 @@ void OrderingPuzzle::readData(Common::SeekableReadStream &stream) {
     char buf[10];
 
     stream.read(buf, 10);
-    imageName = buf;
+    _imageName = buf;
     uint16 numElements = stream.readUint16LE();
 
     for (uint i = 0; i < numElements; ++i) {
-        srcRects.push_back(Common::Rect());
-        readRect(stream, srcRects.back());
+        _srcRects.push_back(Common::Rect());
+        readRect(stream, _srcRects.back());
     }
     
     stream.skip(16 * (15 - numElements));
 
     for (uint i = 0; i < numElements; ++i) {
-        destRects.push_back(Common::Rect());
-        readRect(stream, destRects.back());
+        _destRects.push_back(Common::Rect());
+        readRect(stream, _destRects.back());
 
         if (i == 0) {
-            _screenPosition = destRects[i];
+            _screenPosition = _destRects[i];
         } else {
-            _screenPosition.extend(destRects[i]);
+            _screenPosition.extend(_destRects[i]);
         }
 
-        drawnElements.push_back(false);
+        _drawnElements.push_back(false);
     }
 
     stream.skip(16 * (15 - numElements));
 
-    sequenceLength = stream.readUint16LE();
+    _sequenceLength = stream.readUint16LE();
 
     for (uint i = 0; i < 15; ++i) {
-        correctSequence.push_back(stream.readByte());
+        _correctSequence.push_back(stream.readByte());
     }
 
-    clickSound.read(stream, SoundDescription::kNormal);
-    solveExitScene.readData(stream);
+    _clickSound.read(stream, SoundDescription::kNormal);
+    _solveExitScene.readData(stream);
     stream.skip(2); // shouldStopRendering, useless
-    flagOnSolve.label = stream.readSint16LE();
-    flagOnSolve.flag = (NancyFlag)stream.readByte();
-    solveSoundDelay = stream.readUint16LE();
-    solveSound.read(stream, SoundDescription::kNormal);
-    exitScene.readData(stream);
+    _flagOnSolve.label = stream.readSint16LE();
+    _flagOnSolve.flag = (NancyFlag)stream.readByte();
+    _solveSoundDelay = stream.readUint16LE();
+    _solveSound.read(stream, SoundDescription::kNormal);
+    _exitScene.readData(stream);
     stream.skip(2); // shouldStopRendering, useless
-    flagOnExit.label = stream.readSint16LE();
-    flagOnExit.flag = (NancyFlag)stream.readByte();
-    readRect(stream, exitHotspot);
+    _flagOnExit.label = stream.readSint16LE();
+    _flagOnExit.flag = (NancyFlag)stream.readByte();
+    readRect(stream, _exitHotspot);
 }
 
 void OrderingPuzzle::execute() {
-    switch (state) {
+    switch (_state) {
     case kBegin:
         init();
         registerGraphics();
-        g_nancy->sound->loadSound(clickSound);
-        g_nancy->sound->loadSound(solveSound);
-        state = kRun;
+        g_nancy->_sound->loadSound(_clickSound);
+        g_nancy->_sound->loadSound(_solveSound);
+        _state = kRun;
         // fall through
     case kRun:
-        switch (solveState) {
+        switch (_solveState) {
         case kNotSolved:
-            if (clickedSequence.size() != sequenceLength) {
+            if (_clickedSequence.size() != _sequenceLength) {
                 return;
             }
 
-            for (uint i = 0; i < sequenceLength; ++i) {
-                if (clickedSequence[i] != (int16)correctSequence[i]) {
+            for (uint i = 0; i < _sequenceLength; ++i) {
+                if (_clickedSequence[i] != (int16)_correctSequence[i]) {
                     return;
                 }
             }
 
-            NancySceneState.setEventFlag(flagOnSolve);
-            solveSoundPlayTime = g_nancy->getTotalPlayTime() + solveSoundDelay * 1000;
-            solveState = kPlaySound;
+            NancySceneState.setEventFlag(_flagOnSolve);
+            _solveSoundPlayTime = g_nancy->getTotalPlayTime() + _solveSoundDelay * 1000;
+            _solveState = kPlaySound;
             // fall through
         case kPlaySound:
-            if (g_nancy->getTotalPlayTime() <= solveSoundPlayTime) {
+            if (g_nancy->getTotalPlayTime() <= _solveSoundPlayTime) {
                 break;
             }
 
-            g_nancy->sound->playSound(solveSound);
-            solveState = kWaitForSound;
+            g_nancy->_sound->playSound(_solveSound);
+            _solveState = kWaitForSound;
             break;
         case kWaitForSound:
-            if (!g_nancy->sound->isSoundPlaying(solveSound)) {
-                state = kActionTrigger;
+            if (!g_nancy->_sound->isSoundPlaying(_solveSound)) {
+                _state = kActionTrigger;
             }
 
             break;
         }
         break;
     case kActionTrigger:
-        g_nancy->sound->stopSound(clickSound);
-        g_nancy->sound->stopSound(solveSound);
+        g_nancy->_sound->stopSound(_clickSound);
+        g_nancy->_sound->stopSound(_solveSound);
 
-        if (solveState == kNotSolved) {
-            NancySceneState.changeScene(exitScene);
-            NancySceneState.setEventFlag(flagOnExit);
+        if (_solveState == kNotSolved) {
+            NancySceneState.changeScene(_exitScene);
+            NancySceneState.setEventFlag(_flagOnExit);
         } else {
-            NancySceneState.changeScene(solveExitScene);
+            NancySceneState.changeScene(_solveExitScene);
         }
 
         finishExecution();
@@ -160,40 +160,40 @@ void OrderingPuzzle::execute() {
 }
 
 void OrderingPuzzle::handleInput(NancyInput &input) {
-    if (solveState != kNotSolved) {
+    if (_solveState != kNotSolved) {
         return;
     }
 
-    if (NancySceneState.getViewport().convertViewportToScreen(exitHotspot).contains(input.mousePos)) {
-        g_nancy->cursorManager->setCursorType(CursorManager::kExitArrow);
+    if (NancySceneState.getViewport().convertViewportToScreen(_exitHotspot).contains(input.mousePos)) {
+        g_nancy->_cursorManager->setCursorType(CursorManager::kExitArrow);
 
         if (input.input & NancyInput::kLeftMouseButtonUp) {
-            state = kActionTrigger;
+            _state = kActionTrigger;
         }
         return;
     }
 
-    for (int i = 0; i < (int)destRects.size(); ++i) {
-        if (NancySceneState.getViewport().convertViewportToScreen(destRects[i]).contains(input.mousePos)) {
-            g_nancy->cursorManager->setCursorType(CursorManager::kHotspot);
+    for (int i = 0; i < (int)_destRects.size(); ++i) {
+        if (NancySceneState.getViewport().convertViewportToScreen(_destRects[i]).contains(input.mousePos)) {
+            g_nancy->_cursorManager->setCursorType(CursorManager::kHotspot);
 
             if (input.input & NancyInput::kLeftMouseButtonUp) {
-                g_nancy->sound->playSound(clickSound);
+                g_nancy->_sound->playSound(_clickSound);
                 
-                for (uint j = 0; j < clickedSequence.size(); ++j) {
-                    if (clickedSequence[j] == i && drawnElements[i] == true) {
+                for (uint j = 0; j < _clickedSequence.size(); ++j) {
+                    if (_clickedSequence[j] == i && _drawnElements[i] == true) {
                         undrawElement(i);
-                        if (clickedSequence.back() == i) {
-                            clickedSequence.pop_back();
+                        if (_clickedSequence.back() == i) {
+                            _clickedSequence.pop_back();
                         }
                         
                         return;
                     }
                 }
 
-                clickedSequence.push_back(i);
+                _clickedSequence.push_back(i);
 
-                if (clickedSequence.size() > (uint)sequenceLength + 1) {
+                if (_clickedSequence.size() > (uint)_sequenceLength + 1) {
                     clearAllElements();
                 } else {
                     drawElement(i);
@@ -211,15 +211,15 @@ void OrderingPuzzle::onPause(bool pause) {
 }
 
 void OrderingPuzzle::drawElement(uint id) {
-    drawnElements[id] = true;
-    Common::Point destPoint(destRects[id].left - _screenPosition.left, destRects[id].top - _screenPosition.top);
-    _drawSurface.blitFrom(image, srcRects[id], destPoint);
+    _drawnElements[id] = true;
+    Common::Point destPoint(_destRects[id].left - _screenPosition.left, _destRects[id].top - _screenPosition.top);
+    _drawSurface.blitFrom(_image, _srcRects[id], destPoint);
     setVisible(true);
 }
 
 void OrderingPuzzle::undrawElement(uint id) {
-    drawnElements[id] = false;
-    Common::Rect bounds = destRects[id];
+    _drawnElements[id] = false;
+    Common::Rect bounds = _destRects[id];
     bounds.translate(-_screenPosition.left, -_screenPosition.top);
 
     _drawSurface.fillRect(bounds, GraphicsManager::getTransColor());
@@ -229,7 +229,7 @@ void OrderingPuzzle::undrawElement(uint id) {
 void OrderingPuzzle::clearAllElements() {
     _drawSurface.clear(GraphicsManager::getTransColor());
     setVisible(false);
-    clickedSequence.clear();
+    _clickedSequence.clear();
     return;
 }
 
diff --git a/engines/nancy/action/orderingpuzzle.h b/engines/nancy/action/orderingpuzzle.h
index 98d20cbf30..c676d6b696 100644
--- a/engines/nancy/action/orderingpuzzle.h
+++ b/engines/nancy/action/orderingpuzzle.h
@@ -50,25 +50,25 @@ public:
     virtual void handleInput(NancyInput &input) override;
     virtual void onPause(bool pause) override;
 
-    Common::String imageName; // 0x00
-    Common::Array<Common::Rect> srcRects; // 0xC, 15
-    Common::Array<Common::Rect> destRects; // 0xFC, 15
-    uint16 sequenceLength; // 0x1EC;
-    Common::Array<byte> correctSequence; // 0x1EE, 15 bytes
-    Nancy::SoundDescription clickSound; // 0x1FD, kNormal
-    SceneChangeDescription solveExitScene; // 0x21F
-    EventFlagDescription flagOnSolve; // 0x229
-    uint16 solveSoundDelay; // 0x22C 
-    Nancy::SoundDescription solveSound; // 0x22E
-    SceneChangeDescription exitScene; // 0x250
-    EventFlagDescription flagOnExit; // 0x25A
-    Common::Rect exitHotspot; // 0x25D
-
-    SolveState solveState = kNotSolved;
-    Graphics::ManagedSurface image;
-    Common::Array<int16> clickedSequence;
-    Common::Array<bool> drawnElements;
-    Time solveSoundPlayTime;
+    Common::String _imageName; // 0x00
+    Common::Array<Common::Rect> _srcRects; // 0xC, 15
+    Common::Array<Common::Rect> _destRects; // 0xFC, 15
+    uint16 _sequenceLength; // 0x1EC;
+    Common::Array<byte> _correctSequence; // 0x1EE, 15 bytes
+    Nancy::SoundDescription _clickSound; // 0x1FD, kNormal
+    SceneChangeDescription _solveExitScene; // 0x21F
+    EventFlagDescription _flagOnSolve; // 0x229
+    uint16 _solveSoundDelay; // 0x22C 
+    Nancy::SoundDescription _solveSound; // 0x22E
+    SceneChangeDescription _exitScene; // 0x250
+    EventFlagDescription _flagOnExit; // 0x25A
+    Common::Rect _exitHotspot; // 0x25D
+
+    SolveState _solveState = kNotSolved;
+    Graphics::ManagedSurface _image;
+    Common::Array<int16> _clickedSequence;
+    Common::Array<bool> _drawnElements;
+    Time _solveSoundPlayTime;
 
 protected:
     virtual Common::String getRecordTypeName() const override { return "OrderingPuzzle"; }
diff --git a/engines/nancy/action/passwordpuzzle.cpp b/engines/nancy/action/passwordpuzzle.cpp
index 5043810ef2..984cff6012 100644
--- a/engines/nancy/action/passwordpuzzle.cpp
+++ b/engines/nancy/action/passwordpuzzle.cpp
@@ -44,51 +44,51 @@ void PasswordPuzzle::init() {
 }
 
 void PasswordPuzzle::readData(Common::SeekableReadStream &stream) {
-    fontID = stream.readUint16LE();
-    cursorBlinkTime = stream.readUint16LE();
-    readRect(stream, nameBounds);
-    readRect(stream, passwordBounds);
+    _fontID = stream.readUint16LE();
+    _cursorBlinkTime = stream.readUint16LE();
+    readRect(stream, _nameBounds);
+    readRect(stream, _passwordBounds);
     readRect(stream, _screenPosition);
 
     char buf[20];
     stream.read(buf, 20);
-    name = buf;
+    _name = buf;
     stream.read(buf, 20);
-    password = buf;
-    solveExitScene.readData(stream);
+    _password = buf;
+    _solveExitScene.readData(stream);
     stream.skip(2);
-    flagOnSolve.label = stream.readSint16LE();
-    flagOnSolve.flag = (NancyFlag)stream.readByte();
-    solveSound.read(stream, SoundDescription::kNormal);
-    failExitScene.readData(stream);
+    _flagOnSolve.label = stream.readSint16LE();
+    _flagOnSolve.flag = (NancyFlag)stream.readByte();
+    _solveSound.read(stream, SoundDescription::kNormal);
+    _failExitScene.readData(stream);
     stream.skip(2);
-    flagOnFail.label = stream.readSint16LE();
-    flagOnFail.flag = (NancyFlag)stream.readByte();
-    failSound.read(stream, SoundDescription::kNormal);
-    exitScene.readData(stream);
+    _flagOnFail.label = stream.readSint16LE();
+    _flagOnFail.flag = (NancyFlag)stream.readByte();
+    _failSound.read(stream, SoundDescription::kNormal);
+    _exitScene.readData(stream);
     stream.skip(2);
-    flagOnExit.label = stream.readSint16LE();
-    flagOnExit.flag = (NancyFlag)stream.readByte();
-    readRect(stream, exitHotspot);
+    _flagOnExit.label = stream.readSint16LE();
+    _flagOnExit.flag = (NancyFlag)stream.readByte();
+    readRect(stream, _exitHotspot);
 }
 
 void PasswordPuzzle::execute() {
-    switch (state) {
+    switch (_state) {
     case kBegin:
         init();
         registerGraphics();
-        nextBlinkTime = g_nancy->getTotalPlayTime() + cursorBlinkTime;
-        state = kRun;
+        _nextBlinkTime = g_nancy->getTotalPlayTime() + _cursorBlinkTime;
+        _state = kRun;
         // fall through
     case kRun:
-        switch (solveState) {
+        switch (_solveState) {
         case kNotSolved: {
-            Common::String &activeField = passwordFieldIsActive ? playerPasswordInput : playerNameInput;
-            Common::String &correctField = passwordFieldIsActive ? password : name;
+            Common::String &activeField = _passwordFieldIsActive ? _playerPasswordInput : _playerNameInput;
+            Common::String &correctField = _passwordFieldIsActive ? _password : _name;
             Time currentTime = g_nancy->getTotalPlayTime();
 
-            if (playerHasHitReturn) {
-                playerHasHitReturn = false;
+            if (_playerHasHitReturn) {
+                _playerHasHitReturn = false;
 
                 if (activeField.lastChar() == '-') {
                     activeField.deleteLastChar();
@@ -96,22 +96,22 @@ void PasswordPuzzle::execute() {
                 }
 
                 if (activeField.equalsIgnoreCase(correctField)) {
-                    if (!passwordFieldIsActive) {
-                        passwordFieldIsActive = true;
+                    if (!_passwordFieldIsActive) {
+                        _passwordFieldIsActive = true;
                     } else {
-                        g_nancy->sound->loadSound(solveSound);
-                        g_nancy->sound->playSound(solveSound);
-                        solveState = kSolved;
+                        g_nancy->_sound->loadSound(_solveSound);
+                        g_nancy->_sound->playSound(_solveSound);
+                        _solveState = kSolved;
                     }
                 } else {
-                    g_nancy->sound->loadSound(failSound);
-                    g_nancy->sound->playSound(failSound);
-                    solveState = kFailed;
+                    g_nancy->_sound->loadSound(_failSound);
+                    g_nancy->_sound->playSound(_failSound);
+                    _solveState = kFailed;
                 }
                 
                 
-            } else if (currentTime >= nextBlinkTime) {
-                nextBlinkTime = currentTime + cursorBlinkTime;
+            } else if (currentTime >= _nextBlinkTime) {
+                _nextBlinkTime = currentTime + _cursorBlinkTime;
 
                 if (activeField.size() && activeField.lastChar() == '-') {
                     activeField.deleteLastChar();
@@ -125,16 +125,16 @@ void PasswordPuzzle::execute() {
             break;
         }
         case kFailed:
-            if (!g_nancy->sound->isSoundPlaying(failSound)) {
-                g_nancy->sound->stopSound(failSound);
-                state = kActionTrigger;
+            if (!g_nancy->_sound->isSoundPlaying(_failSound)) {
+                g_nancy->_sound->stopSound(_failSound);
+                _state = kActionTrigger;
             }
 
             break;
         case kSolved:
-            if (!g_nancy->sound->isSoundPlaying(solveSound)) {
-                g_nancy->sound->stopSound(solveSound);
-                state = kActionTrigger;
+            if (!g_nancy->_sound->isSoundPlaying(_solveSound)) {
+                g_nancy->_sound->stopSound(_solveSound);
+                _state = kActionTrigger;
             }
 
             break;
@@ -142,18 +142,18 @@ void PasswordPuzzle::execute() {
 
         break;
     case kActionTrigger:
-        switch (solveState) {
+        switch (_solveState) {
         case kNotSolved:
-            NancySceneState.changeScene(exitScene);
-            NancySceneState.setEventFlag(flagOnExit);
+            NancySceneState.changeScene(_exitScene);
+            NancySceneState.setEventFlag(_flagOnExit);
             break;
         case kFailed:
-            NancySceneState.changeScene(failExitScene);
-            NancySceneState.setEventFlag(flagOnFail.label);
+            NancySceneState.changeScene(_failExitScene);
+            NancySceneState.setEventFlag(_flagOnFail.label);
             break;
         case kSolved:
-            NancySceneState.changeScene(solveExitScene);
-            NancySceneState.setEventFlag(flagOnSolve.label);
+            NancySceneState.changeScene(_solveExitScene);
+            NancySceneState.setEventFlag(_flagOnSolve.label);
             break;
         }
 
@@ -162,15 +162,15 @@ void PasswordPuzzle::execute() {
 }
 
 void PasswordPuzzle::handleInput(NancyInput &input) {
-    if (solveState != kNotSolved) {
+    if (_solveState != kNotSolved) {
         return;
     }
 
-    if (NancySceneState.getViewport().convertViewportToScreen(exitHotspot).contains(input.mousePos)) {
-        g_nancy->cursorManager->setCursorType(CursorManager::kExitArrow);
+    if (NancySceneState.getViewport().convertViewportToScreen(_exitHotspot).contains(input.mousePos)) {
+        g_nancy->_cursorManager->setCursorType(CursorManager::kExitArrow);
 
         if (input.input & NancyInput::kLeftMouseButtonUp) {
-            state = kActionTrigger;
+            _state = kActionTrigger;
         }
         
         return;
@@ -178,8 +178,8 @@ void PasswordPuzzle::handleInput(NancyInput &input) {
 
     for (uint i = 0; i < input.otherKbdInput.size(); ++i) {
         Common::KeyState &key = input.otherKbdInput[i];
-        Common::String &activeField = passwordFieldIsActive ? playerPasswordInput : playerNameInput;
-        Common::String &correctField = passwordFieldIsActive ? password : name;
+        Common::String &activeField = _passwordFieldIsActive ? _playerPasswordInput : _playerNameInput;
+        Common::String &correctField = _passwordFieldIsActive ? _password : _name;
         if (key.keycode == Common::KEYCODE_BACKSPACE) {
             if (activeField.size() && activeField.lastChar() == '-' ? activeField.size() > 1 : true) {
                 if (activeField.lastChar() == '-') {
@@ -191,7 +191,7 @@ void PasswordPuzzle::handleInput(NancyInput &input) {
                 drawText();
             }
         } else if (key.keycode == Common::KEYCODE_RETURN) {
-            playerHasHitReturn = true;
+            _playerHasHitReturn = true;
         } else if (Common::isAlnum(key.ascii) || Common::isSpace(key.ascii)) {
             if (activeField.size() && activeField.lastChar() == '-') {
                 if (activeField.size() <= correctField.size() + 2) {
@@ -218,20 +218,20 @@ void PasswordPuzzle::onPause(bool pause) {
 
 void PasswordPuzzle::drawText() {
     _drawSurface.clear(GraphicsManager::getTransColor());
-    Graphics::Font *font = g_nancy->graphicsManager->getFont(fontID);
+    Graphics::Font *font = g_nancy->_graphicsManager->getFont(_fontID);
 
-    Common::Rect bounds = nameBounds;
+    Common::Rect bounds = _nameBounds;
     bounds = NancySceneState.getViewport().convertViewportToScreen(bounds);
     bounds = convertToLocal(bounds);
     Common::Point destPoint(bounds.left, bounds.bottom + 1 - font->getFontHeight());
-    font->drawString(&_drawSurface, playerNameInput, destPoint.x, destPoint.y, bounds.width(), 0);
+    font->drawString(&_drawSurface, _playerNameInput, destPoint.x, destPoint.y, bounds.width(), 0);
 
-    bounds = passwordBounds;
+    bounds = _passwordBounds;
     bounds = NancySceneState.getViewport().convertViewportToScreen(bounds);
     bounds = convertToLocal(bounds);
     destPoint.x = bounds.left;
     destPoint.y = bounds.bottom + 1 - font->getFontHeight();
-    font->drawString(&_drawSurface, playerPasswordInput, destPoint.x, destPoint.y, bounds.width(), 0);
+    font->drawString(&_drawSurface, _playerPasswordInput, destPoint.x, destPoint.y, bounds.width(), 0);
 
     _needsRedraw = true;
 }
diff --git a/engines/nancy/action/passwordpuzzle.h b/engines/nancy/action/passwordpuzzle.h
index d135cb36a4..c727c153d2 100644
--- a/engines/nancy/action/passwordpuzzle.h
+++ b/engines/nancy/action/passwordpuzzle.h
@@ -40,9 +40,9 @@ public:
     enum SolveState { kNotSolved, kFailed, kSolved };
     PasswordPuzzle(RenderObject &redrawFrom) :
         RenderObject(redrawFrom),
-        passwordFieldIsActive(false),
-        playerHasHitReturn(false),
-        solveState(kNotSolved) {}
+        _passwordFieldIsActive(false),
+        _playerHasHitReturn(false),
+        _solveState(kNotSolved) {}
     virtual ~PasswordPuzzle() {}
 
     virtual void init() override;
@@ -52,29 +52,29 @@ public:
     virtual void handleInput(NancyInput &input) override;
     virtual void onPause(bool pause) override;
 
-    uint16 fontID; // 0x00
-    Time cursorBlinkTime; // 0x2
-    Common::Rect nameBounds; // 0x4
-    Common::Rect passwordBounds; // 0x14
+    uint16 _fontID; // 0x00
+    Time _cursorBlinkTime; // 0x2
+    Common::Rect _nameBounds; // 0x4
+    Common::Rect _passwordBounds; // 0x14
     // _screenPosition 0x24
-    Common::String name; // 0x34, 20 bytes long
-    Common::String password; // 0x48, 20 bytes long
-    SceneChangeDescription solveExitScene; // 0x5A
-    EventFlagDescription flagOnSolve; // 0x66
-    SoundDescription solveSound; // 0x69
-    SceneChangeDescription failExitScene; // 0x8B
-    EventFlagDescription flagOnFail; // 0x95
-    SoundDescription failSound; // 0x98
-    SceneChangeDescription exitScene; // 0xBA
-    EventFlagDescription flagOnExit; // 0xC4
-    Common::Rect exitHotspot; // 0xC7
+    Common::String _name; // 0x34, 20 bytes long
+    Common::String _password; // 0x48, 20 bytes long
+    SceneChangeDescription _solveExitScene; // 0x5A
+    EventFlagDescription _flagOnSolve; // 0x66
+    SoundDescription _solveSound; // 0x69
+    SceneChangeDescription _failExitScene; // 0x8B
+    EventFlagDescription _flagOnFail; // 0x95
+    SoundDescription _failSound; // 0x98
+    SceneChangeDescription _exitScene; // 0xBA
+    EventFlagDescription _flagOnExit; // 0xC4
+    Common::Rect _exitHotspot; // 0xC7
 
-    Common::String playerNameInput;
-    Common::String playerPasswordInput;
-    Time nextBlinkTime;
-    bool passwordFieldIsActive;
-    bool playerHasHitReturn;
-    SolveState solveState;
+    Common::String _playerNameInput;
+    Common::String _playerPasswordInput;
+    Time _nextBlinkTime;
+    bool _passwordFieldIsActive;
+    bool _playerHasHitReturn;
+    SolveState _solveState;
 
 protected:
     virtual Common::String getRecordTypeName() const override { return "PasswordPuzzle"; }
diff --git a/engines/nancy/action/primaryvideo.cpp b/engines/nancy/action/primaryvideo.cpp
index 9f0f28f129..24008a9c10 100644
--- a/engines/nancy/action/primaryvideo.cpp
+++ b/engines/nancy/action/primaryvideo.cpp
@@ -37,7 +37,7 @@
 namespace Nancy {
 namespace Action {
 
-PlayPrimaryVideoChan0 *PlayPrimaryVideoChan0::activePrimaryVideo = nullptr;
+PlayPrimaryVideoChan0 *PlayPrimaryVideoChan0::_activePrimaryVideo = nullptr;
 
 void PlayPrimaryVideoChan0::ConditionFlag::read(Common::SeekableReadStream &stream) {
     type = (ConditionType)stream.readByte();
@@ -109,8 +109,8 @@ bool PlayPrimaryVideoChan0::ConditionFlags::isSatisfied() const {
 PlayPrimaryVideoChan0::~PlayPrimaryVideoChan0() {
     _decoder.close();
 
-	if (activePrimaryVideo == this) {
-		activePrimaryVideo = nullptr;
+	if (_activePrimaryVideo == this) {
+		_activePrimaryVideo = nullptr;
 	}
     
     NancySceneState.setShouldClearTextbox(true);
@@ -118,8 +118,8 @@ PlayPrimaryVideoChan0::~PlayPrimaryVideoChan0() {
 }
 
 void PlayPrimaryVideoChan0::init() {
-    _decoder.loadFile(videoName + ".avf");
-    _drawSurface.create(src.width(), src.height(), _decoder.getPixelFormat());
+    _decoder.loadFile(_videoName + ".avf");
+    _drawSurface.create(_src.width(), _src.height(), _decoder.getPixelFormat());
 
     RenderObject::init();
     
@@ -136,7 +136,7 @@ void PlayPrimaryVideoChan0::updateGraphics() {
     }
 
     if (_decoder.needsUpdate()) {
-        _drawSurface.blitFrom(*_decoder.decodeNextFrame(), src, Common::Point());
+        _drawSurface.blitFrom(*_decoder.decodeNextFrame(), _src, Common::Point());
         _needsRedraw = true;
     }
 
@@ -156,34 +156,34 @@ void PlayPrimaryVideoChan0::readData(Common::SeekableReadStream &stream) {
 
     char name[10];
     stream.read(name, 10);
-    videoName = Common::String(name);
+    _videoName = Common::String(name);
 
     stream.skip(0x13);
 
-    readRect(stream, src);
+    readRect(stream, _src);
     readRect(stream, _screenPosition);
 
     char *rawText = new char[1500]();
     stream.read(rawText, 1500);
-    UI::Textbox::assembleTextLine(rawText, text, 1500);
+    UI::Textbox::assembleTextLine(rawText, _text, 1500);
     delete[] rawText;
 
-    sound.read(stream, SoundDescription::kNormal);
-    responseGenericSound.read(stream, SoundDescription::kNormal);
+    _sound.read(stream, SoundDescription::kNormal);
+    _responseGenericSound.read(stream, SoundDescription::kNormal);
     stream.skip(1);
-    conditionalResponseCharacterID = stream.readByte();
-    goodbyeResponseCharacterID = stream.readByte();
-    isDialogueExitScene = (NancyFlag)stream.readByte();
-    doNotPop = (NancyFlag)stream.readByte();
-    sceneChange.readData(stream);
+    _conditionalResponseCharacterID = stream.readByte();
+    _goodbyeResponseCharacterID = stream.readByte();
+    _isDialogueExitScene = (NancyFlag)stream.readByte();
+    _doNotPop = (NancyFlag)stream.readByte();
+    _sceneChange.readData(stream);
 
     stream.seek(beginOffset + 0x69C);
 
     uint16 numResponses = stream.readUint16LE();
     if (numResponses > 0) {
         for (uint i = 0; i < numResponses; ++i) {
-            responses.push_back(ResponseStruct());
-            ResponseStruct &response = responses[i];
+            _responses.push_back(ResponseStruct());
+            ResponseStruct &response = _responses[i];
             response.conditionFlags.read(stream);
             rawText = new char[400];
             stream.read(rawText, 400);
@@ -193,7 +193,7 @@ void PlayPrimaryVideoChan0::readData(Common::SeekableReadStream &stream) {
             stream.read(name, 10);
             response.soundName = name;
             stream.skip(1);
-            response.sceneChange.readData(stream);
+            response._sceneChange.readData(stream);
             response.flagDesc.label = stream.readSint16LE();
             response.flagDesc.flag = (NancyFlag)stream.readByte();
 
@@ -209,8 +209,8 @@ void PlayPrimaryVideoChan0::readData(Common::SeekableReadStream &stream) {
     uint16 numFlagsStructs = stream.readUint16LE();
     if (numFlagsStructs > 0) {
         for (uint16 i = 0; i < numFlagsStructs; ++i) {
-            flagsStructs.push_back(FlagsStruct());
-            FlagsStruct &flagsStruct = flagsStructs.back();
+            _flagsStructs.push_back(FlagsStruct());
+            FlagsStruct &flagsStruct = _flagsStructs.back();
             flagsStruct.conditions.read(stream);
             flagsStruct.flagToSet.type = (ConditionFlag::ConditionType)stream.readByte();
             flagsStruct.flagToSet.flag.label = stream.readSint16LE();
@@ -220,36 +220,36 @@ void PlayPrimaryVideoChan0::readData(Common::SeekableReadStream &stream) {
 }
 
 void PlayPrimaryVideoChan0::execute() {
-	if (activePrimaryVideo != this && activePrimaryVideo != nullptr) {
+	if (_activePrimaryVideo != this && _activePrimaryVideo != nullptr) {
         return;
     }
 
-    switch (state) {
+    switch (_state) {
     case kBegin:
         init();
         registerGraphics();
-        g_nancy->sound->loadSound(sound);
-        g_nancy->sound->playSound(sound);
-        state = kRun;
-        activePrimaryVideo = this;
+        g_nancy->_sound->loadSound(_sound);
+        g_nancy->_sound->playSound(_sound);
+        _state = kRun;
+        _activePrimaryVideo = this;
         // fall through
     case kRun:
-        if (!hasDrawnTextbox) {
-            hasDrawnTextbox = true;
+        if (!_hasDrawnTextbox) {
+            _hasDrawnTextbox = true;
             NancySceneState.getTextbox().clear();
-            NancySceneState.getTextbox().addTextLine(text);
+            NancySceneState.getTextbox().addTextLine(_text);
 
             // Add responses when conditions have been satisfied
-            if (conditionalResponseCharacterID != 10) {
+            if (_conditionalResponseCharacterID != 10) {
                 addConditionalResponses();
             }
 
-            if (goodbyeResponseCharacterID != 10) {
+            if (_goodbyeResponseCharacterID != 10) {
                 addGoodbye();
             }
 
-            for (uint i = 0; i < responses.size(); ++i) {
-                auto &res = responses[i];
+            for (uint i = 0; i < _responses.size(); ++i) {
+                auto &res = _responses[i];
 
                 if (res.conditionFlags.isSatisfied()) {
                     NancySceneState.getTextbox().addTextLine(res.text);
@@ -257,56 +257,56 @@ void PlayPrimaryVideoChan0::execute() {
             }
         }
 
-        if (!g_nancy->sound->isSoundPlaying(sound) && _decoder.endOfVideo()) {
-            g_nancy->sound->stopSound(sound);
+        if (!g_nancy->_sound->isSoundPlaying(_sound) && _decoder.endOfVideo()) {
+            g_nancy->_sound->stopSound(_sound);
             
-            if (responses.size() == 0) {
+            if (_responses.size() == 0) {
                 // NPC has finished talking with no responses available, auto-advance to next scene
-                state = kActionTrigger;
+                _state = kActionTrigger;
             } else {
                 // NPC has finished talking, we have responses
                 for (uint i = 0; i < 30; ++i) {
                     if (NancySceneState.getLogicCondition(i, kTrue)) {
-                        pickedResponse = i;
+                        _pickedResponse = i;
                         break;
                     }
                 }
 
-                if (pickedResponse != -1) {
-                    // Player has picked response, play sound file and change state
-                    responseGenericSound.name = responses[pickedResponse].soundName;
+                if (_pickedResponse != -1) {
+                    // Player has picked response, play sound file and change _state
+                    _responseGenericSound.name = _responses[_pickedResponse].soundName;
                     // TODO this is probably not correct
-                    g_nancy->sound->loadSound(responseGenericSound);
-                    g_nancy->sound->playSound(responseGenericSound);
-                    state = kActionTrigger;
+                    g_nancy->_sound->loadSound(_responseGenericSound);
+                    g_nancy->_sound->playSound(_responseGenericSound);
+                    _state = kActionTrigger;
                 }
             }
         }
         break;
     case kActionTrigger:
         // process flags structs
-        for (auto flags : flagsStructs) {
+        for (auto flags : _flagsStructs) {
             if (flags.conditions.isSatisfied()) {
                 flags.flagToSet.set();
             }
         }
         
-        if (pickedResponse != -1) {
+        if (_pickedResponse != -1) {
             // Set response's event flag, if any
-            NancySceneState.setEventFlag(responses[pickedResponse].flagDesc);
+            NancySceneState.setEventFlag(_responses[_pickedResponse].flagDesc);
         }
 
-        if (!g_nancy->sound->isSoundPlaying(responseGenericSound)) {
-            g_nancy->sound->stopSound(responseGenericSound);
+        if (!g_nancy->_sound->isSoundPlaying(_responseGenericSound)) {
+            g_nancy->_sound->stopSound(_responseGenericSound);
             
-            if (pickedResponse != -1) {
-                NancySceneState.changeScene(responses[pickedResponse].sceneChange);
+            if (_pickedResponse != -1) {
+                NancySceneState.changeScene(_responses[_pickedResponse]._sceneChange);
             } else {
                 // Evaluate scene branch structs here
 
-                if (isDialogueExitScene == kFalse) {
-                    NancySceneState.changeScene(sceneChange);
-                } else if (doNotPop == kFalse) {
+                if (_isDialogueExitScene == kFalse) {
+                    NancySceneState.changeScene(_sceneChange);
+                } else if (_doNotPop == kFalse) {
                     // Exit dialogue
                     NancySceneState.popScene();
                 }
@@ -321,7 +321,7 @@ void PlayPrimaryVideoChan0::execute() {
 
 void PlayPrimaryVideoChan0::addConditionalResponses() {
     for (auto &res : nancy1ConditionalResponses) {
-        if (res.characterID == conditionalResponseCharacterID) {
+        if (res.characterID == _conditionalResponseCharacterID) {
             bool isSatisfied = true;
             for (auto & cond : res.conditions) {
                 if (cond.label == -1) {
@@ -342,12 +342,12 @@ void PlayPrimaryVideoChan0::addConditionalResponses() {
                 file.seek(nancy1ResponseBaseFileOffset + res.fileOffset);
                 file.read(snd, 8);
 
-                responses.push_back(ResponseStruct());
-                ResponseStruct &newResponse = responses.back();
+                _responses.push_back(ResponseStruct());
+                ResponseStruct &newResponse = _responses.back();
                 newResponse.soundName = snd;
                 newResponse.text = file.readString();
-                newResponse.sceneChange.sceneID = res.sceneID;
-                newResponse.sceneChange.doNotStartSound = true;
+                newResponse._sceneChange.sceneID = res.sceneID;
+                newResponse._sceneChange.doNotStartSound = true;
 
                 file.close();
             }
@@ -357,7 +357,7 @@ void PlayPrimaryVideoChan0::addConditionalResponses() {
 
 void PlayPrimaryVideoChan0::addGoodbye() {
     for (auto &res : nancy1Goodbyes) {
-        if (res.characterID == goodbyeResponseCharacterID) {
+        if (res.characterID == _goodbyeResponseCharacterID) {
             Common::File file;
             char snd[10];
 
@@ -365,13 +365,13 @@ void PlayPrimaryVideoChan0::addGoodbye() {
             file.seek(nancy1ResponseBaseFileOffset + res.fileOffset);
             file.read(snd, 8);
 
-            responses.push_back(ResponseStruct());
-            ResponseStruct &newResponse = responses.back();
+            _responses.push_back(ResponseStruct());
+            ResponseStruct &newResponse = _responses.back();
             newResponse.soundName = snd;
             newResponse.text = file.readString();
             // response is picked randomly
-            newResponse.sceneChange.sceneID = res.sceneIDs[g_nancy->randomSource->getRandomNumber(3)];
-            newResponse.sceneChange.doNotStartSound = true;
+            newResponse._sceneChange.sceneID = res.sceneIDs[g_nancy->_randomSource->getRandomNumber(3)];
+            newResponse._sceneChange.doNotStartSound = true;
 
             file.close();
         }
diff --git a/engines/nancy/action/primaryvideo.h b/engines/nancy/action/primaryvideo.h
index 11c7513463..c5fcb13fc8 100644
--- a/engines/nancy/action/primaryvideo.h
+++ b/engines/nancy/action/primaryvideo.h
@@ -64,7 +64,7 @@ struct ResponseStruct {
     ConditionFlags conditionFlags; // 0x01
     Common::String text; // 0x06
     Common::String soundName; // 0x196
-    SceneChangeDescription sceneChange; // 0x1A0
+    SceneChangeDescription _sceneChange; // 0x1A0
     EventFlagDescription flagDesc; // 0x1A8
 };
 
@@ -88,30 +88,30 @@ public:
     void addConditionalResponses();
     void addGoodbye();
 
-    Common::String videoName; // 0x00
-    Common::Rect src; // 0x1D
+    Common::String _videoName; // 0x00
+    Common::Rect _src; // 0x1D
     // _screenPosition 0x2D
-    Common::String text; // 0x3D
+    Common::String _text; // 0x3D
 
-    SoundDescription sound; // 0x619
-    SoundDescription responseGenericSound; // 0x63B
+    SoundDescription _sound; // 0x619
+    SoundDescription _responseGenericSound; // 0x63B
 
-    byte conditionalResponseCharacterID; // 0x65E
-    byte goodbyeResponseCharacterID; // 0x65F
-    NancyFlag isDialogueExitScene; // 0x660
-    NancyFlag doNotPop; // 0x661
-    SceneChangeDescription sceneChange; // 0x662
+    byte _conditionalResponseCharacterID; // 0x65E
+    byte _goodbyeResponseCharacterID; // 0x65F
+    NancyFlag _isDialogueExitScene; // 0x660
+    NancyFlag _doNotPop; // 0x661
+    SceneChangeDescription _sceneChange; // 0x662
 
-    Common::Array<ResponseStruct> responses; // 0x69E
-    Common::Array<FlagsStruct> flagsStructs; // 0x6AA
+    Common::Array<ResponseStruct> _responses; // 0x69E
+    Common::Array<FlagsStruct> _flagsStructs; // 0x6AA
 
     AVFDecoder _decoder;
 
-    bool hasDrawnTextbox = false;
-    int16 pickedResponse = -1;
+    bool _hasDrawnTextbox = false;
+    int16 _pickedResponse = -1;
 
     // Used to avoid clashes between multiple instances in the same scene
-	static PlayPrimaryVideoChan0 *activePrimaryVideo;
+	static PlayPrimaryVideoChan0 *_activePrimaryVideo;
 
 protected:
     virtual Common::String getRecordTypeName() const override { return "PlayPrimaryVideoChan0"; }
diff --git a/engines/nancy/action/recordtypes.cpp b/engines/nancy/action/recordtypes.cpp
index 4f8cec0445..97dd9bc29c 100644
--- a/engines/nancy/action/recordtypes.cpp
+++ b/engines/nancy/action/recordtypes.cpp
@@ -41,12 +41,12 @@ namespace Nancy {
 namespace Action {
 
 void SceneChange::readData(Common::SeekableReadStream &stream) {
-    sceneChange.readData(stream);
+    _sceneChange.readData(stream);
 }
 
 void SceneChange::execute() {
-    NancySceneState.changeScene(sceneChange);
-    isDone = true;
+    NancySceneState.changeScene(_sceneChange);
+    _isDone = true;
 }
 
 void HotMultiframeSceneChange::readData(Common::SeekableReadStream &stream) {
@@ -54,24 +54,24 @@ void HotMultiframeSceneChange::readData(Common::SeekableReadStream &stream) {
     uint16 numHotspots = stream.readUint16LE();
 
     for (uint i = 0; i < numHotspots; ++i) {
-        hotspots.push_back(HotspotDescription());
-        HotspotDescription &newDesc = hotspots[i];
+        _hotspots.push_back(HotspotDescription());
+        HotspotDescription &newDesc = _hotspots[i];
         newDesc.readData(stream);
     }
 }
 
 void HotMultiframeSceneChange::execute() {
-    switch (state) {
+    switch (_state) {
     case kBegin:
         // turn main rendering on
-        state = kRun;
+        _state = kRun;
         // fall through
     case kRun:
-        hasHotspot = false;
-        for (uint i = 0; i < hotspots.size(); ++i) {
-            if (hotspots[i].frameID == NancySceneState.getSceneInfo().frameID) {
-                hasHotspot = true;
-                hotspot = hotspots[i].coords;
+        _hasHotspot = false;
+        for (uint i = 0; i < _hotspots.size(); ++i) {
+            if (_hotspots[i].frameID == NancySceneState.getSceneInfo().frameID) {
+                _hasHotspot = true;
+                _hotspot = _hotspots[i].coords;
             }
         }
         break;
@@ -83,20 +83,20 @@ void HotMultiframeSceneChange::execute() {
 
 void Hot1FrSceneChange::readData(Common::SeekableReadStream &stream) {
     SceneChange::readData(stream);
-    hotspotDesc.readData(stream);
+    _hotspotDesc.readData(stream);
 }
 
 void Hot1FrSceneChange::execute() {
-    switch (state) {
+    switch (_state) {
     case kBegin:
-        hotspot = hotspotDesc.coords;
-        state = kRun;
+        _hotspot = _hotspotDesc.coords;
+        _state = kRun;
         // fall through
     case kRun:
-        if (hotspotDesc.frameID == NancySceneState.getSceneInfo().frameID) {
-            hasHotspot = true;
+        if (_hotspotDesc.frameID == NancySceneState.getSceneInfo().frameID) {
+            _hasHotspot = true;
         } else {
-            hasHotspot = false;
+            _hasHotspot = false;
         }
         break;
     case kActionTrigger:
@@ -124,7 +124,7 @@ void MapCall::readData(Common::SeekableReadStream &stream) {
 }
 
 void MapCall::execute() {
-    execType = kRepeating;
+    _execType = kRepeating;
     NancySceneState.requestStateChange(NancyEngine::kMap);
     finishExecution();
 }
@@ -134,14 +134,14 @@ void MapCallHot1Fr::readData(Common::SeekableReadStream &stream) {
 }
 
 void MapCallHot1Fr::execute() {
-    switch (state) {
+    switch (_state) {
     case kBegin:
-        hotspot = hotspotDesc.coords;
-        state = kRun;
+        _hotspot = _hotspotDesc.coords;
+        _state = kRun;
         // fall through
     case kRun:
-        if (hotspotDesc.frameID == NancySceneState.getSceneInfo().frameID) {
-            hasHotspot = true;
+        if (_hotspotDesc.frameID == NancySceneState.getSceneInfo().frameID) {
+            _hasHotspot = true;
         }
         break;
     case kActionTrigger:
@@ -153,22 +153,22 @@ void MapCallHot1Fr::execute() {
 void MapCallHotMultiframe::readData(Common::SeekableReadStream &stream) {
     uint16 numDescs = stream.readUint16LE();
     for (uint i = 0; i < numDescs; ++i) {
-        hotspots.push_back(HotspotDescription());
-        hotspots[i].readData(stream);
+        _hotspots.push_back(HotspotDescription());
+        _hotspots[i].readData(stream);
     }
 }
 
 void MapCallHotMultiframe::execute() {
-    switch (state) {
+    switch (_state) {
     case kBegin:
-        state = kRun;
+        _state = kRun;
         // fall through
     case kRun:
-        hasHotspot = false;
-        for (uint i = 0; i < hotspots.size(); ++i) {
-            if (hotspots[i].frameID == NancySceneState.getSceneInfo().frameID) {
-                hasHotspot = true;
-                hotspot = hotspots[i].coords;
+        _hasHotspot = false;
+        for (uint i = 0; i < _hotspots.size(); ++i) {
+            if (_hotspots[i].frameID == NancySceneState.getSceneInfo().frameID) {
+                _hasHotspot = true;
+                _hotspot = _hotspots[i].coords;
             }
         }
         break;
@@ -229,7 +229,7 @@ void ResetAndStartTimer::readData(Common::SeekableReadStream &stream) {
 
 void ResetAndStartTimer::execute() {
     NancySceneState.resetAndStartTimer();
-    isDone = true;
+    _isDone = true;
 }
 
 void StopTimer::readData(Common::SeekableReadStream &stream) {
@@ -238,16 +238,16 @@ void StopTimer::readData(Common::SeekableReadStream &stream) {
 
 void StopTimer::execute() {
     NancySceneState.stopTimer();
-    isDone = true;
+    _isDone = true;
 }
 
 void EventFlags::readData(Common::SeekableReadStream &stream) {
-    flags.readData(stream);
+    _flags.readData(stream);
 }
 
 void EventFlags::execute() {
-    flags.execute();
-    isDone = true;
+    _flags.execute();
+    _isDone = true;
 }
 
 void EventFlagsMultiHS::readData(Common::SeekableReadStream &stream) {
@@ -255,31 +255,31 @@ void EventFlagsMultiHS::readData(Common::SeekableReadStream &stream) {
     uint16 numHotspots = stream.readUint16LE();
 
     for (uint16 i = 0; i < numHotspots; ++i) {
-        hotspots.push_back(HotspotDescription());
-        HotspotDescription &newDesc = hotspots[i];
+        _hotspots.push_back(HotspotDescription());
+        HotspotDescription &newDesc = _hotspots[i];
         newDesc.readData(stream);
     }
 }
 
 void EventFlagsMultiHS::execute() {
-    switch (state) {
+    switch (_state) {
     case kBegin:
         // turn main rendering on
-        state = kRun;
+        _state = kRun;
         // fall through
     case kRun:
-        hasHotspot = false;
+        _hasHotspot = false;
 
-        for (uint i = 0; i < hotspots.size(); ++i) {
-            if (hotspots[i].frameID == NancySceneState.getSceneInfo().frameID) {
-                hasHotspot = true;
-                hotspot = hotspots[i].coords;
+        for (uint i = 0; i < _hotspots.size(); ++i) {
+            if (_hotspots[i].frameID == NancySceneState.getSceneInfo().frameID) {
+                _hasHotspot = true;
+                _hotspot = _hotspots[i].coords;
             }
         }
 
         break;
     case kActionTrigger:
-        hasHotspot = false;
+        _hasHotspot = false;
         EventFlags::execute();
         finishExecution();
         break;
@@ -291,10 +291,10 @@ void LoseGame::readData(Common::SeekableReadStream &stream) {
 }
 
 void LoseGame::execute() {
-    g_nancy->sound->stopAndUnloadSpecificSounds();
+    g_nancy->_sound->stopAndUnloadSpecificSounds();
     g_nancy->setState(NancyEngine::kMainMenu);
     NancySceneState.resetStateToInit();
-    isDone = true;
+    _isDone = true;
 }
 
 void PushScene::readData(Common::SeekableReadStream &stream) {
@@ -310,24 +310,24 @@ void WinGame::readData(Common::SeekableReadStream &stream) {
 }
 
 void WinGame::execute() {
-    g_nancy->sound->stopAndUnloadSpecificSounds();
+    g_nancy->_sound->stopAndUnloadSpecificSounds();
     g_nancy->setState(NancyEngine::kCredits, NancyEngine::kMainMenu);
     
     // TODO replace with destroy()?
     NancySceneState.resetStateToInit();
-    isDone = true;
+    _isDone = true;
 }
 
 void AddInventoryNoHS::readData(Common::SeekableReadStream &stream) {
-    itemID = stream.readUint16LE();
+    _itemID = stream.readUint16LE();
 }
 
 void AddInventoryNoHS::execute() {
-    if (NancySceneState.hasItem(itemID) == kFalse) {
-        NancySceneState.addItemToInventory(itemID);
+    if (NancySceneState.hasItem(_itemID) == kFalse) {
+        NancySceneState.addItemToInventory(_itemID);
     }
 
-    isDone = true;
+    _isDone = true;
 }
 
 void RemoveInventoryNoHS::readData(Common::SeekableReadStream &stream) {
@@ -335,67 +335,67 @@ void RemoveInventoryNoHS::readData(Common::SeekableReadStream &stream) {
 }
 
 void DifficultyLevel::readData(Common::SeekableReadStream &stream) {
-    difficulty = stream.readUint16LE();
-    flag.label = stream.readSint16LE();
-    flag.flag = (NancyFlag)stream.readUint16LE();
+    _difficulty = stream.readUint16LE();
+    _flag.label = stream.readSint16LE();
+    _flag.flag = (NancyFlag)stream.readUint16LE();
 }
 
 void DifficultyLevel::execute() {
-    NancySceneState.setDifficulty(difficulty);
-    NancySceneState.setEventFlag(flag);
-    isDone = true;
+    NancySceneState.setDifficulty(_difficulty);
+    NancySceneState.setEventFlag(_flag);
+    _isDone = true;
 }
 
 void ShowInventoryItem::init() {
-    g_nancy->resource->loadImage(imageName, _fullSurface);
+    g_nancy->_resource->loadImage(_imageName, _fullSurface);
 
-    _drawSurface.create(_fullSurface, bitmaps[0].src);
+    _drawSurface.create(_fullSurface, _bitmaps[0].src);
 
     RenderObject::init();
 }
 
 void ShowInventoryItem::readData(Common::SeekableReadStream &stream) {
-    objectID = stream.readUint16LE();
+    _objectID = stream.readUint16LE();
     char name[10];
     stream.read(name, 10);
-    imageName = Common::String(name);
+    _imageName = Common::String(name);
 
     uint16 numFrames = stream.readUint16LE();
 
     for (uint i = 0; i < numFrames; ++i) {
-        bitmaps.push_back(BitmapDescription());
-        bitmaps[i].readData(stream);
+        _bitmaps.push_back(BitmapDescription());
+        _bitmaps[i].readData(stream);
     }
 }
 
 void ShowInventoryItem::execute() {
-    switch (state) {
+    switch (_state) {
     case kBegin:
         init();
         registerGraphics();
-        state = kRun;
+        _state = kRun;
         // fall through
     case kRun: {
         int newFrame = -1;
 
-        for (uint i = 0; i < bitmaps.size(); ++i) {
-            if (bitmaps[i].frameID == NancySceneState.getSceneInfo().frameID) {
+        for (uint i = 0; i < _bitmaps.size(); ++i) {
+            if (_bitmaps[i].frameID == NancySceneState.getSceneInfo().frameID) {
                 newFrame = i;
                 break;
             }
         }
 
-        if (newFrame != drawnFrameID) {
-            drawnFrameID = newFrame;
+        if (newFrame != _drawnFrameID) {
+            _drawnFrameID = newFrame;
 
             if (newFrame != -1) {
-                hasHotspot = true;
-                hotspot = bitmaps[newFrame].dest;
-                _drawSurface.create(_fullSurface, bitmaps[newFrame].src);
-                _screenPosition = bitmaps[newFrame].dest;
+                _hasHotspot = true;
+                _hotspot = _bitmaps[newFrame].dest;
+                _drawSurface.create(_fullSurface, _bitmaps[newFrame].src);
+                _screenPosition = _bitmaps[newFrame].dest;
                 setVisible(true);
             } else {
-                hasHotspot = false;
+                _hasHotspot = false;
                 setVisible(false);
             }
         }
@@ -403,10 +403,10 @@ void ShowInventoryItem::execute() {
         break;
     }
     case kActionTrigger:
-        g_nancy->sound->playSound(24); // Hardcoded by original engine
-        NancySceneState.addItemToInventory(objectID);
+        g_nancy->_sound->playSound(24); // Hardcoded by original engine
+        NancySceneState.addItemToInventory(_objectID);
         setVisible(false);
-        hasHotspot = false;
+        _hasHotspot = false;
         finishExecution();
         break;
     }
@@ -419,33 +419,33 @@ void ShowInventoryItem::onPause(bool pause) {
 }
 
 void PlayDigiSoundAndDie::readData(Common::SeekableReadStream &stream) {
-    sound.read(stream, SoundDescription::kDIGI);
-    sceneChange.readData(stream);
-    flagOnTrigger.label = stream.readSint16LE();
-    flagOnTrigger.flag = (NancyFlag)stream.readByte();
+    _sound.read(stream, SoundDescription::kDIGI);
+    _sceneChange.readData(stream);
+    _flagOnTrigger.label = stream.readSint16LE();
+    _flagOnTrigger.flag = (NancyFlag)stream.readByte();
     stream.skip(2);
 }
 
 void PlayDigiSoundAndDie::execute() {
-    switch (state) {
+    switch (_state) {
     case kBegin:
-        g_nancy->sound->loadSound(sound);
-        g_nancy->sound->playSound(sound);
-        state = kRun;
+        g_nancy->_sound->loadSound(_sound);
+        g_nancy->_sound->playSound(_sound);
+        _state = kRun;
         break;
     case kRun:
-        if (!g_nancy->sound->isSoundPlaying(sound)) {
-            state = kActionTrigger;
+        if (!g_nancy->_sound->isSoundPlaying(_sound)) {
+            _state = kActionTrigger;
         }
 
         break;
     case kActionTrigger:
-        if (sceneChange.sceneID != 9999) {
-            NancySceneState.changeScene(sceneChange);
+        if (_sceneChange.sceneID != 9999) {
+            NancySceneState.changeScene(_sceneChange);
         }
         
-        NancySceneState.setEventFlag(flagOnTrigger);
-        g_nancy->sound->stopSound(sound);
+        NancySceneState.setEventFlag(_flagOnTrigger);
+        g_nancy->_sound->stopSound(_sound);
 
         finishExecution();
         break;
@@ -457,33 +457,33 @@ void PlaySoundPanFrameAnchorAndDie::readData(Common::SeekableReadStream &stream)
 }
 
 void PlaySoundMultiHS::readData(Common::SeekableReadStream &stream) {
-    sound.read(stream, SoundDescription::kNormal);
-    sceneChange.readData(stream);
-    flag.label = stream.readSint16LE();
-    flag.flag = (NancyFlag)stream.readByte();
+    _sound.read(stream, SoundDescription::kNormal);
+    _sceneChange.readData(stream);
+    _flag.label = stream.readSint16LE();
+    _flag.flag = (NancyFlag)stream.readByte();
     stream.skip(2);
     uint16 numHotspots = stream.readUint16LE();
 
     for (uint i = 0; i < numHotspots; ++i) {
-        hotspots.push_back(HotspotDescription());
-        hotspots.back().frameID = stream.readUint16LE();
-        readRect(stream, hotspots.back().coords);
+        _hotspots.push_back(HotspotDescription());
+        _hotspots.back().frameID = stream.readUint16LE();
+        readRect(stream, _hotspots.back().coords);
     }
 }
 
 void PlaySoundMultiHS::execute() {
-    switch (state) {
+    switch (_state) {
     case kBegin:
-        state = kRun;
+        _state = kRun;
         // fall through
     case kRun: {
-        hasHotspot = false;
+        _hasHotspot = false;
         uint currentFrame = NancySceneState.getSceneInfo().frameID;
 
-        for (uint i = 0; i < hotspots.size(); ++i) {
-            if (hotspots[i].frameID == currentFrame) {
-                hotspot = hotspots[i].coords;
-                hasHotspot = true;
+        for (uint i = 0; i < _hotspots.size(); ++i) {
+            if (_hotspots[i].frameID == currentFrame) {
+                _hotspot = _hotspots[i].coords;
+                _hasHotspot = true;
                 break;
             }
         }
@@ -491,22 +491,22 @@ void PlaySoundMultiHS::execute() {
         break;
     }
     case kActionTrigger:
-        g_nancy->sound->loadSound(sound);
-        g_nancy->sound->playSound(sound);
-        NancySceneState.changeScene(sceneChange);
-        NancySceneState.setEventFlag(flag);
+        g_nancy->_sound->loadSound(_sound);
+        g_nancy->_sound->playSound(_sound);
+        NancySceneState.changeScene(_sceneChange);
+        NancySceneState.setEventFlag(_flag);
         finishExecution();
         break;
     }
 }
 
 void HintSystem::readData(Common::SeekableReadStream &stream) {
-    characterID = stream.readByte();
-    genericSound.read(stream, SoundDescription::kNormal);
+    _characterID = stream.readByte();
+    _genericSound.read(stream, SoundDescription::kNormal);
 }
 
 void HintSystem::execute() {
-    switch (state) {
+    switch (_state) {
     case kBegin:
         if (NancySceneState.getHintsRemaining() > 0) {
             selectHint();
@@ -515,35 +515,35 @@ void HintSystem::execute() {
         }
 
         NancySceneState.getTextbox().clear();
-        NancySceneState.getTextbox().addTextLine(text);
+        NancySceneState.getTextbox().addTextLine(_text);
 
-        g_nancy->sound->loadSound(genericSound);
-        g_nancy->sound->playSound(genericSound);
-        state = kRun;
+        g_nancy->_sound->loadSound(_genericSound);
+        g_nancy->_sound->playSound(_genericSound);
+        _state = kRun;
         break;
     case kRun:
-        if (!g_nancy->sound->isSoundPlaying(genericSound)) {
-            g_nancy->sound->stopSound(genericSound);
-            state = kActionTrigger;
+        if (!g_nancy->_sound->isSoundPlaying(_genericSound)) {
+            g_nancy->_sound->stopSound(_genericSound);
+            _state = kActionTrigger;
         } else {
             break;
         }
 
         // fall through
     case kActionTrigger:
-        NancySceneState.useHint(hintID, hintWeight);
+        NancySceneState.useHint(_hintID, _hintWeight);
         NancySceneState.getTextbox().clear();
 
-        NancySceneState.changeScene(sceneChange);
+        NancySceneState.changeScene(_sceneChange);
 
-        isDone = true;
+        _isDone = true;
         break;
     }
 }
 
 void HintSystem::selectHint() {
     for (auto &hint : nancy1Hints) {
-        if (hint.characterID != characterID) {
+        if (hint.characterID != _characterID) {
             continue;
         }
 
@@ -580,8 +580,8 @@ void HintSystem::selectHint() {
 
 void HintSystem::getHint(uint hint, uint difficulty) {
     uint fileOffset;
-    if (characterID < 3) {
-        fileOffset = nancy1HintOffsets[characterID];
+    if (_characterID < 3) {
+        fileOffset = nancy1HintOffsets[_characterID];
     }
 
     fileOffset += 0x288 * hint;
@@ -590,26 +590,26 @@ void HintSystem::getHint(uint hint, uint difficulty) {
     file.open("game.exe");
     file.seek(fileOffset);
 
-    hintID = file.readSint16LE();
-    hintWeight = file.readSint16LE();
+    _hintID = file.readSint16LE();
+    _hintWeight = file.readSint16LE();
 
     file.seek(difficulty * 10, SEEK_CUR);
 
     char soundName[10];
     file.read(soundName, 10);
-    genericSound.name = soundName;
+    _genericSound.name = soundName;
 
     file.seek(-(difficulty * 10) - 10, SEEK_CUR);
     file.seek(30 + difficulty * 200, SEEK_CUR);
 
     char textBuf[200];
     file.read(textBuf, 200);
-    text = textBuf;
+    _text = textBuf;
 
     file.seek(-(difficulty * 200) - 200, SEEK_CUR);
     file.seek(600, SEEK_CUR);
 
-    sceneChange.readData(file);
+    _sceneChange.readData(file);
 }
 
 }
diff --git a/engines/nancy/action/recordtypes.h b/engines/nancy/action/recordtypes.h
index de15567d17..0ab9a0544c 100644
--- a/engines/nancy/action/recordtypes.h
+++ b/engines/nancy/action/recordtypes.h
@@ -44,7 +44,7 @@ public:
     virtual void readData(Common::SeekableReadStream &stream) override;
     virtual void execute() override;
 
-    SceneChangeDescription sceneChange;
+    SceneChangeDescription _sceneChange;
 
 protected:
     virtual Common::String getRecordTypeName() const override { return "SceneChange"; }
@@ -55,7 +55,7 @@ public:
     virtual void readData(Common::SeekableReadStream &stream) override;
     virtual void execute() override;
 
-    Common::Array<HotspotDescription> hotspots;
+    Common::Array<HotspotDescription> _hotspots;
 
 protected:
     virtual Common::String getRecordTypeName() const override { return "HotMultiframeSceneChange"; }
@@ -66,7 +66,7 @@ public:
     virtual void readData(Common::SeekableReadStream &stream) override;
     virtual void execute() override;
 
-    HotspotDescription hotspotDesc;
+    HotspotDescription _hotspotDesc;
 
 protected:
     virtual Common::String getRecordTypeName() const override { return "Hot1FrSceneChange"; }
@@ -100,7 +100,7 @@ public:
     virtual void readData(Common::SeekableReadStream &stream) override;
     // TODO add a Start and Stop subclass
 
-    byte type = 0;
+    byte _type = 0;
 
 protected:
     virtual Common::String getRecordTypeName() const override { return "StartStopPlayerScrolling"; }
@@ -122,7 +122,7 @@ public:
     virtual void readData(Common::SeekableReadStream &stream) override;
     virtual void execute() override;
 
-    HotspotDescription hotspotDesc;
+    HotspotDescription _hotspotDesc;
     
 protected:
     virtual Common::String getRecordTypeName() const override { return "MapCallHot1Fr"; }
@@ -133,7 +133,7 @@ public:
     virtual void readData(Common::SeekableReadStream &stream) override;
     virtual void execute() override;
 
-    Common::Array<HotspotDescription> hotspots;
+    Common::Array<HotspotDescription> _hotspots;
     
 protected:
     virtual Common::String getRecordTypeName() const override { return "MapCallHotMultiframe"; }
@@ -242,7 +242,7 @@ public:
     virtual void readData(Common::SeekableReadStream &stream) override;
     virtual void execute() override;
 
-    MultiEventFlagDescription flags;
+    MultiEventFlagDescription _flags;
     
 protected:
     virtual Common::String getRecordTypeName() const override { return "EventFlags"; }
@@ -253,7 +253,7 @@ public:
     virtual void readData(Common::SeekableReadStream &stream) override;
     virtual void execute() override;
 
-    Common::Array<HotspotDescription> hotspots;
+    Common::Array<HotspotDescription> _hotspots;
     
 protected:
     virtual Common::String getRecordTypeName() const override { return "EventFlagsMultiHS"; }
@@ -298,7 +298,7 @@ public:
     virtual void readData(Common::SeekableReadStream &stream) override;
     virtual void execute() override;
     
-    uint itemID;
+    uint _itemID;
     
 protected:
     virtual Common::String getRecordTypeName() const override { return "AddInventoryNoHS"; }
@@ -317,8 +317,8 @@ public:
     virtual void readData(Common::SeekableReadStream &stream) override;
     virtual void execute() override;
 
-    uint16 difficulty = 0;
-    EventFlagDescription flag;
+    uint16 _difficulty = 0;
+    EventFlagDescription _flag;
     
 protected:
     virtual Common::String getRecordTypeName() const override { return "DifficultyLevel"; }
@@ -335,11 +335,11 @@ public:
     virtual void init() override;
     virtual void onPause(bool pause) override;
  
-    uint16 objectID = 0;
-    Common::String imageName;
-    Common::Array<BitmapDescription> bitmaps;
+    uint16 _objectID = 0;
+    Common::String _imageName;
+    Common::Array<BitmapDescription> _bitmaps;
 
-    int16 drawnFrameID = -1;
+    int16 _drawnFrameID = -1;
     Graphics::ManagedSurface _fullSurface;
     
 protected:
@@ -355,9 +355,9 @@ public:
     virtual void execute() override;
     // TODO subclass into Play and Stop (?)
 
-    SoundDescription sound;
-    SceneChangeDescription sceneChange;
-    EventFlagDescription flagOnTrigger;
+    SoundDescription _sound;
+    SceneChangeDescription _sceneChange;
+    EventFlagDescription _flagOnTrigger;
     
 protected:
     virtual Common::String getRecordTypeName() const override { return "PlayDigiSoundAndDie"; }
@@ -376,10 +376,10 @@ public:
     virtual void readData(Common::SeekableReadStream &stream) override;
     virtual void execute() override;
 
-    SoundDescription sound; // 0x0
-    SceneChangeDescription sceneChange; // 0x22
-    EventFlagDescription flag; // 0x2A
-    Common::Array<HotspotDescription> hotspots; // 0x31
+    SoundDescription _sound; // 0x0
+    SceneChangeDescription _sceneChange; // 0x22
+    EventFlagDescription _flag; // 0x2A
+    Common::Array<HotspotDescription> _hotspots; // 0x31
     
 protected:
     virtual Common::String getRecordTypeName() const override { return "PlaySoundMultiHS"; }
@@ -390,13 +390,13 @@ public:
     virtual void readData(Common::SeekableReadStream &stream) override;
     virtual void execute() override;
 
-    byte characterID; // 0x00
-    SoundDescription genericSound; // 0x01
+    byte _characterID; // 0x00
+    SoundDescription _genericSound; // 0x01
 
-    Common::String text;
-    SceneChangeDescription sceneChange;
-    uint16 hintID;
-    int16 hintWeight;
+    Common::String _text;
+    SceneChangeDescription _sceneChange;
+    uint16 _hintID;
+    int16 _hintWeight;
 
     void selectHint();
     void getHint(uint hint, uint difficulty);
diff --git a/engines/nancy/action/rotatinglockpuzzle.cpp b/engines/nancy/action/rotatinglockpuzzle.cpp
index ea13af2bae..6661122ac1 100644
--- a/engines/nancy/action/rotatinglockpuzzle.cpp
+++ b/engines/nancy/action/rotatinglockpuzzle.cpp
@@ -40,121 +40,121 @@ void RotatingLockPuzzle::init() {
     
     setTransparent(true);
 
-    g_nancy->resource->loadImage(imageName, image);
+    g_nancy->_resource->loadImage(_imageName, _image);
 }
 
 void RotatingLockPuzzle::readData(Common::SeekableReadStream &stream) {
     char buf[10];
     stream.read(buf, 10);
-    imageName = buf;
+    _imageName = buf;
 
     uint numDials = stream.readUint16LE();
 
     for (uint i = 0; i < 10; ++i) {
-        srcRects.push_back(Common::Rect());
-        readRect(stream, srcRects.back());
+        _srcRects.push_back(Common::Rect());
+        readRect(stream, _srcRects.back());
     }
 
     for (uint i = 0; i < numDials; ++i) {
-        destRects.push_back(Common::Rect());
-        readRect(stream, destRects.back());
+        _destRects.push_back(Common::Rect());
+        readRect(stream, _destRects.back());
 
         if (i == 0) {
-            _screenPosition = destRects.back();
+            _screenPosition = _destRects.back();
         } else {
-            _screenPosition.extend(destRects.back());
+            _screenPosition.extend(_destRects.back());
         }
     }
 
     stream.skip((8 - numDials) * 16);
 
     for (uint i = 0; i < numDials; ++i) {
-        upHotspots.push_back(Common::Rect());
-        readRect(stream, upHotspots.back());
+        _upHotspots.push_back(Common::Rect());
+        readRect(stream, _upHotspots.back());
     }
 
     stream.skip((8 - numDials) * 16);
 
     for (uint i = 0; i < numDials; ++i) {
-        downHotspots.push_back(Common::Rect());
-        readRect(stream, downHotspots.back());
+        _downHotspots.push_back(Common::Rect());
+        readRect(stream, _downHotspots.back());
     }
 
     stream.skip((8 - numDials) * 16);
 
     for (uint i = 0; i < numDials; ++i) {
-        correctSequence.push_back(stream.readByte());
+        _correctSequence.push_back(stream.readByte());
     }
 
     stream.skip(8 - numDials);
 
-    clickSound.read(stream, SoundDescription::kNormal);
-    solveExitScene.readData(stream);
+    _clickSound.read(stream, SoundDescription::kNormal);
+    _solveExitScene.readData(stream);
     stream.skip(2); // shouldStopRendering, useless
-    flagOnSolve.label = stream.readSint16LE();
-    flagOnSolve.flag = (NancyFlag)stream.readByte();
-    solveSoundDelay = stream.readUint16LE();
-    solveSound.read(stream, SoundDescription::kNormal);
-    exitScene.readData(stream);
+    _flagOnSolve.label = stream.readSint16LE();
+    _flagOnSolve.flag = (NancyFlag)stream.readByte();
+    _solveSoundDelay = stream.readUint16LE();
+    _solveSound.read(stream, SoundDescription::kNormal);
+    _exitScene.readData(stream);
     stream.skip(2); // shouldStopRendering, useless
-    flagOnExit.label = stream.readSint16LE();
-    flagOnExit.flag = (NancyFlag)stream.readByte();
-    readRect(stream, exitHotspot);
+    _flagOnExit.label = stream.readSint16LE();
+    _flagOnExit.flag = (NancyFlag)stream.readByte();
+    readRect(stream, _exitHotspot);
 }
 
 void RotatingLockPuzzle::execute() {
-    switch (state) {
+    switch (_state) {
     case kBegin:
         init();
         registerGraphics();
 
-        for (uint i = 0; i < correctSequence.size(); ++i) {
-            currentSequence.push_back(g_nancy->randomSource->getRandomNumber(9));
+        for (uint i = 0; i < _correctSequence.size(); ++i) {
+            _currentSequence.push_back(g_nancy->_randomSource->getRandomNumber(9));
             drawDial(i);
         }
 
-        g_nancy->sound->loadSound(clickSound);
-        g_nancy->sound->loadSound(solveSound);
-        state = kRun;
+        g_nancy->_sound->loadSound(_clickSound);
+        g_nancy->_sound->loadSound(_solveSound);
+        _state = kRun;
         // fall through
     case kRun:
-        switch (solveState) {
+        switch (_solveState) {
         case kNotSolved:
-            for (uint i = 0; i < correctSequence.size(); ++i) {
-                if (currentSequence[i] != (int16)correctSequence[i]) {
+            for (uint i = 0; i < _correctSequence.size(); ++i) {
+                if (_currentSequence[i] != (int16)_correctSequence[i]) {
                     return;
                 }
             }
 
-            NancySceneState.setEventFlag(flagOnSolve);
-            solveSoundPlayTime = g_nancy->getTotalPlayTime() + solveSoundDelay * 1000;
-            solveState = kPlaySound;
+            NancySceneState.setEventFlag(_flagOnSolve);
+            _solveSoundPlayTime = g_nancy->getTotalPlayTime() + _solveSoundDelay * 1000;
+            _solveState = kPlaySound;
             // fall through
         case kPlaySound:
-            if (g_nancy->getTotalPlayTime() <= solveSoundPlayTime) {
+            if (g_nancy->getTotalPlayTime() <= _solveSoundPlayTime) {
                 break;
             }
 
-            g_nancy->sound->playSound(solveSound);
-            solveState = kWaitForSound;
+            g_nancy->_sound->playSound(_solveSound);
+            _solveState = kWaitForSound;
             break;
         case kWaitForSound:
-            if (!g_nancy->sound->isSoundPlaying(solveSound)) {
-                state = kActionTrigger;
+            if (!g_nancy->_sound->isSoundPlaying(_solveSound)) {
+                _state = kActionTrigger;
             }
 
             break;
         }
         break;
     case kActionTrigger:
-        g_nancy->sound->stopSound(clickSound);
-        g_nancy->sound->stopSound(solveSound);
+        g_nancy->_sound->stopSound(_clickSound);
+        g_nancy->_sound->stopSound(_solveSound);
 
-        if (solveState == kNotSolved) {
-            NancySceneState.changeScene(exitScene);
-            NancySceneState.setEventFlag(flagOnExit);
+        if (_solveState == kNotSolved) {
+            NancySceneState.changeScene(_exitScene);
+            NancySceneState.setEventFlag(_flagOnExit);
         } else {
-            NancySceneState.changeScene(solveExitScene);
+            NancySceneState.changeScene(_solveExitScene);
         }
 
         finishExecution();
@@ -162,28 +162,28 @@ void RotatingLockPuzzle::execute() {
 }
 
 void RotatingLockPuzzle::handleInput(NancyInput &input) {
-    if (solveState != kNotSolved) {
+    if (_solveState != kNotSolved) {
         return;
     }
 
-    if (NancySceneState.getViewport().convertViewportToScreen(exitHotspot).contains(input.mousePos)) {
-        g_nancy->cursorManager->setCursorType(CursorManager::kExitArrow);
+    if (NancySceneState.getViewport().convertViewportToScreen(_exitHotspot).contains(input.mousePos)) {
+        g_nancy->_cursorManager->setCursorType(CursorManager::kExitArrow);
 
         if (input.input & NancyInput::kLeftMouseButtonUp) {
-            state = kActionTrigger;
+            _state = kActionTrigger;
         }
 
         return;
     }
 
-    for (uint i = 0; i < upHotspots.size(); ++i) {
-        if (NancySceneState.getViewport().convertViewportToScreen(upHotspots[i]).contains(input.mousePos)) {
-            g_nancy->cursorManager->setCursorType(CursorManager::kHotspot);
+    for (uint i = 0; i < _upHotspots.size(); ++i) {
+        if (NancySceneState.getViewport().convertViewportToScreen(_upHotspots[i]).contains(input.mousePos)) {
+            g_nancy->_cursorManager->setCursorType(CursorManager::kHotspot);
 
             if (input.input & NancyInput::kLeftMouseButtonUp) {
-                g_nancy->sound->playSound(clickSound);
+                g_nancy->_sound->playSound(_clickSound);
                 
-                currentSequence[i] = ++currentSequence[i] > 9 ? 0 : currentSequence[i];
+                _currentSequence[i] = ++_currentSequence[i] > 9 ? 0 : _currentSequence[i];
                 drawDial(i);
             }
 
@@ -191,16 +191,16 @@ void RotatingLockPuzzle::handleInput(NancyInput &input) {
         }
     }
 
-    for (uint i = 0; i < downHotspots.size(); ++i) {
-        if (NancySceneState.getViewport().convertViewportToScreen(downHotspots[i]).contains(input.mousePos)) {
-            g_nancy->cursorManager->setCursorType(CursorManager::kHotspot);
+    for (uint i = 0; i < _downHotspots.size(); ++i) {
+        if (NancySceneState.getViewport().convertViewportToScreen(_downHotspots[i]).contains(input.mousePos)) {
+            g_nancy->_cursorManager->setCursorType(CursorManager::kHotspot);
 
             if (input.input & NancyInput::kLeftMouseButtonUp) {
-                g_nancy->sound->playSound(clickSound);
+                g_nancy->_sound->playSound(_clickSound);
 
-                int8 n = currentSequence[i];
+                int8 n = _currentSequence[i];
                 n = --n < 0 ? 9 : n;
-                currentSequence[i] = n;
+                _currentSequence[i] = n;
                 drawDial(i);
             }
             
@@ -216,8 +216,8 @@ void RotatingLockPuzzle::onPause(bool pause) {
 }
 
 void RotatingLockPuzzle::drawDial(uint id) {
-    Common::Point destPoint(destRects[id].left - _screenPosition.left, destRects[id].top - _screenPosition.top);
-    _drawSurface.blitFrom(image, srcRects[currentSequence[id]], destPoint);
+    Common::Point destPoint(_destRects[id].left - _screenPosition.left, _destRects[id].top - _screenPosition.top);
+    _drawSurface.blitFrom(_image, _srcRects[_currentSequence[id]], destPoint);
 
     _needsRedraw = true;
 }
diff --git a/engines/nancy/action/rotatinglockpuzzle.h b/engines/nancy/action/rotatinglockpuzzle.h
index 3245424b40..a4312f8eae 100644
--- a/engines/nancy/action/rotatinglockpuzzle.h
+++ b/engines/nancy/action/rotatinglockpuzzle.h
@@ -49,26 +49,26 @@ public:
     virtual void handleInput(NancyInput &input) override;
     virtual void onPause(bool pause) override;
 
-    Common::String imageName; // 0x00
+    Common::String _imageName; // 0x00
     // 0xA numDials
-    Common::Array<Common::Rect> srcRects; // 0xC, 10
-    Common::Array<Common::Rect> destRects; // 0xAC, 8
-    Common::Array<Common::Rect> upHotspots; // 0x12C, 8
-    Common::Array<Common::Rect> downHotspots; // 0x1AC, 8
-    Common::Array<byte> correctSequence; // 0x22C
-    Nancy::SoundDescription clickSound; // 0x234, kNormal
-    SceneChangeDescription solveExitScene; // 0x256
-    EventFlagDescription flagOnSolve; // 0x260
-    uint16 solveSoundDelay; // 0x263
-    Nancy::SoundDescription solveSound; // 0x265
-    SceneChangeDescription exitScene; // 0x287
-    EventFlagDescription flagOnExit; // 0x291
-    Common::Rect exitHotspot; // 0x294
-
-    SolveState solveState = kNotSolved;
-    Graphics::ManagedSurface image;
-    Common::Array<byte> currentSequence;
-    Time solveSoundPlayTime;
+    Common::Array<Common::Rect> _srcRects; // 0xC, 10
+    Common::Array<Common::Rect> _destRects; // 0xAC, 8
+    Common::Array<Common::Rect> _upHotspots; // 0x12C, 8
+    Common::Array<Common::Rect> _downHotspots; // 0x1AC, 8
+    Common::Array<byte> _correctSequence; // 0x22C
+    Nancy::SoundDescription _clickSound; // 0x234, kNormal
+    SceneChangeDescription _solveExitScene; // 0x256
+    EventFlagDescription _flagOnSolve; // 0x260
+    uint16 _solveSoundDelay; // 0x263
+    Nancy::SoundDescription _solveSound; // 0x265
+    SceneChangeDescription _exitScene; // 0x287
+    EventFlagDescription _flagOnExit; // 0x291
+    Common::Rect _exitHotspot; // 0x294
+
+    SolveState _solveState = kNotSolved;
+    Graphics::ManagedSurface _image;
+    Common::Array<byte> _currentSequence;
+    Time _solveSoundPlayTime;
 
 
 protected:
diff --git a/engines/nancy/action/secondarymovie.cpp b/engines/nancy/action/secondarymovie.cpp
index d84c23c42c..c0216ff1d2 100644
--- a/engines/nancy/action/secondarymovie.cpp
+++ b/engines/nancy/action/secondarymovie.cpp
@@ -32,7 +32,7 @@ namespace Action {
 PlaySecondaryMovie::~PlaySecondaryMovie() {
     _decoder.close();
     
-    if (hideMouse == kTrue && unknown == 5) {
+    if (_hideMouse == kTrue && _unknown == 5) {
         g_nancy->setMouseEnabled(true);
     }
 }
@@ -40,29 +40,29 @@ PlaySecondaryMovie::~PlaySecondaryMovie() {
 void PlaySecondaryMovie::readData(Common::SeekableReadStream &stream) {  
     char name[10];
     stream.read(name, 10);
-    videoName = name;
+    _videoName = name;
 
     stream.skip(0x12);
-    unknown = stream.readUint16LE();
-	hideMouse = (NancyFlag)stream.readUint16LE();
-    isReverse = (NancyFlag)stream.readUint16LE();
-    firstFrame = (NancyFlag)stream.readUint16LE();
-    lastFrame = (NancyFlag)stream.readUint16LE();
+    _unknown = stream.readUint16LE();
+	_hideMouse = (NancyFlag)stream.readUint16LE();
+    _isReverse = (NancyFlag)stream.readUint16LE();
+    _firstFrame = (NancyFlag)stream.readUint16LE();
+    _lastFrame = (NancyFlag)stream.readUint16LE();
 
     for (uint i = 0; i < 15; ++i) {
-        frameFlags[i].frameID = stream.readSint16LE();
-        frameFlags[i].flagDesc.label = stream.readSint16LE();
-        frameFlags[i].flagDesc.flag = (NancyFlag)stream.readUint16LE();
+        _frameFlags[i].frameID = stream.readSint16LE();
+        _frameFlags[i].flagDesc.label = stream.readSint16LE();
+        _frameFlags[i].flagDesc.flag = (NancyFlag)stream.readUint16LE();
     }
 
-    triggerFlags.readData(stream);
-    sound.read(stream, SoundDescription::kNormal);
-    sceneChange.readData(stream);
+    _triggerFlags.readData(stream);
+    _sound.read(stream, SoundDescription::kNormal);
+    _sceneChange.readData(stream);
 
     uint16 numVideoDescs = stream.readUint16LE();
     for (uint i = 0; i < numVideoDescs; ++i) {
-        videoDescs.push_back(SecondaryVideoDescription());
-        videoDescs[i].readData(stream);
+        _videoDescs.push_back(SecondaryVideoDescription());
+        _videoDescs[i].readData(stream);
     }
 }
 
@@ -71,7 +71,7 @@ void PlaySecondaryMovie::init() {
         _decoder.close();
     }
 
-    _decoder.loadFile(videoName + ".avf");
+    _decoder.loadFile(_videoName + ".avf");
     _drawSurface.create(_decoder.getWidth(), _decoder.getHeight(), GraphicsManager::getInputPixelFormat());
     _screenPosition = _drawSurface.getBounds();
 
@@ -83,43 +83,43 @@ void PlaySecondaryMovie::updateGraphics() {
         return;
     }
 
-    if (!_decoder.isPlaying() && _isVisible && !isFinished) {
+    if (!_decoder.isPlaying() && _isVisible && !_isFinished) {
         _decoder.start();
 
-		if (isReverse == kTrue) {
+		if (_isReverse == kTrue) {
 			_decoder.setRate(-_decoder.getRate());
-			_decoder.seekToFrame(lastFrame);
+			_decoder.seekToFrame(_lastFrame);
 		} else {
-			_decoder.seekToFrame(firstFrame);
+			_decoder.seekToFrame(_firstFrame);
 		}
     }
 
     if (_decoder.needsUpdate()) {
         uint descID = 0;
 
-        for (uint i = 0; i < videoDescs.size(); ++i) {
-            if (videoDescs[i].frameID == _curViewportFrame) {
+        for (uint i = 0; i < _videoDescs.size(); ++i) {
+            if (_videoDescs[i].frameID == _curViewportFrame) {
                 descID = i;
             }
         }
 
-        _drawSurface.blitFrom(*_decoder.decodeNextFrame(), videoDescs[descID].srcRect, Common::Point());
+        _drawSurface.blitFrom(*_decoder.decodeNextFrame(), _videoDescs[descID].srcRect, Common::Point());
         _needsRedraw = true;
         
-        for (auto f : frameFlags) {
+        for (auto f : _frameFlags) {
             if (_decoder.getCurFrame() == f.frameID) {
                 NancySceneState.setEventFlag(f.flagDesc);
             }
         }
     }
 
-	if ((_decoder.getCurFrame() == lastFrame && isReverse == kFalse) ||
-	    (_decoder.getCurFrame() == firstFrame && isReverse == kTrue)) {
-		if (!g_nancy->sound->isSoundPlaying(sound)) {
-			g_nancy->sound->stopSound(sound);
+	if ((_decoder.getCurFrame() == _lastFrame && _isReverse == kFalse) ||
+	    (_decoder.getCurFrame() == _firstFrame && _isReverse == kTrue)) {
+		if (!g_nancy->_sound->isSoundPlaying(_sound)) {
+			g_nancy->_sound->stopSound(_sound);
 			_decoder.stop();
-            isFinished = true;
-			state = kActionTrigger;
+            _isFinished = true;
+			_state = kActionTrigger;
 		}
 	}
 
@@ -135,18 +135,18 @@ void PlaySecondaryMovie::onPause(bool pause) {
 }
 
 void PlaySecondaryMovie::execute() {
-    switch (state) {
+    switch (_state) {
     case kBegin:
         init();
         registerGraphics();
-        g_nancy->sound->loadSound(sound);
-        g_nancy->sound->playSound(sound);
+        g_nancy->_sound->loadSound(_sound);
+        g_nancy->_sound->playSound(_sound);
 
-        if (hideMouse == kTrue) {
+        if (_hideMouse == kTrue) {
             g_nancy->setMouseEnabled(false);
         }
 
-        state = kRun;
+        _state = kRun;
         // fall through
     case kRun: {
         int newFrame = NancySceneState.getSceneInfo().frameID;
@@ -154,15 +154,15 @@ void PlaySecondaryMovie::execute() {
         if (newFrame != _curViewportFrame) {
             _curViewportFrame = newFrame;
             int activeFrame = -1;
-            for (uint i = 0; i < videoDescs.size(); ++i) {
-                if (newFrame == videoDescs[i].frameID) {
+            for (uint i = 0; i < _videoDescs.size(); ++i) {
+                if (newFrame == _videoDescs[i].frameID) {
                     activeFrame = i;
                     break;
                 }
             }
 
             if (activeFrame != -1) {
-                _screenPosition = videoDescs[activeFrame].destRect;
+                _screenPosition = _videoDescs[activeFrame].destRect;
                 setVisible(true);
             } else {
                 setVisible(false);
@@ -172,12 +172,12 @@ void PlaySecondaryMovie::execute() {
         break;
     }
     case kActionTrigger:
-        triggerFlags.execute();
-        if (unknown == 5) {
-            NancySceneState.changeScene(sceneChange);
+        _triggerFlags.execute();
+        if (_unknown == 5) {
+            NancySceneState.changeScene(_sceneChange);
         } else {
             // Not changing the scene so enable the mouse now
-            if (hideMouse == kTrue) {
+            if (_hideMouse == kTrue) {
                 g_nancy->setMouseEnabled(true);
             }
         }
diff --git a/engines/nancy/action/secondarymovie.h b/engines/nancy/action/secondarymovie.h
index 524ae2faa2..8a3583f6c7 100644
--- a/engines/nancy/action/secondarymovie.h
+++ b/engines/nancy/action/secondarymovie.h
@@ -45,7 +45,7 @@ public:
     PlaySecondaryMovie(RenderObject &redrawFrom) :
         RenderObject(redrawFrom),
         _curViewportFrame(-1),
-        isFinished(false) {}
+        _isFinished(false) {}
     virtual ~PlaySecondaryMovie();
 
     virtual void init() override;
@@ -55,20 +55,20 @@ public:
     virtual void readData(Common::SeekableReadStream &stream) override;
     virtual void execute() override;
 
-    Common::String videoName; // 0x00
+    Common::String _videoName; // 0x00
 
-    uint16 unknown; // 0x1C
-    NancyFlag hideMouse; // 0x1E
-    NancyFlag isReverse; // 0x20
-    uint16 firstFrame; // 0x22
-    uint16 lastFrame; // 0x24
-    FlagAtFrame frameFlags[15]; // 0x26
-    MultiEventFlagDescription triggerFlags; // 0x80
+    uint16 _unknown; // 0x1C
+    NancyFlag _hideMouse; // 0x1E
+    NancyFlag _isReverse; // 0x20
+    uint16 _firstFrame; // 0x22
+    uint16 _lastFrame; // 0x24
+    FlagAtFrame _frameFlags[15]; // 0x26
+    MultiEventFlagDescription _triggerFlags; // 0x80
 
-    SoundDescription sound; // 0xA8
+    SoundDescription _sound; // 0xA8
 
-    SceneChangeDescription sceneChange; // 0xCA
-    Common::Array<SecondaryVideoDescription> videoDescs; // 0xD4
+    SceneChangeDescription _sceneChange; // 0xCA
+    Common::Array<SecondaryVideoDescription> _videoDescs; // 0xD4
 
 protected:
     virtual Common::String getRecordTypeName() const override { return "PlaySecondaryMovie"; }
@@ -78,7 +78,7 @@ protected:
 
     AVFDecoder _decoder;
     int _curViewportFrame;
-    bool isFinished;
+    bool _isFinished;
 };
     
 } // End of namespace Action
diff --git a/engines/nancy/action/secondaryvideo.cpp b/engines/nancy/action/secondaryvideo.cpp
index efc2893fbf..2c702f6b46 100644
--- a/engines/nancy/action/secondaryvideo.cpp
+++ b/engines/nancy/action/secondaryvideo.cpp
@@ -45,14 +45,14 @@ void PlaySecondaryVideo::init() {
         _decoder.close();
     }
 
-    _decoder.loadFile(filename + ".avf");
+    _decoder.loadFile(_filename + ".avf");
     // Every secondary video frame (in nancy1) plays exactly 12ms slower than what its metadata says.
     // I'm still not sure how/why that happens so for now I'm using this hack to fix the timings
     _decoder.addFrameTime(12);
     _drawSurface.create(_decoder.getWidth(), _decoder.getHeight(), GraphicsManager::getInputPixelFormat());
 
-    if (paletteFilename.size()) {
-        GraphicsManager::loadSurfacePalette(_drawSurface, paletteFilename);
+    if (_paletteFilename.size()) {
+        GraphicsManager::loadSurfacePalette(_drawSurface, _paletteFilename);
     }
 
     setVisible(false);
@@ -71,16 +71,16 @@ void PlaySecondaryVideo::updateGraphics() {
             _decoder.start();
         }
         
-        switch (hoverState) {
+        switch (_hoverState) {
         case kNoHover:
             if (_isHovered) {
-                _decoder.seekToFrame(onHoverFirstFrame);
+                _decoder.seekToFrame(_onHoverFirstFrame);
                 
-                hoverState = kHover;
+                _hoverState = kHover;
             } else {
-                if (_decoder.getCurFrame() == loopLastFrame) {
+                if (_decoder.getCurFrame() == _loopLastFrame) {
                     // loop back to beginning
-                    _decoder.seekToFrame(loopFirstFrame);
+                    _decoder.seekToFrame(_loopFirstFrame);
                 }
 
                 break;
@@ -89,33 +89,33 @@ void PlaySecondaryVideo::updateGraphics() {
         case kHover:
             if (!_isHovered) {
                 // Stopped hovering, reverse playback
-                _decoder.seekToFrame(onHoverEndLastFrame);
+                _decoder.seekToFrame(_onHoverEndLastFrame);
                 _decoder.setRate(-_decoder.getRate());
                 if (!_decoder.isPlaying()) {
                     _decoder.start();
                 }
 
-                hoverState = kEndHover;
+                _hoverState = kEndHover;
             } else {
                 break;
             }
             // fall through
         case kEndHover:
-            if (_decoder.getCurFrame() == onHoverEndFirstFrame) {
-                // reversed playback has ended, go back to no hover state
-                _decoder.seekToFrame(loopFirstFrame);
+            if (_decoder.getCurFrame() == _onHoverEndFirstFrame) {
+                // reversed playback has ended, go back to no hover _state
+                _decoder.seekToFrame(_loopFirstFrame);
                 _decoder.setRate(-_decoder.getRate());
-                hoverState = kNoHover;
+                _hoverState = kNoHover;
             }
 
             break;
         }
 
         if (_decoder.needsUpdate() && !_screenPosition.isEmpty()) {
-            for (uint i = 0; i < videoDescs.size(); ++i) {
-                if ((uint16)videoDescs[i].frameID == _currentViewportFrame) {
+            for (uint i = 0; i < _videoDescs.size(); ++i) {
+                if ((uint16)_videoDescs[i].frameID == _currentViewportFrame) {
                     // This ignores the srcRects for every frame
-                    GraphicsManager::copyToManaged(*_decoder.decodeNextFrame(), _drawSurface, paletteFilename.size() > 0);
+                    GraphicsManager::copyToManaged(*_decoder.decodeNextFrame(), _drawSurface, _paletteFilename.size() > 0);
                     break;
                 }
             }
@@ -138,7 +138,7 @@ void PlaySecondaryVideo::onPause(bool pause) {
 }
 
 void PlaySecondaryVideo::handleInput(NancyInput &input) {
-    if (hasHotspot && NancySceneState.getViewport().convertViewportToScreen(hotspot).contains(input.mousePos)) {
+    if (_hasHotspot && NancySceneState.getViewport().convertViewportToScreen(_hotspot).contains(input.mousePos)) {
         _isHovered = true;
     } else {
         _isHovered = false;
@@ -148,25 +148,25 @@ void PlaySecondaryVideo::handleInput(NancyInput &input) {
 void PlaySecondaryVideo::readData(Common::SeekableReadStream &stream) {
     char buf[10];
     stream.read(buf, 10);
-    filename = buf;
+    _filename = buf;
     stream.read(buf, 10);
-    paletteFilename = buf;
+    _paletteFilename = buf;
     stream.skip(10);
     
-    if (paletteFilename.size()) {
+    if (_paletteFilename.size()) {
         stream.skip(14); // unknown data
     }
 
-    loopFirstFrame = stream.readUint16LE();
-    loopLastFrame = stream.readUint16LE();
-    onHoverFirstFrame = stream.readUint16LE();
-    onHoverLastFrame = stream.readUint16LE();
-    onHoverEndFirstFrame = stream.readUint16LE();
-    onHoverEndLastFrame = stream.readUint16LE();
+    _loopFirstFrame = stream.readUint16LE();
+    _loopLastFrame = stream.readUint16LE();
+    _onHoverFirstFrame = stream.readUint16LE();
+    _onHoverLastFrame = stream.readUint16LE();
+    _onHoverEndFirstFrame = stream.readUint16LE();
+    _onHoverEndLastFrame = stream.readUint16LE();
 
-    sceneChange.readData(stream);
+    _sceneChange.readData(stream);
 
-    if (paletteFilename.size()) {
+    if (_paletteFilename.size()) {
         stream.skip(3);
     } else {
         stream.skip(1);
@@ -174,17 +174,17 @@ void PlaySecondaryVideo::readData(Common::SeekableReadStream &stream) {
 
     uint16 numVideoDescs = stream.readUint16LE();
     for (uint i = 0; i < numVideoDescs; ++i) {
-        videoDescs.push_back(SecondaryVideoDescription());
-        videoDescs[i].readData(stream);
+        _videoDescs.push_back(SecondaryVideoDescription());
+        _videoDescs[i].readData(stream);
     }
 }
 
 void PlaySecondaryVideo::execute() {
-    switch (state) {
+    switch (_state) {
     case kBegin:
         init();
         registerGraphics();
-        state = kRun;
+        _state = kRun;
         // fall through
     case kRun: {
         // Set correct position according to viewport frame
@@ -193,24 +193,24 @@ void PlaySecondaryVideo::execute() {
 
             int activeFrame = -1;
 
-            for (uint i = 0; i < videoDescs.size(); ++i) {
-                if ((uint16)videoDescs[i].frameID == _currentViewportFrame) {
+            for (uint i = 0; i < _videoDescs.size(); ++i) {
+                if ((uint16)_videoDescs[i].frameID == _currentViewportFrame) {
                     activeFrame = i;
                 }
             }
 
             if (activeFrame != -1) {
                 // Make the drawing destination rectangle valid
-                _screenPosition = videoDescs[activeFrame].destRect;
+                _screenPosition = _videoDescs[activeFrame].destRect;
 
                 // Activate the hotspot
-                hotspot = videoDescs[activeFrame].destRect;
-                hasHotspot = true;
+                _hotspot = _videoDescs[activeFrame].destRect;
+                _hasHotspot = true;
                 _isPlaying = true;
                 setVisible(true);
             } else {
                 setVisible(false);
-                hasHotspot = false;
+                _hasHotspot = false;
                 _isPlaying = false;
             }
         }
@@ -219,7 +219,7 @@ void PlaySecondaryVideo::execute() {
     }
     case kActionTrigger:
         NancySceneState.pushScene();
-        NancySceneState.changeScene(sceneChange);
+        NancySceneState.changeScene(_sceneChange);
         finishExecution();
         break;
     }
diff --git a/engines/nancy/action/secondaryvideo.h b/engines/nancy/action/secondaryvideo.h
index 389485bdf5..106bdc98eb 100644
--- a/engines/nancy/action/secondaryvideo.h
+++ b/engines/nancy/action/secondaryvideo.h
@@ -52,17 +52,17 @@ public:
     virtual void readData(Common::SeekableReadStream &stream) override;
     virtual void execute() override;
 
-    Common::String filename;
-    Common::String paletteFilename;
-    uint16 loopFirstFrame = 0; // 0x1E
-    uint16 loopLastFrame = 0; // 0x20
-    uint16 onHoverFirstFrame = 0; // 0x22
-    uint16 onHoverLastFrame = 0; // 0x24
-    uint16 onHoverEndFirstFrame = 0; // 0x26
-    uint16 onHoverEndLastFrame = 0; // 0x28
-    SceneChangeDescription sceneChange; // 0x2A
+    Common::String _filename;
+    Common::String _paletteFilename;
+    uint16 _loopFirstFrame = 0; // 0x1E
+    uint16 _loopLastFrame = 0; // 0x20
+    uint16 _onHoverFirstFrame = 0; // 0x22
+    uint16 _onHoverLastFrame = 0; // 0x24
+    uint16 _onHoverEndFirstFrame = 0; // 0x26
+    uint16 _onHoverEndLastFrame = 0; // 0x28
+    SceneChangeDescription _sceneChange; // 0x2A
     // unknown byte
-    Common::Array<SecondaryVideoDescription> videoDescs; // 0x35
+    Common::Array<SecondaryVideoDescription> _videoDescs; // 0x35
 
 protected:
     virtual Common::String getRecordTypeName() const override { return Common::String::format("PlaySecondaryVideoChan%i", channel); }
@@ -70,7 +70,7 @@ protected:
     virtual uint16 getZOrder() const override { return 8; }
     virtual bool isViewportRelative() const override { return true; }
 
-    HoverState hoverState = kNoHover;
+    HoverState _hoverState = kNoHover;
     AVFDecoder _decoder;
     int _currentViewportFrame = -1;
     bool _isPlaying = false;
diff --git a/engines/nancy/action/sliderpuzzle.cpp b/engines/nancy/action/sliderpuzzle.cpp
index 7d7f012d5e..548cd29445 100644
--- a/engines/nancy/action/sliderpuzzle.cpp
+++ b/engines/nancy/action/sliderpuzzle.cpp
@@ -34,8 +34,8 @@
 namespace Nancy {
 namespace Action {
 
-Common::Array<Common::Array<int16>> SliderPuzzle::playerTileOrder = Common::Array<Common::Array<int16>>();
-bool SliderPuzzle::playerHasTriedPuzzle = false;
+Common::Array<Common::Array<int16>> SliderPuzzle::_playerTileOrder = Common::Array<Common::Array<int16>>();
+bool SliderPuzzle::_playerHasTriedPuzzle = false;
 
 void SliderPuzzle::init() {
     _drawSurface.create(_screenPosition.width(), _screenPosition.height(), GraphicsManager::getInputPixelFormat());
@@ -43,118 +43,118 @@ void SliderPuzzle::init() {
     
     setTransparent(true);
 
-    g_nancy->resource->loadImage(imageName, image);
+    g_nancy->_resource->loadImage(_imageName, _image);
 }
 
 void SliderPuzzle::readData(Common::SeekableReadStream &stream) {
     char buf[10];
     stream.read(buf, 10);
-    imageName = buf;
+    _imageName = buf;
 
-    width = stream.readUint16LE();
-    height = stream.readUint16LE();
+    _width = stream.readUint16LE();
+    _height = stream.readUint16LE();
 
-    for (uint y = 0; y < height; ++y) {
-        srcRects.push_back(Common::Array<Common::Rect>());
-        for (uint x = 0; x < width; ++x) {
-            srcRects.back().push_back(Common::Rect());
-            readRect(stream, srcRects.back().back());
+    for (uint y = 0; y < _height; ++y) {
+        _srcRects.push_back(Common::Array<Common::Rect>());
+        for (uint x = 0; x < _width; ++x) {
+            _srcRects.back().push_back(Common::Rect());
+            readRect(stream, _srcRects.back().back());
         }
-        stream.skip((6 - width) * 16);
+        stream.skip((6 - _width) * 16);
     }
 
-    stream.skip((6 - height) * 6 * 16);
+    stream.skip((6 - _height) * 6 * 16);
 
-    for (uint y = 0; y < height; ++y) {
-        destRects.push_back(Common::Array<Common::Rect>());
-        for (uint x = 0; x < width; ++x) {
-            destRects.back().push_back(Common::Rect());
-            readRect(stream, destRects.back().back());
+    for (uint y = 0; y < _height; ++y) {
+        _destRects.push_back(Common::Array<Common::Rect>());
+        for (uint x = 0; x < _width; ++x) {
+            _destRects.back().push_back(Common::Rect());
+            readRect(stream, _destRects.back().back());
 
             if (x == 0 && y == 0) {
-                _screenPosition = destRects.back().back();
+                _screenPosition = _destRects.back().back();
             } else {
-                _screenPosition.extend(destRects.back().back());
+                _screenPosition.extend(_destRects.back().back());
             }
         }
-        stream.skip((6 - width) * 16);
+        stream.skip((6 - _width) * 16);
     }
 
-    stream.skip((6 - height) * 6 * 16);
+    stream.skip((6 - _height) * 6 * 16);
 
-    for (uint y = 0; y < height; ++y) {
-        correctTileOrder.push_back(Common::Array<int16>());
-        for (uint x = 0; x < width; ++x) {
-            correctTileOrder.back().push_back(stream.readSint16LE());
+    for (uint y = 0; y < _height; ++y) {
+        _correctTileOrder.push_back(Common::Array<int16>());
+        for (uint x = 0; x < _width; ++x) {
+            _correctTileOrder.back().push_back(stream.readSint16LE());
         }
-        stream.skip((6 - width) * 2);
+        stream.skip((6 - _width) * 2);
     }
 
-    stream.skip((6 - height) * 6 * 2);
+    stream.skip((6 - _height) * 6 * 2);
 
-    clickSound.read(stream, SoundDescription::kNormal);
-    solveExitScene.readData(stream);
+    _clickSound.read(stream, SoundDescription::kNormal);
+    _solveExitScene.readData(stream);
     stream.skip(2);
-    flagOnSolve.label = stream.readSint16LE();
-    flagOnSolve.flag = (NancyFlag)stream.readByte();
-    solveSound.read(stream, SoundDescription::kNormal);
-    exitScene.readData(stream);
+    _flagOnSolve.label = stream.readSint16LE();
+    _flagOnSolve.flag = (NancyFlag)stream.readByte();
+    _solveSound.read(stream, SoundDescription::kNormal);
+    _exitScene.readData(stream);
     stream.skip(2);
-    flagOnExit.label = stream.readSint16LE();
-    flagOnExit.flag = (NancyFlag)stream.readByte();
-    readRect(stream, exitHotspot);
+    _flagOnExit.label = stream.readSint16LE();
+    _flagOnExit.flag = (NancyFlag)stream.readByte();
+    readRect(stream, _exitHotspot);
 }
 
 void SliderPuzzle::execute() {
-    switch (state) {
+    switch (_state) {
     case kBegin:
         init();
         registerGraphics();
-        if (!playerHasTriedPuzzle) {
+        if (!_playerHasTriedPuzzle) {
             Common::SeekableReadStream *spuz = g_nancy->getBootChunkStream("SPUZ");
-            playerTileOrder.clear();
+            _playerTileOrder.clear();
             spuz->seek(NancySceneState.getDifficulty() * 0x48);
-            for (uint y = 0; y < height; ++y) {
-                playerTileOrder.push_back(Common::Array<int16>());
+            for (uint y = 0; y < _height; ++y) {
+                _playerTileOrder.push_back(Common::Array<int16>());
 
-                for (uint x = 0; x < width; ++x) {
-                    playerTileOrder.back().push_back(spuz->readSint16LE());
+                for (uint x = 0; x < _width; ++x) {
+                    _playerTileOrder.back().push_back(spuz->readSint16LE());
                 }
 
-                spuz->skip((6 - width) * 2);
+                spuz->skip((6 - _width) * 2);
             }
 
-            playerHasTriedPuzzle = true;
+            _playerHasTriedPuzzle = true;
         }
 
-        for (uint y = 0; y < height; ++y) {
-            for (uint x = 0; x < width; ++x) {
-                drawTile(playerTileOrder[y][x], x, y);
+        for (uint y = 0; y < _height; ++y) {
+            for (uint x = 0; x < _width; ++x) {
+                drawTile(_playerTileOrder[y][x], x, y);
             }
         }
 
-        g_nancy->sound->loadSound(clickSound);
-        state = kRun;
+        g_nancy->_sound->loadSound(_clickSound);
+        _state = kRun;
         // fall through
     case kRun:
-        switch (solveState) {
+        switch (_solveState) {
         case kNotSolved:
-            for (uint y = 0; y < height; ++y) {
-                for (uint x = 0; x < width; ++x) {
-                    if (playerTileOrder[y][x] != correctTileOrder[y][x]) {
+            for (uint y = 0; y < _height; ++y) {
+                for (uint x = 0; x < _width; ++x) {
+                    if (_playerTileOrder[y][x] != _correctTileOrder[y][x]) {
                         return;
                     }
                 }
             }
 
-            g_nancy->sound->loadSound(solveSound);
-            g_nancy->sound->playSound(solveSound);
-            solveState = kWaitForSound;
+            g_nancy->_sound->loadSound(_solveSound);
+            g_nancy->_sound->playSound(_solveSound);
+            _solveState = kWaitForSound;
             break;
         case kWaitForSound:
-            if (!g_nancy->sound->isSoundPlaying(solveSound)) {
-                g_nancy->sound->stopSound(solveSound);
-                state = kActionTrigger;
+            if (!g_nancy->_sound->isSoundPlaying(_solveSound)) {
+                g_nancy->_sound->stopSound(_solveSound);
+                _state = kActionTrigger;
             }
 
             break;
@@ -162,33 +162,33 @@ void SliderPuzzle::execute() {
 
         break;
     case kActionTrigger:
-        switch (solveState) {
+        switch (_solveState) {
         case kNotSolved:
-            NancySceneState.changeScene(exitScene);
-            NancySceneState.setEventFlag(flagOnExit);
+            NancySceneState.changeScene(_exitScene);
+            NancySceneState.setEventFlag(_flagOnExit);
             break;
         case kWaitForSound:
-            NancySceneState.changeScene(solveExitScene);
-            NancySceneState.setEventFlag(flagOnSolve);
-            playerHasTriedPuzzle = false;
+            NancySceneState.changeScene(_solveExitScene);
+            NancySceneState.setEventFlag(_flagOnSolve);
+            _playerHasTriedPuzzle = false;
             break;
         }
 
-        g_nancy->sound->stopSound(clickSound);
+        g_nancy->_sound->stopSound(_clickSound);
         finishExecution();
     }
 }
 
 void SliderPuzzle::handleInput(NancyInput &input) {
-    if (solveState != kNotSolved) {
+    if (_solveState != kNotSolved) {
         return;
     }
 
-    if (NancySceneState.getViewport().convertViewportToScreen(exitHotspot).contains(input.mousePos)) {
-        g_nancy->cursorManager->setCursorType(CursorManager::kExitArrow);
+    if (NancySceneState.getViewport().convertViewportToScreen(_exitHotspot).contains(input.mousePos)) {
+        g_nancy->_cursorManager->setCursorType(CursorManager::kExitArrow);
 
         if (input.input & NancyInput::kLeftMouseButtonUp) {
-            state = kActionTrigger;
+            _state = kActionTrigger;
         }
         
         return;
@@ -197,35 +197,35 @@ void SliderPuzzle::handleInput(NancyInput &input) {
     int currentTileX = -1;
     int currentTileY = -1;
     uint direction = 0;
-    for (uint y = 0; y < height; ++y) {
+    for (uint y = 0; y < _height; ++y) {
         bool shouldBreak = false;
-        for (uint x = 0; x < width; ++x) {
-            if (x > 0 && playerTileOrder[y][x - 1] < 0) {
-                if (NancySceneState.getViewport().convertViewportToScreen(destRects[y][x]).contains(input.mousePos)) {
+        for (uint x = 0; x < _width; ++x) {
+            if (x > 0 && _playerTileOrder[y][x - 1] < 0) {
+                if (NancySceneState.getViewport().convertViewportToScreen(_destRects[y][x]).contains(input.mousePos)) {
                     currentTileX = x;
                     currentTileY = y;
                     direction = kLeft;
                     shouldBreak = true;
                     break;
                 }
-            } else if ((int)x < width - 1 && playerTileOrder[y][x + 1] < 0) {
-                if (NancySceneState.getViewport().convertViewportToScreen(destRects[y][x]).contains(input.mousePos)) {
+            } else if ((int)x < _width - 1 && _playerTileOrder[y][x + 1] < 0) {
+                if (NancySceneState.getViewport().convertViewportToScreen(_destRects[y][x]).contains(input.mousePos)) {
                     currentTileX = x;
                     currentTileY = y;
                     direction = kRight;
                     shouldBreak = true;
                     break;
                 }
-            } else if (y > 0 && playerTileOrder[y - 1][x] < 0) {
-                if (NancySceneState.getViewport().convertViewportToScreen(destRects[y][x]).contains(input.mousePos)) {
+            } else if (y > 0 && _playerTileOrder[y - 1][x] < 0) {
+                if (NancySceneState.getViewport().convertViewportToScreen(_destRects[y][x]).contains(input.mousePos)) {
                     currentTileX = x;
                     currentTileY = y;
                     direction = kUp;
                     shouldBreak = true;
                     break;
                 }
-            } else if ((int)y < height - 1 && playerTileOrder[y + 1][x] < 0) {
-                if (NancySceneState.getViewport().convertViewportToScreen(destRects[y][x]).contains(input.mousePos)) {
+            } else if ((int)y < _height - 1 && _playerTileOrder[y + 1][x] < 0) {
+                if (NancySceneState.getViewport().convertViewportToScreen(_destRects[y][x]).contains(input.mousePos)) {
                     currentTileX = x;
                     currentTileY = y;
                     direction = kDown;
@@ -241,41 +241,41 @@ void SliderPuzzle::handleInput(NancyInput &input) {
     }
 
     if (currentTileX != -1) {
-        g_nancy->cursorManager->setCursorType(CursorManager::kHotspot);
+        g_nancy->_cursorManager->setCursorType(CursorManager::kHotspot);
 
         if (input.input & NancyInput::kLeftMouseButtonUp) {
-            g_nancy->sound->playSound(clickSound);
+            g_nancy->_sound->playSound(_clickSound);
             switch (direction) {
             case kUp: {
-                uint curTileID = playerTileOrder[currentTileY][currentTileX];
+                uint curTileID = _playerTileOrder[currentTileY][currentTileX];
                 drawTile(curTileID, currentTileX, currentTileY - 1);
                 undrawTile(currentTileX, currentTileY);
-                playerTileOrder[currentTileY - 1][currentTileX] = curTileID;
-                playerTileOrder[currentTileY][currentTileX] = -10;
+                _playerTileOrder[currentTileY - 1][currentTileX] = curTileID;
+                _playerTileOrder[currentTileY][currentTileX] = -10;
                 break;
             }
             case kDown: {
-                uint curTileID = playerTileOrder[currentTileY][currentTileX];
+                uint curTileID = _playerTileOrder[currentTileY][currentTileX];
                 drawTile(curTileID, currentTileX, currentTileY + 1);
                 undrawTile(currentTileX, currentTileY);
-                playerTileOrder[currentTileY + 1][currentTileX] = curTileID;
-                playerTileOrder[currentTileY][currentTileX] = -10;
+                _playerTileOrder[currentTileY + 1][currentTileX] = curTileID;
+                _playerTileOrder[currentTileY][currentTileX] = -10;
                 break;
             }
             case kLeft: {
-                uint curTileID = playerTileOrder[currentTileY][currentTileX];
+                uint curTileID = _playerTileOrder[currentTileY][currentTileX];
                 drawTile(curTileID, currentTileX - 1, currentTileY);
                 undrawTile(currentTileX, currentTileY);
-                playerTileOrder[currentTileY][currentTileX - 1] = curTileID;
-                playerTileOrder[currentTileY][currentTileX] = -10;
+                _playerTileOrder[currentTileY][currentTileX - 1] = curTileID;
+                _playerTileOrder[currentTileY][currentTileX] = -10;
                 break;
             }
             case kRight: {
-                uint curTileID = playerTileOrder[currentTileY][currentTileX];
+                uint curTileID = _playerTileOrder[currentTileY][currentTileX];
                 drawTile(curTileID, currentTileX + 1, currentTileY);
                 undrawTile(currentTileX, currentTileY);
-                playerTileOrder[currentTileY][currentTileX + 1] = curTileID;
-                playerTileOrder[currentTileY][currentTileX] = -10;
+                _playerTileOrder[currentTileY][currentTileX + 1] = curTileID;
+                _playerTileOrder[currentTileY][currentTileX] = -10;
                 break;
             }
             }
@@ -290,25 +290,25 @@ void SliderPuzzle::onPause(bool pause) {
 }
 
 void SliderPuzzle::synchronize(Common::Serializer &ser) {
-    ser.syncAsByte(playerHasTriedPuzzle);
+    ser.syncAsByte(_playerHasTriedPuzzle);
 
     byte x, y;
 
     if (ser.isSaving()) {
-        y = playerTileOrder.size();
+        y = _playerTileOrder.size();
         if (y) {
-            x = playerTileOrder.back().size();
+            x = _playerTileOrder.back().size();
         }
     }
 
     ser.syncAsByte(x);
     ser.syncAsByte(y);
 
-    playerTileOrder.resize(y);
+    _playerTileOrder.resize(y);
 
     for (int i = 0; i < y; ++i) {
-        playerTileOrder[i].resize(x);
-        ser.syncArray(playerTileOrder[i].data(), x, Common::Serializer::Sint16LE);
+        _playerTileOrder[i].resize(x);
+        ser.syncArray(_playerTileOrder[i].data(), x, Common::Serializer::Sint16LE);
     }
 }
 
@@ -318,14 +318,14 @@ void SliderPuzzle::drawTile(int tileID, uint posX, uint posY) {
         return;
     }
 
-    Common::Point destPoint(destRects[posY][posX].left - _screenPosition.left, destRects[posY][posX].top - _screenPosition.top);
-    _drawSurface.blitFrom(image, srcRects[tileID / height][tileID % width], destPoint);
+    Common::Point destPoint(_destRects[posY][posX].left - _screenPosition.left, _destRects[posY][posX].top - _screenPosition.top);
+    _drawSurface.blitFrom(_image, _srcRects[tileID / _height][tileID % _width], destPoint);
 
     _needsRedraw = true;
 }
 
 void SliderPuzzle::undrawTile(uint posX, uint posY) {
-    Common::Rect bounds = destRects[posY][posX];
+    Common::Rect bounds = _destRects[posY][posX];
     bounds.translate(-_screenPosition.left, -_screenPosition.top);
     _drawSurface.fillRect(bounds, GraphicsManager::getTransColor());
 
diff --git a/engines/nancy/action/sliderpuzzle.h b/engines/nancy/action/sliderpuzzle.h
index b33287eed6..1ffe0b0163 100644
--- a/engines/nancy/action/sliderpuzzle.h
+++ b/engines/nancy/action/sliderpuzzle.h
@@ -56,25 +56,25 @@ public:
 
     static void synchronize(Common::Serializer &ser);
 
-    Common::String imageName; // 0x00
-    uint16 width; // 0xA
-    uint16 height; // 0xC
-    Common::Array<Common::Array<Common::Rect>> srcRects; // 0x0E, size 0x240
-    Common::Array<Common::Array<Common::Rect>> destRects; // 0x24E, size 0x240
-    Common::Array<Common::Array<int16>> correctTileOrder; // 0x48E, size 0x48
-    SoundDescription clickSound; // 0x4D6
-    SceneChangeDescription solveExitScene; // 0x4F8
-    EventFlagDescription flagOnSolve; // 0x502
-    SoundDescription solveSound; // 0x505
-    SceneChangeDescription exitScene; // 0x527
-    EventFlagDescription flagOnExit; // 0x531
-    Common::Rect exitHotspot; // 0x534
-
-    SolveState solveState = kNotSolved;
-    Graphics::ManagedSurface image;
-
-    static Common::Array<Common::Array<int16>> playerTileOrder;
-    static bool playerHasTriedPuzzle;
+    Common::String _imageName; // 0x00
+    uint16 _width; // 0xA
+    uint16 _height; // 0xC
+    Common::Array<Common::Array<Common::Rect>> _srcRects; // 0x0E, size 0x240
+    Common::Array<Common::Array<Common::Rect>> _destRects; // 0x24E, size 0x240
+    Common::Array<Common::Array<int16>> _correctTileOrder; // 0x48E, size 0x48
+    SoundDescription _clickSound; // 0x4D6
+    SceneChangeDescription _solveExitScene; // 0x4F8
+    EventFlagDescription _flagOnSolve; // 0x502
+    SoundDescription _solveSound; // 0x505
+    SceneChangeDescription _exitScene; // 0x527
+    EventFlagDescription _flagOnExit; // 0x531
+    Common::Rect _exitHotspot; // 0x534
+
+    SolveState _solveState = kNotSolved;
+    Graphics::ManagedSurface _image;
+
+    static Common::Array<Common::Array<int16>> _playerTileOrder;
+    static bool _playerHasTriedPuzzle;
 
 protected:
     virtual Common::String getRecordTypeName() const override { return "SliderPuzzle"; }
diff --git a/engines/nancy/action/staticbitmapanim.cpp b/engines/nancy/action/staticbitmapanim.cpp
index 6ff2cd3ea8..1c4ba93446 100644
--- a/engines/nancy/action/staticbitmapanim.cpp
+++ b/engines/nancy/action/staticbitmapanim.cpp
@@ -35,7 +35,7 @@ namespace Nancy {
 namespace Action {
 
 void PlayStaticBitmapAnimation::init() {
-    g_nancy->resource->loadImage(imageName, _fullSurface);
+    g_nancy->_resource->loadImage(_imageName, _fullSurface);
 
     setFrame(0);
 
@@ -45,40 +45,40 @@ void PlayStaticBitmapAnimation::init() {
 void PlayStaticBitmapAnimation::readData(Common::SeekableReadStream &stream) {
     char name[10];
     stream.read(name, 10);
-    imageName = Common::String(name);
+    _imageName = Common::String(name);
 
     stream.skip(0x2);
-    isTransparent = (NancyFlag)(stream.readUint16LE());
-    doNotChangeScene = (NancyFlag)(stream.readUint16LE());
-    isReverse = (NancyFlag)(stream.readUint16LE());
-    isLooping = (NancyFlag)(stream.readUint16LE());
-    firstFrame = stream.readUint16LE();
-    loopFirstFrame = stream.readUint16LE();
-    loopLastFrame = stream.readUint16LE();
-    frameTime = Common::Rational(1000, stream.readUint16LE()).toInt();
-    zOrder = stream.readUint16LE();
-
-    if (isInterruptible) {
-        interruptCondition.label = stream.readSint16LE();
-        interruptCondition.flag = (NancyFlag)stream.readUint16LE();
+    _isTransparent = (NancyFlag)(stream.readUint16LE());
+    _doNotChangeScene = (NancyFlag)(stream.readUint16LE());
+    _isReverse = (NancyFlag)(stream.readUint16LE());
+    _isLooping = (NancyFlag)(stream.readUint16LE());
+    _firstFrame = stream.readUint16LE();
+    _loopFirstFrame = stream.readUint16LE();
+    _loopLastFrame = stream.readUint16LE();
+    _frameTime = Common::Rational(1000, stream.readUint16LE()).toInt();
+    _zOrder = stream.readUint16LE();
+
+    if (_isInterruptible) {
+        _interruptCondition.label = stream.readSint16LE();
+        _interruptCondition.flag = (NancyFlag)stream.readUint16LE();
     } else {
-        interruptCondition.label = -1;
-        interruptCondition.flag = kFalse;
+        _interruptCondition.label = -1;
+        _interruptCondition.flag = kFalse;
     }
 
-    sceneChange.readData(stream);
-    triggerFlags.readData(stream);
-    sound.read(stream, SoundDescription::kNormal);
+    _sceneChange.readData(stream);
+    _triggerFlags.readData(stream);
+    _sound.read(stream, SoundDescription::kNormal);
     uint numViewportFrames = stream.readUint16LE();
 
-    for (uint i = firstFrame; i <= loopLastFrame; ++i) {
-        srcRects.push_back(Common::Rect());
-        readRect(stream, srcRects[i]);
+    for (uint i = _firstFrame; i <= _loopLastFrame; ++i) {
+        _srcRects.push_back(Common::Rect());
+        readRect(stream, _srcRects[i]);
     }
 
     for (uint i = 0; i < numViewportFrames; ++i) {
-        bitmaps.push_back(BitmapDescription());
-        BitmapDescription &rects = bitmaps.back();
+        _bitmaps.push_back(BitmapDescription());
+        BitmapDescription &rects = _bitmaps.back();
         rects.frameID = stream.readUint16LE();
         readRect(stream, rects.src);
         readRect(stream, rects.dest);
@@ -86,58 +86,58 @@ void PlayStaticBitmapAnimation::readData(Common::SeekableReadStream &stream) {
 }
 
 void PlayStaticBitmapAnimation::execute() {
-    uint32 currentFrameTime = g_nancy->getTotalPlayTime();
-    switch (state) {
+    uint32 _currentFrameTime = g_nancy->getTotalPlayTime();
+    switch (_state) {
     case kBegin:
         init();
         registerGraphics();
-        g_nancy->sound->loadSound(sound);
-        g_nancy->sound->playSound(sound);
-        state = kRun;
+        g_nancy->_sound->loadSound(_sound);
+        g_nancy->_sound->playSound(_sound);
+        _state = kRun;
         // fall through
     case kRun: {
         // Check the timer to see if we need to draw the next animation frame
-        if (nextFrameTime <= currentFrameTime) {
+        if (_nextFrameTime <= _currentFrameTime) {
             // World's worst if statement
-            if (NancySceneState.getEventFlag(interruptCondition) ||
-                (   (((currentFrame == loopLastFrame) && (isReverse == kFalse) && (isLooping == kFalse)) ||
-                    ((currentFrame == loopFirstFrame) && (isReverse == kTrue) && (isLooping == kFalse))) &&
-                        !g_nancy->sound->isSoundPlaying(sound))   ) {
+            if (NancySceneState.getEventFlag(_interruptCondition) ||
+                (   (((_currentFrame == _loopLastFrame) && (_isReverse == kFalse) && (_isLooping == kFalse)) ||
+                    ((_currentFrame == _loopFirstFrame) && (_isReverse == kTrue) && (_isLooping == kFalse))) &&
+                        !g_nancy->_sound->isSoundPlaying(_sound))   ) {
                 
-                state = kActionTrigger;
+                _state = kActionTrigger;
 
                 // Not sure if hiding when triggered is a hack or the intended behavior, but it's here to fix
                 // nancy1's safe lock light not turning off.
                 setVisible(false);
     
-                if (!g_nancy->sound->isSoundPlaying(sound)) {
-                    g_nancy->sound->stopSound(sound);
+                if (!g_nancy->_sound->isSoundPlaying(_sound)) {
+                    g_nancy->_sound->stopSound(_sound);
                 }
             } else {
                 // Check if we've moved the viewport
                 uint16 newFrame = NancySceneState.getSceneInfo().frameID;
 
-                if (currentViewportFrame != newFrame) {
-                    currentViewportFrame = newFrame;
+                if (_currentViewportFrame != newFrame) {
+                    _currentViewportFrame = newFrame;
 
-                    for (uint i = 0; i < bitmaps.size(); ++i) {
-                        if (currentViewportFrame == bitmaps[i].frameID) {
-                            _screenPosition = bitmaps[i].dest;
+                    for (uint i = 0; i < _bitmaps.size(); ++i) {
+                        if (_currentViewportFrame == _bitmaps[i].frameID) {
+                            _screenPosition = _bitmaps[i].dest;
                             break;
                         }
                     }
                 }
                 
-                nextFrameTime = currentFrameTime + frameTime;
-                setFrame(currentFrame);
+                _nextFrameTime = _currentFrameTime + _frameTime;
+                setFrame(_currentFrame);
 
-                if (isReverse == kTrue) {
-                    --currentFrame;
-                    currentFrame = currentFrame < loopFirstFrame ? loopLastFrame : currentFrame;
+                if (_isReverse == kTrue) {
+                    --_currentFrame;
+                    _currentFrame = _currentFrame < _loopFirstFrame ? _loopLastFrame : _currentFrame;
                     return;
                 } else {
-                    ++currentFrame;
-                    currentFrame = currentFrame > loopLastFrame ? loopFirstFrame : currentFrame;
+                    ++_currentFrame;
+                    _currentFrame = _currentFrame > _loopLastFrame ? _loopFirstFrame : _currentFrame;
                     return;
                 }
             }                
@@ -145,12 +145,12 @@ void PlayStaticBitmapAnimation::execute() {
             // Check if we've moved the viewport
             uint16 newFrame = NancySceneState.getSceneInfo().frameID;
 
-            if (currentViewportFrame != newFrame) {
-                currentViewportFrame = newFrame;
+            if (_currentViewportFrame != newFrame) {
+                _currentViewportFrame = newFrame;
                 
-                for (uint i = 0; i < bitmaps.size(); ++i) {
-                    if (currentViewportFrame == bitmaps[i].frameID) {
-                        _screenPosition = bitmaps[i].dest;
+                for (uint i = 0; i < _bitmaps.size(); ++i) {
+                    if (_currentViewportFrame == _bitmaps[i].frameID) {
+                        _screenPosition = _bitmaps[i].dest;
                         break;
                     }
                 }
@@ -160,9 +160,9 @@ void PlayStaticBitmapAnimation::execute() {
         break;
     }
     case kActionTrigger:
-        triggerFlags.execute();
-        if (doNotChangeScene == kFalse) {
-            NancySceneState.changeScene(sceneChange);
+        _triggerFlags.execute();
+        if (_doNotChangeScene == kFalse) {
+            NancySceneState.changeScene(_sceneChange);
             finishExecution();
         }
         break;
@@ -176,10 +176,10 @@ void PlayStaticBitmapAnimation::onPause(bool pause) {
 }
 
 void PlayStaticBitmapAnimation::setFrame(uint frame) {
-    currentFrame = frame;
-    _drawSurface.create(_fullSurface, srcRects[frame]);
+    _currentFrame = frame;
+    _drawSurface.create(_fullSurface, _srcRects[frame]);
     
-    setTransparent(isTransparent == kTrue);
+    setTransparent(_isTransparent == kTrue);
 
     _needsRedraw = true;
 }
diff --git a/engines/nancy/action/staticbitmapanim.h b/engines/nancy/action/staticbitmapanim.h
index cc702edf26..66b4eac776 100644
--- a/engines/nancy/action/staticbitmapanim.h
+++ b/engines/nancy/action/staticbitmapanim.h
@@ -40,7 +40,7 @@ namespace Action {
 // action record types, whose functionality is nearly identical
 class PlayStaticBitmapAnimation : public ActionRecord, public RenderObject {
 public:
-    PlayStaticBitmapAnimation(bool interruptible, RenderObject &redrawFrom) : RenderObject(redrawFrom), isInterruptible(interruptible) {}
+    PlayStaticBitmapAnimation(bool interruptible, RenderObject &redrawFrom) : RenderObject(redrawFrom), _isInterruptible(interruptible) {}
     virtual ~PlayStaticBitmapAnimation() { _fullSurface.free(); }
 
     virtual void init() override;
@@ -49,38 +49,38 @@ public:
     virtual void execute() override;
     virtual void onPause(bool pause) override;
 
-    Common::String imageName;
+    Common::String _imageName;
 
-    NancyFlag isTransparent; // 0xC
-    NancyFlag doNotChangeScene; // 0xE
-    NancyFlag isReverse; // 0x10
-    NancyFlag isLooping; // 0x12
-    uint16 firstFrame; // 0x14
-    uint16 loopFirstFrame; // 0x16
-    uint16 loopLastFrame; // 0x18
-    Time frameTime;
-    uint16 zOrder; // 0x1C
-    EventFlagDescription interruptCondition; // 0x1E
-    SceneChangeDescription sceneChange;
-    MultiEventFlagDescription triggerFlags; // 0x2A
+    NancyFlag _isTransparent; // 0xC
+    NancyFlag _doNotChangeScene; // 0xE
+    NancyFlag _isReverse; // 0x10
+    NancyFlag _isLooping; // 0x12
+    uint16 _firstFrame; // 0x14
+    uint16 _loopFirstFrame; // 0x16
+    uint16 _loopLastFrame; // 0x18
+    Time _frameTime;
+    uint16 _zOrder; // 0x1C
+    EventFlagDescription _interruptCondition; // 0x1E
+    SceneChangeDescription _sceneChange;
+    MultiEventFlagDescription _triggerFlags; // 0x2A
 
-    Nancy::SoundDescription sound; // 0x52
+    Nancy::SoundDescription _sound; // 0x52
 
     // Describes a single frame in this animation
-    Common::Array<Common::Rect> srcRects;
+    Common::Array<Common::Rect> _srcRects;
     // Describes how the animation will be displayed on a single
     // frame of the viewport
-    Common::Array<BitmapDescription> bitmaps;
+    Common::Array<BitmapDescription> _bitmaps;
 
-    int16 currentFrame = -1;
-    int16 currentViewportFrame = -1;
-    Time nextFrameTime;
-    bool isInterruptible;
+    int16 _currentFrame = -1;
+    int16 _currentViewportFrame = -1;
+    Time _nextFrameTime;
+    bool _isInterruptible;
     
 protected:
-    virtual Common::String getRecordTypeName() const override { return isInterruptible ? "PlayIntStaticBitmapAnimation" : "PlayStaticBitmapAnimation"; }
+    virtual Common::String getRecordTypeName() const override { return _isInterruptible ? "PlayIntStaticBitmapAnimation" : "PlayStaticBitmapAnimation"; }
 
-    virtual uint16 getZOrder() const override { return zOrder; }
+    virtual uint16 getZOrder() const override { return _zOrder; }
     virtual bool isViewportRelative() const override { return true; }
 
     void setFrame(uint frame);
diff --git a/engines/nancy/action/telephone.cpp b/engines/nancy/action/telephone.cpp
index cb17be5f27..dafb17e299 100644
--- a/engines/nancy/action/telephone.cpp
+++ b/engines/nancy/action/telephone.cpp
@@ -42,7 +42,7 @@ void Telephone::init() {
     
     setTransparent(true);
 
-    g_nancy->resource->loadImage(imageName, image);
+    g_nancy->_resource->loadImage(_imageName, _image);
 
     NancySceneState.setShouldClearTextbox(false);
 }
@@ -50,56 +50,56 @@ void Telephone::init() {
 void Telephone::readData(Common::SeekableReadStream &stream) {
     char buf[10];
     stream.read(buf, 10);
-    imageName = buf;
+    _imageName = buf;
 
     for (uint i = 0; i < 12; ++i) {
-        srcRects.push_back(Common::Rect());
-        readRect(stream, srcRects.back());
+        _srcRects.push_back(Common::Rect());
+        readRect(stream, _srcRects.back());
     }
 
     for (uint i = 0; i < 12; ++i) {
-        destRects.push_back(Common::Rect());
-        readRect(stream, destRects.back());
+        _destRects.push_back(Common::Rect());
+        readRect(stream, _destRects.back());
 
         if (i == 0) {
-            _screenPosition = destRects.back();
+            _screenPosition = _destRects.back();
         } else {
-            _screenPosition.extend(destRects.back());
+            _screenPosition.extend(_destRects.back());
         }
     }
 
-    genericDialogueSound.read(stream, SoundDescription::kNormal);
-    genericButtonSound.read(stream, SoundDescription::kNormal);
-    ringSound.read(stream, SoundDescription::kNormal);
-    dialToneSound.read(stream, SoundDescription::kNormal);
-    dialAgainSound.read(stream, SoundDescription::kNormal);
-    hangUpSound.read(stream, SoundDescription::kNormal);
+    _genericDialogueSound.read(stream, SoundDescription::kNormal);
+    _genericButtonSound.read(stream, SoundDescription::kNormal);
+    _ringSound.read(stream, SoundDescription::kNormal);
+    _dialToneSound.read(stream, SoundDescription::kNormal);
+    _dialAgainSound.read(stream, SoundDescription::kNormal);
+    _hangUpSound.read(stream, SoundDescription::kNormal);
 
     for (uint i = 0; i < 12; ++i) {
         stream.read(buf, 10);
-        buttonSoundNames.push_back(buf);
+        _buttonSoundNames.push_back(buf);
     }
 
     char buf2[200];
     stream.read(buf2, 200);
-    addressBookString = buf2;
+    _addressBookString = buf2;
     stream.read(buf2, 200);
-    dialAgainString = buf2;
-    reloadScene.readData(stream);
+    _dialAgainString = buf2;
+    _reloadScene.readData(stream);
     stream.skip(2);
-    flagOnReload.label = stream.readSint16LE();
-    flagOnReload.flag = (NancyFlag)stream.readUint16LE();
-    exitScene.readData(stream);
+    _flagOnReload.label = stream.readSint16LE();
+    _flagOnReload.flag = (NancyFlag)stream.readUint16LE();
+    _exitScene.readData(stream);
     stream.skip(2);
-    flagOnExit.label = stream.readSint16LE();
-    flagOnExit.flag = (NancyFlag)stream.readUint16LE();
-    readRect(stream, exitHotspot);
+    _flagOnExit.label = stream.readSint16LE();
+    _flagOnExit.flag = (NancyFlag)stream.readUint16LE();
+    readRect(stream, _exitHotspot);
 
     uint numCalls = stream.readUint16LE();
 
     for (uint i = 0; i < numCalls; ++i) {
-        calls.push_back(PhoneCall());
-        PhoneCall &call = calls.back();
+        _calls.push_back(PhoneCall());
+        PhoneCall &call = _calls.back();
 
         for (uint j = 0; j < 11; ++j) {
             call.phoneNumber.push_back(stream.readByte());
@@ -109,7 +109,7 @@ void Telephone::readData(Common::SeekableReadStream &stream) {
         call.soundName = buf;
         stream.read(buf2, 200);
         call.text = buf2;
-        call.sceneChange.readData(stream);
+        call._sceneChange.readData(stream);
         stream.skip(2);
         call.flag.label = stream.readSint16LE();
         call.flag.flag = (NancyFlag)stream.readUint16LE();
@@ -117,47 +117,47 @@ void Telephone::readData(Common::SeekableReadStream &stream) {
 }
 
 void Telephone::execute() {
-    switch (state) {
+    switch (_state) {
     case kBegin:
         init();
         registerGraphics();
-        g_nancy->sound->loadSound(dialToneSound);
-        g_nancy->sound->playSound(dialToneSound);
+        g_nancy->_sound->loadSound(_dialToneSound);
+        g_nancy->_sound->playSound(_dialToneSound);
         NancySceneState.getTextbox().clear();
-        NancySceneState.getTextbox().addTextLine(addressBookString);
-        state = kRun;
+        NancySceneState.getTextbox().addTextLine(_addressBookString);
+        _state = kRun;
         // fall through
     case kRun:
-        switch (callState) {
+        switch (_callState) {
         case kWaiting:
             // Long phone numbers start with 1
-            if (calledNumber.size() >= 11 || (calledNumber.size() >= 7 && (calledNumber[0] != 1))) {
+            if (_calledNumber.size() >= 11 || (_calledNumber.size() >= 7 && (_calledNumber[0] != 1))) {
                 NancySceneState.getTextbox().clear();
                 NancySceneState.getTextbox().addTextLine("ringing...<n><e>"); // Hardcoded in the original engine
-                g_nancy->sound->loadSound(ringSound);
-                g_nancy->sound->playSound(ringSound);
-                callState = kRinging;
+                g_nancy->_sound->loadSound(_ringSound);
+                g_nancy->_sound->playSound(_ringSound);
+                _callState = kRinging;
             }
 
             break;
         case kButtonPress:
-            if (!g_nancy->sound->isSoundPlaying(genericButtonSound)) {
-                g_nancy->sound->stopSound(genericButtonSound);
-                undrawButton(selected);
-                callState = kWaiting;
+            if (!g_nancy->_sound->isSoundPlaying(_genericButtonSound)) {
+                g_nancy->_sound->stopSound(_genericButtonSound);
+                undrawButton(_selected);
+                _callState = kWaiting;
             }
 
             break;
         case kRinging:
-            if (!g_nancy->sound->isSoundPlaying(ringSound)) {
-                g_nancy->sound->stopSound(ringSound);
-                uint numberLength = calledNumber[0] == 1 ? 11 : 7;
+            if (!g_nancy->_sound->isSoundPlaying(_ringSound)) {
+                g_nancy->_sound->stopSound(_ringSound);
+                uint numberLength = _calledNumber[0] == 1 ? 11 : 7;
 
-                for (uint i = 0; i < calls.size(); ++i) {
+                for (uint i = 0; i < _calls.size(); ++i) {
                     bool invalid = false;
 
                     for (uint j = 0; j < numberLength; ++j) {
-                        if (calledNumber[j] != calls[i].phoneNumber[j]) {
+                        if (_calledNumber[j] != _calls[i].phoneNumber[j]) {
                             // Invalid number, move onto next
                             invalid = true;
                             break;
@@ -169,48 +169,48 @@ void Telephone::execute() {
                     }
 
                     NancySceneState.getTextbox().clear();
-                    NancySceneState.getTextbox().addTextLine(calls[i].text);
+                    NancySceneState.getTextbox().addTextLine(_calls[i].text);
 
-                    genericDialogueSound.name = calls[i].soundName;
-                    g_nancy->sound->loadSound(genericDialogueSound);
-                    g_nancy->sound->playSound(genericDialogueSound);
-                    selected = i;
-                    callState = kCall;
+                    _genericDialogueSound.name = _calls[i].soundName;
+                    g_nancy->_sound->loadSound(_genericDialogueSound);
+                    g_nancy->_sound->playSound(_genericDialogueSound);
+                    _selected = i;
+                    _callState = kCall;
 
                     return;
                 }
                 
                 NancySceneState.getTextbox().clear();
-                NancySceneState.getTextbox().addTextLine(dialAgainString);
+                NancySceneState.getTextbox().addTextLine(_dialAgainString);
 
-                g_nancy->sound->loadSound(dialAgainSound);
-                g_nancy->sound->playSound(dialAgainSound);
-                callState = kBadNumber;
+                g_nancy->_sound->loadSound(_dialAgainSound);
+                g_nancy->_sound->playSound(_dialAgainSound);
+                _callState = kBadNumber;
                 return;
             }
 
             break;
         case kBadNumber:
-            if (!g_nancy->sound->isSoundPlaying(dialAgainSound)) {
-                g_nancy->sound->stopSound(dialAgainSound);
+            if (!g_nancy->_sound->isSoundPlaying(_dialAgainSound)) {
+                g_nancy->_sound->stopSound(_dialAgainSound);
 
-                state = kActionTrigger;
+                _state = kActionTrigger;
             }
 
             break;
         case kCall:
-            if (!g_nancy->sound->isSoundPlaying(genericDialogueSound)) {
-                g_nancy->sound->stopSound(genericDialogueSound);
+            if (!g_nancy->_sound->isSoundPlaying(_genericDialogueSound)) {
+                g_nancy->_sound->stopSound(_genericDialogueSound);
 
-                state = kActionTrigger;
+                _state = kActionTrigger;
             }
 
             break;
         case kHangUp:
-            if (!g_nancy->sound->isSoundPlaying(hangUpSound)) {
-                g_nancy->sound->stopSound(hangUpSound);
+            if (!g_nancy->_sound->isSoundPlaying(_hangUpSound)) {
+                g_nancy->_sound->stopSound(_hangUpSound);
 
-                state = kActionTrigger;
+                _state = kActionTrigger;
             }
 
             break;
@@ -218,25 +218,25 @@ void Telephone::execute() {
 
         break;
     case kActionTrigger:
-        switch (callState) {
+        switch (_callState) {
         case kBadNumber:
-            NancySceneState.changeScene(reloadScene);
-            calledNumber.clear();
-            NancySceneState.setEventFlag(flagOnReload);
-            state = kRun;
-            callState = kWaiting;
+            NancySceneState.changeScene(_reloadScene);
+            _calledNumber.clear();
+            NancySceneState.setEventFlag(_flagOnReload);
+            _state = kRun;
+            _callState = kWaiting;
 
             break;
         case kCall: {
-            PhoneCall &call = calls[selected];
-            NancySceneState.changeScene(call.sceneChange);
+            PhoneCall &call = _calls[_selected];
+            NancySceneState.changeScene(call._sceneChange);
             NancySceneState.setEventFlag(call.flag);
 
             break;
         }
         case kHangUp:
-            NancySceneState.changeScene(exitScene);
-            NancySceneState.setEventFlag(flagOnExit);
+            NancySceneState.changeScene(_exitScene);
+            NancySceneState.setEventFlag(_flagOnExit);
             
             break;
         default:
@@ -253,25 +253,25 @@ void Telephone::handleInput(NancyInput &input) {
     int buttonNr = -1;
     // Cursor gets changed regardless of state
     for (uint i = 0; i < 12; ++i) {
-        if (NancySceneState.getViewport().convertViewportToScreen(destRects[i]).contains(input.mousePos)) {
-            g_nancy->cursorManager->setCursorType(CursorManager::kHotspot);
+        if (NancySceneState.getViewport().convertViewportToScreen(_destRects[i]).contains(input.mousePos)) {
+            g_nancy->_cursorManager->setCursorType(CursorManager::kHotspot);
             buttonNr = i;
             break;
         }
     }
 
-    if (callState != kWaiting) {
+    if (_callState != kWaiting) {
         return;
     }
 
-    if (NancySceneState.getViewport().convertViewportToScreen(exitHotspot).contains(input.mousePos)) {
-        g_nancy->cursorManager->setCursorType(CursorManager::kExitArrow);
+    if (NancySceneState.getViewport().convertViewportToScreen(_exitHotspot).contains(input.mousePos)) {
+        g_nancy->_cursorManager->setCursorType(CursorManager::kExitArrow);
 
         if (input.input & NancyInput::kLeftMouseButtonUp) {
-            g_nancy->sound->loadSound(hangUpSound);
-            g_nancy->sound->playSound(hangUpSound);
+            g_nancy->_sound->loadSound(_hangUpSound);
+            g_nancy->_sound->playSound(_hangUpSound);
 
-            callState = kHangUp;
+            _callState = kHangUp;
         }
         
         return;
@@ -279,33 +279,33 @@ void Telephone::handleInput(NancyInput &input) {
 
     if (buttonNr != -1) {
         if (input.input & NancyInput::kLeftMouseButtonUp) {
-            if (g_nancy->sound->isSoundPlaying(dialToneSound)) {
-                g_nancy->sound->stopSound(dialToneSound);
+            if (g_nancy->_sound->isSoundPlaying(_dialToneSound)) {
+                g_nancy->_sound->stopSound(_dialToneSound);
             }
 
-            calledNumber.push_back(buttonNr);
-            genericButtonSound.name = buttonSoundNames[buttonNr];
-            g_nancy->sound->loadSound(genericButtonSound);
-            g_nancy->sound->playSound(genericButtonSound);
+            _calledNumber.push_back(buttonNr);
+            _genericButtonSound.name = _buttonSoundNames[buttonNr];
+            g_nancy->_sound->loadSound(_genericButtonSound);
+            g_nancy->_sound->playSound(_genericButtonSound);
 
             drawButton(buttonNr);
 
-            selected = buttonNr;
+            _selected = buttonNr;
 
-            callState = kButtonPress;
+            _callState = kButtonPress;
         }
     }
 }
 
 void Telephone::drawButton(uint id) {
-    Common::Point destPoint(destRects[id].left - _screenPosition.left, destRects[id].top - _screenPosition.top);
-    _drawSurface.blitFrom(image, srcRects[id], destPoint);
+    Common::Point destPoint(_destRects[id].left - _screenPosition.left, _destRects[id].top - _screenPosition.top);
+    _drawSurface.blitFrom(_image, _srcRects[id], destPoint);
 
     _needsRedraw = true;
 }
 
 void Telephone::undrawButton(uint id) {
-    Common::Rect bounds = destRects[id];
+    Common::Rect bounds = _destRects[id];
     bounds.translate(-_screenPosition.left, -_screenPosition.top);
 
     _drawSurface.fillRect(bounds, GraphicsManager::getTransColor());
diff --git a/engines/nancy/action/telephone.h b/engines/nancy/action/telephone.h
index 4f138962ab..612a507301 100644
--- a/engines/nancy/action/telephone.h
+++ b/engines/nancy/action/telephone.h
@@ -43,7 +43,7 @@ public:
         Common::Array<byte> phoneNumber; // 0x0, 11 bytes
         Common::String soundName; // 0xB
         Common::String text; // 0x15, 0xC8 bytes
-        SceneChangeDescription sceneChange; // 0xDD
+        SceneChangeDescription _sceneChange; // 0xDD
         // shouldStopRendering
         EventFlagDescription flag; // 0xE7
     };
@@ -52,8 +52,8 @@ public:
 
     Telephone(RenderObject &redrawFrom) :
         RenderObject(redrawFrom),
-        callState(kWaiting),
-        selected(0) {}
+        _callState(kWaiting),
+        _selected(0) {}
     virtual ~Telephone() {}
 
     virtual void init() override;
@@ -62,30 +62,30 @@ public:
     virtual void execute() override;
     virtual void handleInput(NancyInput &input) override;
 
-    Common::String imageName; // 0x00
-    Common::Array<Common::Rect> srcRects; // 0xA, 12
-    Common::Array<Common::Rect> destRects; // 0xCA, 12
-    SoundDescription genericDialogueSound; // 0x18A
-    SoundDescription genericButtonSound; // 0x1AC
-    SoundDescription ringSound; // 0x1CE
-    SoundDescription dialToneSound; // 0x1F0
-    SoundDescription dialAgainSound; // 0x212
-    SoundDescription hangUpSound; // 0x234
-    Common::Array<Common::String> buttonSoundNames; // 0x256, 12 * 0xA
-    Common::String addressBookString; // 0x2CE, 0xC8 long
-    Common::String dialAgainString; // 0x396
-    SceneChangeDescription reloadScene; // 0x45E
-    EventFlagDescription flagOnReload; // 0x468 ??
-    SceneChangeDescription exitScene; // 0x46C
-    EventFlagDescription flagOnExit; // 0x476
-    Common::Rect exitHotspot; // 0x47A
+    Common::String _imageName; // 0x00
+    Common::Array<Common::Rect> _srcRects; // 0xA, 12
+    Common::Array<Common::Rect> _destRects; // 0xCA, 12
+    SoundDescription _genericDialogueSound; // 0x18A
+    SoundDescription _genericButtonSound; // 0x1AC
+    SoundDescription _ringSound; // 0x1CE
+    SoundDescription _dialToneSound; // 0x1F0
+    SoundDescription _dialAgainSound; // 0x212
+    SoundDescription _hangUpSound; // 0x234
+    Common::Array<Common::String> _buttonSoundNames; // 0x256, 12 * 0xA
+    Common::String _addressBookString; // 0x2CE, 0xC8 long
+    Common::String _dialAgainString; // 0x396
+    SceneChangeDescription _reloadScene; // 0x45E
+    EventFlagDescription _flagOnReload; // 0x468 ??
+    SceneChangeDescription _exitScene; // 0x46C
+    EventFlagDescription _flagOnExit; // 0x476
+    Common::Rect _exitHotspot; // 0x47A
     // 0x48A numConvos
-    Common::Array<PhoneCall> calls; // 0x48C
+    Common::Array<PhoneCall> _calls; // 0x48C
 
-    Common::Array<byte> calledNumber;
-    Graphics::ManagedSurface image;
-    CallState callState;
-    uint selected;
+    Common::Array<byte> _calledNumber;
+    Graphics::ManagedSurface _image;
+    CallState _callState;
+    uint _selected;
 
 protected:
     virtual Common::String getRecordTypeName() const override { return "Telephone"; }
diff --git a/engines/nancy/console.cpp b/engines/nancy/console.cpp
index f0e28cac49..b5e460f8e4 100644
--- a/engines/nancy/console.cpp
+++ b/engines/nancy/console.cpp
@@ -85,7 +85,7 @@ void NancyConsole::postEnter() {
 				g_system->delayMillis(10);
 			}
 
-			g_nancy->graphicsManager->redrawAll();
+			g_nancy->_graphicsManager->redrawAll();
 		} else {
 			debugPrintf("Failed to load '%s'\n", _videoFile.c_str());
 		}
@@ -96,7 +96,7 @@ void NancyConsole::postEnter() {
 
 	if (!_imageFile.empty()) {
 		Graphics::Surface surf;
-		if (g_nancy->resource->loadImage(_imageFile, surf)) {
+		if (g_nancy->_resource->loadImage(_imageFile, surf)) {
 			g_system->fillScreen(0);
 			g_system->copyRectToScreen(surf.getPixels(), surf.pitch, 0, 0, surf.w > 640 ? 640 : surf.w, surf.h > 480 ? 480 : surf.h);
 			g_system->updateScreen();
@@ -116,7 +116,7 @@ void NancyConsole::postEnter() {
 				g_system->delayMillis(10);
 			}
 			
-			g_nancy->graphicsManager->redrawAll();
+			g_nancy->_graphicsManager->redrawAll();
 		} else {
 			debugPrintf("Failed to load image '%s'\n", _imageFile.c_str());
 		}
@@ -126,7 +126,7 @@ void NancyConsole::postEnter() {
 
 	// After calling the console, action end events get sent to it and the input manager
 	// can still think a keyboard button is being held when it is not; clearing all inputs fixes that
-	g_nancy->input->forceCleanInput();
+	g_nancy->_input->forceCleanInput();
 }
 
 bool NancyConsole::Cmd_cifHexDump(int argc, const char **argv) {
@@ -137,7 +137,7 @@ bool NancyConsole::Cmd_cifHexDump(int argc, const char **argv) {
 	}
 
 	uint size;
-	byte *buf = g_nancy->resource->loadCif((argc == 2 ? "ciftree" : argv[2]), argv[1], size);
+	byte *buf = g_nancy->_resource->loadCif((argc == 2 ? "ciftree" : argv[2]), argv[1], size);
 	if (!buf) {
 		debugPrintf("Failed to load resource '%s'\n", argv[1]);
 		return true;
@@ -155,7 +155,7 @@ bool NancyConsole::Cmd_cifExport(int argc, const char **argv) {
 		return true;
 	}
 
-	if (!g_nancy->resource->exportCif((argc == 2 ? "ciftree" : argv[2]), argv[1]))
+	if (!g_nancy->_resource->exportCif((argc == 2 ? "ciftree" : argv[2]), argv[1]))
 		debugPrintf("Failed to export '%s'\n", argv[1]);
 
 	return true;
@@ -170,7 +170,7 @@ bool NancyConsole::Cmd_cifList(int argc, const char **argv) {
 	}
 
 	Common::Array<Common::String> list;
-	g_nancy->resource->list((argc == 2 ? "ciftree" : argv[2]), list, atoi(argv[1]));
+	g_nancy->_resource->list((argc == 2 ? "ciftree" : argv[2]), list, atoi(argv[1]));
 	for (uint i = 0; i < list.size(); i++) {
 		debugPrintf("%-38s", list[i].c_str());
 		if ((i % 2) == 1 && i + 1 != list.size())
@@ -189,7 +189,7 @@ bool NancyConsole::Cmd_cifInfo(int argc, const char **argv) {
 		return true;
 	}
 
-	debugPrintf("%s", g_nancy->resource->getCifDescription((argc == 2 ? "ciftree" : argv[2]), argv[1]).c_str());
+	debugPrintf("%s", g_nancy->_resource->getCifDescription((argc == 2 ? "ciftree" : argv[2]), argv[1]).c_str());
 	return true;
 }
 
@@ -272,7 +272,7 @@ bool NancyConsole::Cmd_loadCal(int argc, const char **argv) {
 		return true;
 	}
 
-	if (!g_nancy->resource->loadCifTree(argv[1], "cal"))
+	if (!g_nancy->_resource->loadCifTree(argv[1], "cal"))
 		debugPrintf("Failed to load '%s.cal'\n", argv[1]);
 	return true;
 }
diff --git a/engines/nancy/cursor.cpp b/engines/nancy/cursor.cpp
index 12a42dc187..2915f886b2 100644
--- a/engines/nancy/cursor.cpp
+++ b/engines/nancy/cursor.cpp
@@ -51,7 +51,7 @@ void CursorManager::init() {
     _primaryVideoInitialPos.x = chunk->readUint16LE();
     _primaryVideoInitialPos.y = chunk->readUint16LE();
 
-    g_nancy->resource->loadImage(inventoryCursorsImageName, _invCursorsSurface);
+    g_nancy->_resource->loadImage(inventoryCursorsImageName, _invCursorsSurface);
 
     setCursor(kNormalArrow, -1);
     showCursor(false);
@@ -106,7 +106,7 @@ void CursorManager::setCursor(CursorType type, int16 itemID) {
         surf = &_invCursorsSurface;
         
     } else {
-        surf = &g_nancy->graphicsManager->object0;
+        surf = &g_nancy->_graphicsManager->_object0;
     }
 
     // TODO this is ridiculous, figure out why just calling
diff --git a/engines/nancy/font.cpp b/engines/nancy/font.cpp
index 91674b775e..8fed0c995c 100644
--- a/engines/nancy/font.cpp
+++ b/engines/nancy/font.cpp
@@ -38,7 +38,7 @@ void Font::read(Common::SeekableReadStream &stream) {
     stream.read(name, 10);
     Common::String imageName = name;
 
-    g_nancy->resource->loadImage(name, _image);
+    g_nancy->_resource->loadImage(name, _image);
 
     char desc[0x20];
     stream.read(desc, 0x20);
diff --git a/engines/nancy/graphics.cpp b/engines/nancy/graphics.cpp
index 9de7a7b946..647417df58 100644
--- a/engines/nancy/graphics.cpp
+++ b/engines/nancy/graphics.cpp
@@ -39,20 +39,20 @@
 
 namespace Nancy {
 
-const Graphics::PixelFormat GraphicsManager::inputPixelFormat = Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0);
-const Graphics::PixelFormat GraphicsManager::screenPixelFormat = Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0);
-const Graphics::PixelFormat GraphicsManager::clut8Format = Graphics::PixelFormat::createFormatCLUT8();
+const Graphics::PixelFormat GraphicsManager::_inputPixelFormat = Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0);
+const Graphics::PixelFormat GraphicsManager::_screenPixelFormat = Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0);
+const Graphics::PixelFormat GraphicsManager::_clut8Format = Graphics::PixelFormat::createFormatCLUT8();
 
 void GraphicsManager::init() {
-    initGraphics(640, 480, &screenPixelFormat);
-    _screen.create(640, 480, screenPixelFormat);
+    initGraphics(640, 480, &_screenPixelFormat);
+    _screen.create(640, 480, _screenPixelFormat);
     _screen.setTransparentColor(getTransColor());
     _screen.clear();
 
     Common::SeekableReadStream *ob = g_nancy->getBootChunkStream("OB0");
     ob->seek(0);
 
-    g_nancy->resource->loadImage(ob->readString(), object0);
+    g_nancy->_resource->loadImage(ob->readString(), _object0);
 
     loadFonts();
 }
@@ -220,9 +220,9 @@ void GraphicsManager::copyToManaged(void *src, Graphics::ManagedSurface &dst, ui
 
 const Graphics::PixelFormat &GraphicsManager::getInputPixelFormat() {
     if (g_nancy->getGameFlags() & NGF_8BITCOLOR) {
-        return clut8Format;
+        return _clut8Format;
     } else {
-        return inputPixelFormat;
+        return _inputPixelFormat;
     }
 }
 
@@ -230,7 +230,7 @@ uint GraphicsManager::getTransColor() {
     if (g_nancy->getGameFlags() & NGF_8BITCOLOR) {
         return 1; // If this isn't correct, try picking the pixel at [0, 0] inside the palette bitmap
     } else {
-        return inputPixelFormat.ARGBToColor(0, 0, 255, 0);
+        return _inputPixelFormat.ARGBToColor(0, 0, 255, 0);
     }
 }
 
diff --git a/engines/nancy/graphics.h b/engines/nancy/graphics.h
index 0e0d9d49a5..15918db555 100644
--- a/engines/nancy/graphics.h
+++ b/engines/nancy/graphics.h
@@ -57,9 +57,9 @@ public:
     static const Graphics::PixelFormat &getInputPixelFormat();
     static uint getTransColor();
 
-    Graphics::ManagedSurface object0;
+    Graphics::ManagedSurface _object0;
     
-    static const Graphics::PixelFormat screenPixelFormat;
+    static const Graphics::PixelFormat _screenPixelFormat;
     
 private:
     void loadFonts();
@@ -69,8 +69,8 @@ private:
 
     Common::SortedArray<RenderObject *> _objects;
 
-    static const Graphics::PixelFormat inputPixelFormat;
-    static const Graphics::PixelFormat clut8Format;
+    static const Graphics::PixelFormat _inputPixelFormat;
+    static const Graphics::PixelFormat _clut8Format;
 
     Graphics::Screen _screen;
     Common::Array<Font> _fonts;
diff --git a/engines/nancy/iff.cpp b/engines/nancy/iff.cpp
index 9c7392e233..099ca07d05 100644
--- a/engines/nancy/iff.cpp
+++ b/engines/nancy/iff.cpp
@@ -69,7 +69,7 @@ bool IFF::callback(Common::IFFChunk &c) {
 bool IFF::load() {
 	byte *data;
 	uint size;
-	data = g_nancy->resource->loadData(_name, size);
+	data = g_nancy->_resource->loadData(_name, size);
 
 	if (!data) {
 		return false;
diff --git a/engines/nancy/input.cpp b/engines/nancy/input.cpp
index e5be3ae9a3..8cc79ddb11 100644
--- a/engines/nancy/input.cpp
+++ b/engines/nancy/input.cpp
@@ -42,7 +42,7 @@ void InputManager::processEvents() {
         case EVENT_KEYDOWN:
             if (event.kbd.keycode == KEYCODE_d && event.kbd.flags & Common::KBD_CTRL) {
                 // Launch debug console
-                g_nancy->launchConsole = true;
+                g_nancy->_launchConsole = true;
             } else if (event.kbd.keycode == KEYCODE_q && event.kbd.flags & Common::KBD_CTRL) {
                 // Quit
                 g_nancy->quitGame();
diff --git a/engines/nancy/nancy.cpp b/engines/nancy/nancy.cpp
index 45ac0fcd01..d5f9760863 100644
--- a/engines/nancy/nancy.cpp
+++ b/engines/nancy/nancy.cpp
@@ -72,26 +72,26 @@ NancyEngine::NancyEngine(OSystem *syst, const NancyGameDescription *gd) : Engine
 	DebugMan.addDebugChannel(kDebugScene, "Scene", "Scene debug level");
 
 	_console = new NancyConsole();
-	randomSource = new Common::RandomSource("Nancy");
-	randomSource->setSeed(randomSource->getSeed());
+	_randomSource = new Common::RandomSource("Nancy");
+	_randomSource->setSeed(_randomSource->getSeed());
 
-	input = new InputManager();
-	sound = new SoundManager();
-	graphicsManager = new GraphicsManager();
-	cursorManager = new CursorManager();
+	_input = new InputManager();
+	_sound = new SoundManager();
+	_graphicsManager = new GraphicsManager();
+	_cursorManager = new CursorManager();
 
-	launchConsole = false;
+	_launchConsole = false;
 }
 
 NancyEngine::~NancyEngine() {
 	clearBootChunks();
 	DebugMan.clearAllDebugChannels();
 	delete _console;
-	delete randomSource;
+	delete _randomSource;
 
-	delete graphicsManager;
-	delete input;
-	delete sound;
+	delete _graphicsManager;
+	delete _input;
+	delete _sound;
 }
 
 NancyEngine *NancyEngine::create(GameType type, OSystem *syst, const NancyGameDescription *gd) {
@@ -130,7 +130,7 @@ bool NancyEngine::canLoadGameStateCurrently()  {
 
 bool NancyEngine::canSaveGameStateCurrently() {
 	// TODO also disable during secondary movie
-	return Action::PlayPrimaryVideoChan0::activePrimaryVideo == nullptr;
+	return Action::PlayPrimaryVideoChan0::_activePrimaryVideo == nullptr;
 }
 
 bool NancyEngine::hasFeature(EngineFeature f) const {
@@ -193,13 +193,13 @@ void NancyEngine::setState(GameState state, GameState overridePrevious) {
 			runDialog(*dialog);
 			delete dialog;
 		}
-		input->forceCleanInput();
+		_input->forceCleanInput();
 		return;
 	default:
 		break;
 	}
 
-	graphicsManager->clearObjects();
+	_graphicsManager->clearObjects();
 
 	_gameFlow.previousState = _gameFlow.currentState;
 	_gameFlow.currentState = getStateObject(state);
@@ -230,7 +230,7 @@ void NancyEngine::setPreviousState() {
 }
 
 void NancyEngine::setMouseEnabled(bool enabled) {
-	cursorManager->showCursor(enabled); input->setMouseInputEnabled(enabled);
+	_cursorManager->showCursor(enabled); _input->setMouseInputEnabled(enabled);
 }
 
 void NancyEngine::callCheatMenu(bool eventFlags)
@@ -253,18 +253,18 @@ Common::Error NancyEngine::run() {
 
 	// Main loop
 	while (!shouldQuit()) {
-		cursorManager->setCursorType(CursorManager::kNormalArrow);
-		input->processEvents();
+		_cursorManager->setCursorType(CursorManager::kNormalArrow);
+		_input->processEvents();
 		
 		if (_gameFlow.currentState) {
 			_gameFlow.currentState->process();
 		}
 
-		graphicsManager->draw();
+		_graphicsManager->draw();
 
-		if (launchConsole) {
+		if (_launchConsole) {
 			_console->attach();
-			launchConsole = false;
+			_launchConsole = false;
 		}
 		_console->onFrame();
 
@@ -300,8 +300,8 @@ void NancyEngine::bootGameEngine() {
 		}
 	}
 	
-	resource = new ResourceManager();
-	resource->initialize();
+	_resource = new ResourceManager();
+	_resource->initialize();
 
 	// Setup mixer
 	syncSoundSettings();
@@ -332,22 +332,22 @@ void NancyEngine::bootGameEngine() {
 	// Persistent sounds that are used across the engine. These originally get loaded inside Logo
 	SoundDescription desc;
 	desc.read(*g_nancy->getBootChunkStream("BUOK"), SoundDescription::kNormal);
-	g_nancy->sound->loadSound(desc);
+	g_nancy->_sound->loadSound(desc);
 	desc.read(*g_nancy->getBootChunkStream("BUDE"), SoundDescription::kNormal);
-	g_nancy->sound->loadSound(desc);
+	g_nancy->_sound->loadSound(desc);
 	desc.read(*g_nancy->getBootChunkStream("BULS"), SoundDescription::kNormal);
-	g_nancy->sound->loadSound(desc);
+	g_nancy->_sound->loadSound(desc);
 	desc.read(*g_nancy->getBootChunkStream("GLOB"), SoundDescription::kNormal);
-	g_nancy->sound->loadSound(desc);
+	g_nancy->_sound->loadSound(desc);
 	desc.read(*g_nancy->getBootChunkStream("CURT"), SoundDescription::kNormal);
-	g_nancy->sound->loadSound(desc);
+	g_nancy->_sound->loadSound(desc);
 	desc.read(*g_nancy->getBootChunkStream("CANT"), SoundDescription::kNormal);
-	g_nancy->sound->loadSound(desc);
+	g_nancy->_sound->loadSound(desc);
 
 	delete boot;
 	
-	graphicsManager->init();
-	cursorManager->init();
+	_graphicsManager->init();
+	_cursorManager->init();
 }
 
 State::State *NancyEngine::getStateObject(GameState state) {
@@ -405,7 +405,7 @@ void NancyEngine::preloadCals(const IFF &boot) {
 			stream.read(name, nameLen);
 			name[nameLen - 1] = 0;
 			debugC(1, kDebugEngine, "Preloading CAL '%s'", name);
-			if (!resource->loadCifTree(name, "cal"))
+			if (!_resource->loadCifTree(name, "cal"))
 				error("Failed to preload CAL '%s'", name);
 		}
 
@@ -436,9 +436,9 @@ void NancyEngine::readBootSummary(const IFF &boot) {
 
 	ser.skip(0x71, kGameTypeVampire, kGameTypeVampire);
 	ser.skip(0xA3, kGameTypeNancy1, kGameTypeNancy2);
-	ser.syncAsUint16LE(firstSceneID);
+	ser.syncAsUint16LE(_firstSceneID);
 	ser.skip(4, kGameTypeNancy1, kGameTypeNancy2);
-	ser.syncAsUint16LE(startTimeHours, kGameTypeNancy1, kGameTypeNancy2);
+	ser.syncAsUint16LE(_startTimeHours, kGameTypeNancy1, kGameTypeNancy2);
 	
 	ser.skip(0xB8, kGameTypeVampire, kGameTypeVampire);
 	ser.skip(0xA6, kGameTypeNancy1, kGameTypeNancy1);
@@ -457,15 +457,15 @@ void NancyEngine::readBootSummary(const IFF &boot) {
 	ser.skip(0x99, kGameTypeNancy1, kGameTypeNancy1);
 	int16 time = 0;
 	ser.syncAsSint16LE(time, kGameTypeNancy1, kGameTypeNancy1);
-	playerTimeMinuteLength = time;
+	_playerTimeMinuteLength = time;
 	ser.skip(2, kGameTypeNancy1, kGameTypeNancy1);
-    ser.syncAsByte(overrideMovementTimeDeltas, kGameTypeNancy1, kGameTypeNancy1);
+    ser.syncAsByte(_overrideMovementTimeDeltas, kGameTypeNancy1, kGameTypeNancy1);
 
-    if (overrideMovementTimeDeltas) {
+    if (_overrideMovementTimeDeltas) {
 		ser.syncAsSint16LE(time, kGameTypeNancy1, kGameTypeNancy1);
-		slowMovementTimeDelta = time;
+		_slowMovementTimeDelta = time;
 		ser.syncAsSint16LE(time, kGameTypeNancy1, kGameTypeNancy1);
-		fastMovementTimeDelta = time;
+		_fastMovementTimeDelta = time;
     }
 }
 
diff --git a/engines/nancy/nancy.h b/engines/nancy/nancy.h
index f00bee29f5..585b64606e 100644
--- a/engines/nancy/nancy.h
+++ b/engines/nancy/nancy.h
@@ -136,23 +136,23 @@ public:
 	void callCheatMenu(bool eventFlags);
 
 	// Managers
-	ResourceManager *resource;
-	GraphicsManager *graphicsManager;
-	CursorManager *cursorManager;
-	InputManager *input;
-	SoundManager *sound;
+	ResourceManager *_resource;
+	GraphicsManager *_graphicsManager;
+	CursorManager *_cursorManager;
+	InputManager *_input;
+	SoundManager *_sound;
 	
-	Common::RandomSource *randomSource;
+	Common::RandomSource *_randomSource;
 
-	bool launchConsole;
+	bool _launchConsole;
 	
-	uint16 firstSceneID;
-	uint16 startTimeHours;
+	uint16 _firstSceneID;
+	uint16 _startTimeHours;
 
-	bool overrideMovementTimeDeltas;
-	Time slowMovementTimeDelta;
-	Time fastMovementTimeDelta;
-	Time playerTimeMinuteLength;
+	bool _overrideMovementTimeDeltas;
+	Time _slowMovementTimeDelta;
+	Time _fastMovementTimeDelta;
+	Time _playerTimeMinuteLength;
 
 private:
 	struct GameFlow {
diff --git a/engines/nancy/renderobject.cpp b/engines/nancy/renderobject.cpp
index 08e04ac6ef..084edddc7c 100644
--- a/engines/nancy/renderobject.cpp
+++ b/engines/nancy/renderobject.cpp
@@ -37,11 +37,11 @@ void RenderObject::init() {
 }
 
 void RenderObject::registerGraphics() {
-    g_nancy->graphicsManager->addObject(this);
+    g_nancy->_graphicsManager->addObject(this);
 }
 
 RenderObject::~RenderObject() {
-    g_nancy->graphicsManager->removeObject(this);
+    g_nancy->_graphicsManager->removeObject(this);
     if (_drawSurface.getPixels()) {
         _drawSurface.free();
     }
diff --git a/engines/nancy/state/credits.cpp b/engines/nancy/state/credits.cpp
index 8b3ea46333..d8f4077e7e 100644
--- a/engines/nancy/state/credits.cpp
+++ b/engines/nancy/state/credits.cpp
@@ -66,7 +66,7 @@ void Credits::init() {
     _pixelsToScroll = cred->readUint16LE();
     _sound.read(*cred, SoundDescription::kMenu);
 
-    g_nancy->resource->loadImage(buf, _fullTextSurface);
+    g_nancy->_resource->loadImage(buf, _fullTextSurface);
     
     Common::Rect src = _text._screenPosition;
     src.moveTo(Common::Point());
@@ -74,25 +74,25 @@ void Credits::init() {
     _text.setTransparent(true);
     _text.init();
 
-    g_nancy->sound->loadSound(_sound);
-    g_nancy->sound->playSound(_sound);
+    g_nancy->_sound->loadSound(_sound);
+    g_nancy->_sound->playSound(_sound);
 
     _background.registerGraphics();
     _text.registerGraphics();
 
-    g_nancy->cursorManager->showCursor(false);
+    g_nancy->_cursorManager->showCursor(false);
 
     _state = kRun;
 }
 
 void Credits::run() {
-    NancyInput input = g_nancy->input->getInput();
+    NancyInput input = g_nancy->_input->getInput();
 
     if (input.input & NancyInput::kLeftMouseButtonDown) {
         _state = kInit;
-        g_nancy->sound->stopSound(_sound);
+        g_nancy->_sound->stopSound(_sound);
         g_nancy->setState(NancyEngine::kMainMenu);
-        g_nancy->cursorManager->showCursor(true);
+        g_nancy->_cursorManager->showCursor(true);
         _fullTextSurface.free();
     }
 
diff --git a/engines/nancy/state/help.cpp b/engines/nancy/state/help.cpp
index 2ab7b33649..5795814fbd 100644
--- a/engines/nancy/state/help.cpp
+++ b/engines/nancy/state/help.cpp
@@ -76,29 +76,29 @@ void Help::init() {
 }
 
 void Help::begin() {
-	g_nancy->sound->loadSound(_sound);
-	g_nancy->sound->playSound(_sound);
+	g_nancy->_sound->loadSound(_sound);
+	g_nancy->_sound->playSound(_sound);
     
     _image.registerGraphics();
     _image.setVisible(true);
 
-    g_nancy->cursorManager->setCursorType(CursorManager::kNormalArrow);
+    g_nancy->_cursorManager->setCursorType(CursorManager::kNormalArrow);
     
     _state = kRun;
 }
 
 void Help::run() {
-    NancyInput input = g_nancy->input->getInput();
+    NancyInput input = g_nancy->_input->getInput();
 
     if (_hotspot.contains(input.mousePos) && input.input & NancyInput::kLeftMouseButtonUp) {
-        g_nancy->sound->playSound(0x18); // Hardcoded by original engine
+        g_nancy->_sound->playSound(0x18); // Hardcoded by original engine
         _state = kWaitForSound;
     }
 }
 
 void Help::waitForSound() {
-    if (!g_nancy->sound->isSoundPlaying(18)) {
-	    g_nancy->sound->stopSound(_sound);
+    if (!g_nancy->_sound->isSoundPlaying(18)) {
+	    g_nancy->_sound->stopSound(_sound);
         g_nancy->setPreviousState();
     }
 }
diff --git a/engines/nancy/state/logo.cpp b/engines/nancy/state/logo.cpp
index 45c945d788..024db5df1d 100644
--- a/engines/nancy/state/logo.cpp
+++ b/engines/nancy/state/logo.cpp
@@ -61,7 +61,7 @@ void Logo::process() {
 }
 
 bool Logo::onStateExit() {
-	g_nancy->sound->stopSound(_msnd);
+	g_nancy->_sound->stopSound(_msnd);
 	destroy();
 	return true;
 }
@@ -78,15 +78,15 @@ void Logo::init() {
 
 void Logo::startSound() {
 	_msnd.read(*g_nancy->getBootChunkStream("MSND"), SoundDescription::kMenu);
-	g_nancy->sound->loadSound(_msnd);
-	g_nancy->sound->playSound(_msnd);
+	g_nancy->_sound->loadSound(_msnd);
+	g_nancy->_sound->playSound(_msnd);
 
 	_startTicks = g_system->getMillis();
 	_state = kRun;
 }
 
 void Logo::run() {
-	if (g_system->getMillis() - _startTicks >= 7000 || (g_nancy->input->getInput().input & NancyInput::kLeftMouseButtonDown)) {
+	if (g_system->getMillis() - _startTicks >= 7000 || (g_nancy->_input->getInput().input & NancyInput::kLeftMouseButtonDown)) {
 		_state = kStop;
 	}
 }
@@ -96,7 +96,7 @@ void Logo::stop() {
 	// For the N+C key combo it looks for some kind of cheat file
 	// to initialize the game state with.
 
-	g_nancy->sound->stopSound(_msnd);
+	g_nancy->_sound->stopSound(_msnd);
 
 	g_nancy->setState(NancyEngine::kScene);
 }
diff --git a/engines/nancy/state/map.cpp b/engines/nancy/state/map.cpp
index 667fa4519e..55632b5416 100644
--- a/engines/nancy/state/map.cpp
+++ b/engines/nancy/state/map.cpp
@@ -80,8 +80,8 @@ void Map::init() {
     chunk->seek(0x18 + _mapID * 0x20, SEEK_SET);
     SoundDescription sound;
     sound.read(*chunk, SoundDescription::kMenu);
-    g_nancy->sound->loadSound(sound);
-    g_nancy->sound->playSound(0x14);
+    g_nancy->_sound->loadSound(sound);
+    g_nancy->_sound->playSound(0x14);
 
     _locations.clear();
 
@@ -116,17 +116,17 @@ void Map::init() {
     }
 
     registerGraphics();
-    g_nancy->cursorManager->setCursorItemID(-1);
+    g_nancy->_cursorManager->setCursorItemID(-1);
 
     _state = kRun;
 }
 
 void Map::run() {
-    if (!g_nancy->sound->isSoundPlaying(0x14) && !g_nancy->sound->isSoundPlaying(0x13)) {
-        g_nancy->sound->playSound(0x13);
+    if (!g_nancy->_sound->isSoundPlaying(0x14) && !g_nancy->_sound->isSoundPlaying(0x13)) {
+        g_nancy->_sound->playSound(0x13);
     }
 
-    NancyInput input = g_nancy->input->getInput();
+    NancyInput input = g_nancy->_input->getInput();
 
     _label.setLabel(-1);
 
@@ -140,7 +140,7 @@ void Map::run() {
     for (uint i = 0; i < 4; ++i) {
         auto &loc = _locations[i];
         if (loc.isActive && _viewport.convertToScreen(loc.hotspot).contains(input.mousePos)) {
-            g_nancy->cursorManager->setCursorType(CursorManager::kHotspotArrow);
+            g_nancy->_cursorManager->setCursorType(CursorManager::kHotspotArrow);
 
             _label.setLabel(i);
 
@@ -159,7 +159,7 @@ bool Map::onStateExit() {
     SoundDescription sound;
     chunk->seek(0x18 + _mapID * 0x20, SEEK_SET);
     sound.read(*chunk, SoundDescription::kMenu);
-    g_nancy->sound->stopSound(sound);
+    g_nancy->_sound->stopSound(sound);
     
     g_nancy->setState(NancyEngine::kScene);
 
@@ -168,11 +168,11 @@ bool Map::onStateExit() {
         NancySceneState.changeScene(loc.scenes[_mapID].sceneID, loc.scenes[_mapID].frameID, loc.scenes[_mapID].verticalOffset, false);
         _pickedLocationID = -1;
         
-        g_nancy->sound->playSound(0x18);
+        g_nancy->_sound->playSound(0x18);
     }
     
     // The two sounds play at the same time if a location was picked
-    g_nancy->sound->playSound(0x14);
+    g_nancy->_sound->playSound(0x14);
 
     _mapButtonClicked = false;
 
@@ -197,7 +197,7 @@ void Map::MapLabel::setLabel(int labelID) {
         setVisible(false);
     } else {
         _screenPosition = _parent->_locations[labelID].labelDest;
-        _drawSurface.create(g_nancy->graphicsManager->object0, _parent->_locations[labelID].labelSrc);
+        _drawSurface.create(g_nancy->_graphicsManager->_object0, _parent->_locations[labelID].labelSrc);
         setVisible(true);
     }
 }
@@ -208,7 +208,7 @@ void Map::MapButton::init() {
     map->seek(0x7A, SEEK_SET);
     Common::Rect src;
     readRect(*map, src);
-    _drawSurface.create(g_nancy->graphicsManager->object0, src);
+    _drawSurface.create(g_nancy->_graphicsManager->_object0, src);
     readRect(*map, _screenPosition);
     setVisible(true);
 
diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index 5b4b7a9833..dce13130db 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -83,9 +83,9 @@ void Scene::SceneSummary::read(Common::SeekableReadStream &stream) {
     ser.syncAsUint16LE((uint32 &)slowMoveTimeDelta);
     ser.syncAsUint16LE((uint32 &)fastMoveTimeDelta);
 
-    if (g_nancy->overrideMovementTimeDeltas) {
-        slowMoveTimeDelta = g_nancy->slowMovementTimeDelta;
-        fastMoveTimeDelta = g_nancy->fastMovementTimeDelta;
+    if (g_nancy->_overrideMovementTimeDeltas) {
+        slowMoveTimeDelta = g_nancy->_slowMovementTimeDelta;
+        fastMoveTimeDelta = g_nancy->_fastMovementTimeDelta;
     }
 
     delete[] buf;
@@ -106,10 +106,10 @@ void Scene::process() {
         // fall through
     case kStartSound:
         _state = kRun;
-        if (!_sceneState._doNotStartSound) {
-            g_nancy->sound->stopAndUnloadSpecificSounds();
-            g_nancy->sound->loadSound(_sceneState.summary.sound);
-            g_nancy->sound->playSound(_sceneState.summary.sound);
+        if (!_sceneState.doNotStartSound) {
+            g_nancy->_sound->stopAndUnloadSpecificSounds();
+            g_nancy->_sound->loadSound(_sceneState.summary.sound);
+            g_nancy->_sound->playSound(_sceneState.summary.sound);
         }
         // fall through
     case kRun:
@@ -123,7 +123,7 @@ void Scene::onStateEnter() {
         registerGraphics();
         _actionManager.onPause(false);
 
-        g_nancy->graphicsManager->redrawAll();
+        g_nancy->_graphicsManager->redrawAll();
 
         // Run once to clear out the previous scene when coming from Map
         process();
@@ -151,7 +151,7 @@ void Scene::changeScene(uint16 id, uint16 frame, uint16 verticalOffset, bool noS
     _sceneState.nextScene.sceneID = id;
     _sceneState.nextScene.frameID = frame;
     _sceneState.nextScene.verticalOffset = verticalOffset;
-    _sceneState._doNotStartSound = noSound;
+    _sceneState.doNotStartSound = noSound;
     _state = kLoad;
 }
 
@@ -173,13 +173,13 @@ void Scene::pauseSceneSpecificSounds() {
     // TODO missing if, same condition as the one in SoundManager::stopAndUnloadSpecificSounds
 
     for (uint i = 0; i < 10; ++i) {
-		g_nancy->sound->pauseSound(i, true);
+		g_nancy->_sound->pauseSound(i, true);
 	}
 }
 
 void Scene::unpauseSceneSpecificSounds() {
     for (uint i = 0; i < 10; ++i) {
-		g_nancy->sound->pauseSound(i, false);
+		g_nancy->_sound->pauseSound(i, false);
 	}
 }
 
@@ -275,7 +275,7 @@ void Scene::synchronize(Common::Serializer &ser) {
         ser.syncAsUint16LE(_sceneState.nextScene.sceneID);
         ser.syncAsUint16LE(_sceneState.nextScene.frameID);
         ser.syncAsUint16LE(_sceneState.nextScene.verticalOffset);
-        _sceneState._doNotStartSound = false;
+        _sceneState.doNotStartSound = false;
 
         load();
     }
@@ -319,7 +319,7 @@ void Scene::synchronize(Common::Serializer &ser) {
 	// TODO hardcoded inventory size
 	ser.syncArray(_flags.items, 11, Common::Serializer::Byte);
     ser.syncAsSint16LE(_flags.heldItem);
-    g_nancy->cursorManager->setCursorItemID(_flags.heldItem);
+    g_nancy->_cursorManager->setCursorItemID(_flags.heldItem);
 
 	ser.syncAsUint32LE((uint32 &)_timers.lastTotalTime);
 	ser.syncAsUint32LE((uint32 &)_timers.sceneTime);
@@ -356,7 +356,7 @@ void Scene::init() {
     }
 
     _timers.lastTotalTime = 0;
-    _timers.playerTime = g_nancy->startTimeHours * 3600000;
+    _timers.playerTime = g_nancy->_startTimeHours * 3600000;
     _timers.sceneTime = 0;
     _timers.timerTime = 0;
     _timers.timerIsActive = false;
@@ -364,7 +364,7 @@ void Scene::init() {
     _timers.pushedPlayTime = 0;
     _timers.timeOfDay = Timers::kDay;
 
-    _sceneState.nextScene.sceneID = g_nancy->firstSceneID;
+    _sceneState.nextScene.sceneID = g_nancy->_firstSceneID;
 
     Common::SeekableReadStream *chunk = g_nancy->getBootChunkStream("HINT");
     
@@ -380,7 +380,7 @@ void Scene::init() {
         _lastHint = -1;
     }
 
-    Action::SliderPuzzle::playerHasTriedPuzzle = false;
+    Action::SliderPuzzle::_playerHasTriedPuzzle = false;
 
     initStaticData();
 
@@ -397,7 +397,7 @@ void Scene::init() {
     }
 
     registerGraphics();
-    g_nancy->graphicsManager->redrawAll();
+    g_nancy->_graphicsManager->redrawAll();
 }
 
 void Scene::load() {
@@ -424,7 +424,7 @@ void Scene::load() {
                 _sceneState.summary.description.c_str(),
                 _sceneState.nextScene.frameID,
                 _sceneState.nextScene.verticalOffset,
-                _sceneState._doNotStartSound == true ? "true" : "false");
+                _sceneState.doNotStartSound == true ? "true" : "false");
 
     // Search for Action Records, maximum for a scene is 30
     Common::SeekableReadStream *actionRecordChunk = nullptr;
@@ -504,7 +504,7 @@ void Scene::run() {
     // Calculate the in-game time (playerTime)
     if (currentPlayTime > _timers.playerTimeNextMinute) {
         _timers.playerTime += 60000; // Add a minute
-        _timers.playerTimeNextMinute = currentPlayTime + g_nancy->playerTimeMinuteLength;
+        _timers.playerTimeNextMinute = currentPlayTime + g_nancy->_playerTimeMinuteLength;
     }
 
     // Set the time of day according to playerTime
@@ -517,7 +517,7 @@ void Scene::run() {
     }
 
     // Update the UI elements and handle input
-    NancyInput input = g_nancy->input->getInput();
+    NancyInput input = g_nancy->_input->getInput();
     _viewport.handleInput(input);
     _menuButton.handleInput(input);
     _helpButton.handleInput(input);
@@ -532,7 +532,7 @@ void Scene::run() {
     for (uint i = 0; i < _mapAccessSceneIDs.size(); ++i) {
         if (_sceneState.currentScene.sceneID == _mapAccessSceneIDs[i]) {
             if (_mapHotspot.contains(input.mousePos)) {
-                g_nancy->cursorManager->setCursorType(CursorManager::kHotspotArrow);
+                g_nancy->_cursorManager->setCursorType(CursorManager::kHotspotArrow);
 
                 if (input.input & NancyInput::kLeftMouseButtonUp) {
                     requestStateChange(NancyEngine::kMap);
@@ -569,7 +569,7 @@ void Scene::initStaticData() {
     _inventoryBox.init();
     _menuButton.init();
     _helpButton.init();
-    g_nancy->cursorManager->showCursor(true);
+    g_nancy->_cursorManager->showCursor(true);
 
     _state = kLoad;
 }
diff --git a/engines/nancy/state/scene.h b/engines/nancy/state/scene.h
index 74924b221b..82653db60a 100644
--- a/engines/nancy/state/scene.h
+++ b/engines/nancy/state/scene.h
@@ -130,7 +130,7 @@ public:
     void addItemToInventory(uint16 id);
     void removeItemFromInventory(uint16 id, bool pickUp = true);
     int16 getHeldItem() const { return _flags.heldItem; }
-    void setHeldItem(int16 id) { _flags.heldItem = id; g_nancy->cursorManager->setCursorItemID(id); }
+    void setHeldItem(int16 id) { _flags.heldItem = id; g_nancy->_cursorManager->setCursorItemID(id); }
     NancyFlag hasItem(int16 id) const { return _flags.items[id]; }
 
     void setEventFlag(int16 label, NancyFlag flag = kTrue);
@@ -195,7 +195,7 @@ private:
         SceneInfo pushedScene;
         bool isScenePushed;
 
-        bool _doNotStartSound = false;
+        bool doNotStartSound = false;
     };
 
     struct Timers {
diff --git a/engines/nancy/ui/button.cpp b/engines/nancy/ui/button.cpp
index da26812ba9..332af39ef5 100644
--- a/engines/nancy/ui/button.cpp
+++ b/engines/nancy/ui/button.cpp
@@ -36,7 +36,7 @@ namespace UI {
 
 void Button::handleInput(NancyInput &input) {
     if (_screenPosition.contains(input.mousePos)) {
-        g_nancy->cursorManager->setCursorType(CursorManager::kHotspotArrow);
+        g_nancy->_cursorManager->setCursorType(CursorManager::kHotspotArrow);
 
         if (input.input & NancyInput::kLeftMouseButtonUp) {
             onClick();
@@ -50,7 +50,7 @@ void MenuButton::init() {
     bsum->seek(0x184, SEEK_SET);
     Common::Rect src;
     readRect(*bsum, src);
-    _drawSurface.create(g_nancy->graphicsManager->object0, src);
+    _drawSurface.create(g_nancy->_graphicsManager->_object0, src);
     bsum->skip(16);
     readRect(*bsum, _screenPosition);
     setVisible(false);
@@ -60,7 +60,7 @@ void MenuButton::init() {
 
 void MenuButton::onClick() {
     NancySceneState.requestStateChange(NancyEngine::kMainMenu);
-    g_nancy->sound->playSound(0x18);
+    g_nancy->_sound->playSound(0x18);
     setVisible(true);
 }
 
@@ -70,7 +70,7 @@ void HelpButton::init() {
     bsum->seek(0x194, SEEK_SET);
     Common::Rect src;
     readRect(*bsum, src);
-    _drawSurface.create(g_nancy->graphicsManager->object0, src);
+    _drawSurface.create(g_nancy->_graphicsManager->_object0, src);
     bsum->skip(16);
     readRect(*bsum, _screenPosition);
     setVisible(false);
@@ -80,7 +80,7 @@ void HelpButton::init() {
 
 void HelpButton::onClick() {
     NancySceneState.requestStateChange(NancyEngine::kHelp);
-    g_nancy->sound->playSound(0x18);
+    g_nancy->_sound->playSound(0x18);
     setVisible(true);
 }
 
diff --git a/engines/nancy/ui/fullscreenimage.cpp b/engines/nancy/ui/fullscreenimage.cpp
index 10b2ed056a..979e704a35 100644
--- a/engines/nancy/ui/fullscreenimage.cpp
+++ b/engines/nancy/ui/fullscreenimage.cpp
@@ -30,7 +30,7 @@ namespace Nancy {
 namespace UI {
 
 void FullScreenImage::init(const Common::String &imageName) {
-    g_nancy->resource->loadImage(imageName, _drawSurface);
+    g_nancy->_resource->loadImage(imageName, _drawSurface);
 
     Common::Rect srcBounds = Common::Rect(0,0, _drawSurface.w, _drawSurface.h);
     _screenPosition = srcBounds;
diff --git a/engines/nancy/ui/inventorybox.cpp b/engines/nancy/ui/inventorybox.cpp
index cfbc8336f5..5636047b82 100644
--- a/engines/nancy/ui/inventorybox.cpp
+++ b/engines/nancy/ui/inventorybox.cpp
@@ -72,10 +72,10 @@ void InventoryBox::init() {
         readRect(stream, _itemDescriptions[i].sourceRect);
     }
 
-    g_nancy->resource->loadImage(inventoryBoxIconsImageName, _iconsSurface);
+    g_nancy->_resource->loadImage(inventoryBoxIconsImageName, _iconsSurface);
     
     uint numItems = 11; // TODO
-    _fullInventorySurface.create(_screenPosition.width(), _screenPosition.height() * ((numItems / 4) + 1), GraphicsManager::screenPixelFormat);
+    _fullInventorySurface.create(_screenPosition.width(), _screenPosition.height() * ((numItems / 4) + 1), GraphicsManager::_screenPixelFormat);
     Common::Rect sourceRect = _screenPosition;
     sourceRect.moveTo(0, 0);
     _drawSurface.create(_fullInventorySurface, sourceRect);
@@ -116,16 +116,16 @@ void InventoryBox::handleInput(NancyInput &input) {
     for (uint i = 0; i < 4; ++i) {
         if (_itemHotspots[i].hotspot.contains(input.mousePos)) {
             if (NancySceneState.getHeldItem() != -1) {
-                g_nancy->cursorManager->setCursorType(CursorManager::kHotspotArrow);
+                g_nancy->_cursorManager->setCursorType(CursorManager::kHotspotArrow);
                 if (input.input & NancyInput::kLeftMouseButtonUp) {
                     NancySceneState.addItemToInventory(NancySceneState.getHeldItem());
-                    g_nancy->sound->playSound(0x16);
+                    g_nancy->_sound->playSound(0x16);
                 }                
             } else if (_itemHotspots[i].itemID != -1) {
-                g_nancy->cursorManager->setCursorType(CursorManager::kHotspotArrow);
+                g_nancy->_cursorManager->setCursorType(CursorManager::kHotspotArrow);
                 if (input.input & NancyInput::kLeftMouseButtonUp) {
                     NancySceneState.removeItemFromInventory(_itemHotspots[i].itemID);
-                    g_nancy->sound->playSound(0x18);
+                    g_nancy->_sound->playSound(0x18);
                 }
             }
             break;
@@ -209,7 +209,7 @@ void InventoryBox::InventoryScrollbar::init() {
     Common::Rect &srcBounds = _parent->_sliderSource;
     Common::Point &topPosition = _parent->_sliderDefaultDest;
 
-    _drawSurface.create(g_nancy->graphicsManager->object0, srcBounds);
+    _drawSurface.create(g_nancy->_graphicsManager->_object0, srcBounds);
 
     _startPosition = topPosition;
     _startPosition.x -= srcBounds.width() / 2;
@@ -224,7 +224,7 @@ void InventoryBox::InventoryScrollbar::init() {
 
 void InventoryBox::Shades::init() {
     Common::Rect bounds = _parent->getBounds();
-    _drawSurface.create(bounds.width(), bounds.height(), GraphicsManager::screenPixelFormat);
+    _drawSurface.create(bounds.width(), bounds.height(), GraphicsManager::_screenPixelFormat);
     _screenPosition = _parent->getScreenPosition();
     _nextFrameTime = 0;
     setAnimationFrame(_curFrame);
@@ -243,7 +243,7 @@ void InventoryBox::Shades::updateGraphics() {
 
             if (!_soundTriggered) {
                 _soundTriggered = true;
-                g_nancy->sound->playSound(0x12);
+                g_nancy->_sound->playSound(0x12);
             }
         }
     } else {
@@ -253,7 +253,7 @@ void InventoryBox::Shades::updateGraphics() {
 
             if (!_soundTriggered) {
                 _soundTriggered = true;
-                g_nancy->sound->playSound(0x12);
+                g_nancy->_sound->playSound(0x12);
             }
         }
     }
@@ -264,7 +264,7 @@ void InventoryBox::Shades::updateGraphics() {
 }
 
 void InventoryBox::Shades::setAnimationFrame(uint frame) {
-    Graphics::ManagedSurface &object0 = g_nancy->graphicsManager->object0;
+    Graphics::ManagedSurface &_object0 = g_nancy->_graphicsManager->_object0;
     Common::Rect srcRect;
     Common::Point destPoint;
 
@@ -279,12 +279,12 @@ void InventoryBox::Shades::setAnimationFrame(uint frame) {
 
     // Draw left shade
     srcRect = _parent->_shadesSrc[frame * 2];
-    _drawSurface.blitFrom(object0, srcRect, destPoint);
+    _drawSurface.blitFrom(_object0, srcRect, destPoint);
 
     // Draw right shade
     srcRect = _parent->_shadesSrc[frame * 2 + 1];
     destPoint.x = getBounds().width() - srcRect.width();
-    _drawSurface.blitFrom(object0, srcRect, destPoint);
+    _drawSurface.blitFrom(_object0, srcRect, destPoint);
 
     _needsRedraw = true;
 }
diff --git a/engines/nancy/ui/scrollbar.cpp b/engines/nancy/ui/scrollbar.cpp
index 1346f21353..98114af35b 100644
--- a/engines/nancy/ui/scrollbar.cpp
+++ b/engines/nancy/ui/scrollbar.cpp
@@ -41,7 +41,7 @@ void Scrollbar::init() {
 
 void Scrollbar::handleInput(NancyInput &input) {
     if (_screenPosition.contains(input.mousePos)) {
-        g_nancy->cursorManager->setCursorType(CursorManager::kHotspotArrow);
+        g_nancy->_cursorManager->setCursorType(CursorManager::kHotspotArrow);
 
         if (input.input & NancyInput::kLeftMouseButtonDown && !_isClicked) {
             // Begin click and hold
diff --git a/engines/nancy/ui/textbox.cpp b/engines/nancy/ui/textbox.cpp
index b0742d5d2e..fc1282b3a3 100644
--- a/engines/nancy/ui/textbox.cpp
+++ b/engines/nancy/ui/textbox.cpp
@@ -38,14 +38,14 @@
 namespace Nancy {
 namespace UI {
 
-const char Textbox::CCBeginToken[] = "<i>";
-const char Textbox::CCEndToken[] = "<o>";
-const char Textbox::colorBeginToken[] = "<c1>";
-const char Textbox::colorEndToken[] = "<c0>";
-const char Textbox::hotspotToken[] = "<h>";
-const char Textbox::newLineToken[] = "<n>";
-const char Textbox::tabToken[] = "<t>";
-const char Textbox::telephoneEndToken[] = "<e>";
+const char Textbox::_CCBeginToken[] = "<i>";
+const char Textbox::_CCEndToken[] = "<o>";
+const char Textbox::_colorBeginToken[] = "<c1>";
+const char Textbox::_colorEndToken[] = "<c0>";
+const char Textbox::_hotspotToken[] = "<h>";
+const char Textbox::_newLineToken[] = "<n>";
+const char Textbox::_tabToken[] = "<t>";
+const char Textbox::_telephoneEndToken[] = "<e>";
 
 void Textbox::init() {    
     Common::SeekableReadStream *chunk = g_nancy->getBootChunkStream("TBOX");
@@ -55,7 +55,7 @@ void Textbox::init() {
     chunk->seek(0x20);
     Common::Rect innerBoundingBox;
     readRect(*chunk, innerBoundingBox);
-    _fullSurface.create(innerBoundingBox.width(), innerBoundingBox.height(), GraphicsManager::screenPixelFormat);
+    _fullSurface.create(innerBoundingBox.width(), innerBoundingBox.height(), GraphicsManager::_screenPixelFormat);
     
     _scrollbarDefaultDest.x = chunk->readUint16LE();
     _scrollbarDefaultDest.y = chunk->readUint16LE();
@@ -108,7 +108,7 @@ void Textbox::handleInput(NancyInput &input) {
         Common::Rect hotspot = _hotspots[i];
         hotspot.translate(0, -_drawSurface.getOffsetFromOwner().y);
         if (convertToScreen(hotspot).findIntersectingRect(_screenPosition).contains(input.mousePos)) {
-            g_nancy->cursorManager->setCursorType(CursorManager::kHotspotArrow);
+            g_nancy->_cursorManager->setCursorType(CursorManager::kHotspotArrow);
 
             if (input.input & NancyInput::kLeftMouseButtonUp) {
                 input.input &= ~NancyInput::kLeftMouseButtonUp;
@@ -126,7 +126,7 @@ void Textbox::drawTextbox() {
 
     _numLines = 0;
 
-    Font *font = g_nancy->graphicsManager->getFont(_fontID);
+    Font *font = g_nancy->_graphicsManager->getFont(_fontID);
 
     uint maxWidth = _fullSurface.w - _borderWidth * 2;
     uint lineDist = _lineHeight + _lineHeight / 4;
@@ -140,40 +140,40 @@ void Textbox::drawTextbox() {
         Rect hotspot;
 
         // Trim the begin and end tokens from the line
-        if (currentLine.hasPrefix(CCBeginToken) && currentLine.hasSuffix(CCEndToken)) {
-            currentLine = currentLine.substr(ARRAYSIZE(CCBeginToken) - 1, currentLine.size() - ARRAYSIZE(CCBeginToken) - ARRAYSIZE(CCEndToken) + 2);
+        if (currentLine.hasPrefix(_CCBeginToken) && currentLine.hasSuffix(_CCEndToken)) {
+            currentLine = currentLine.substr(ARRAYSIZE(_CCBeginToken) - 1, currentLine.size() - ARRAYSIZE(_CCBeginToken) - ARRAYSIZE(_CCEndToken) + 2);
         }
         
         // Replace every newline token with \n
         uint32 newLinePos;
-        while (newLinePos = currentLine.find(newLineToken), newLinePos != String::npos) {
-            currentLine.replace(newLinePos, ARRAYSIZE(newLineToken) - 1, "\n");
+        while (newLinePos = currentLine.find(_newLineToken), newLinePos != String::npos) {
+            currentLine.replace(newLinePos, ARRAYSIZE(_newLineToken) - 1, "\n");
         }
 
         // Simply remove telephone end token
-        if (currentLine.hasSuffix(telephoneEndToken)) {
-            currentLine = currentLine.substr(0, currentLine.size() - ARRAYSIZE(telephoneEndToken) + 1);
+        if (currentLine.hasSuffix(_telephoneEndToken)) {
+            currentLine = currentLine.substr(0, currentLine.size() - ARRAYSIZE(_telephoneEndToken) + 1);
         }
 
         // Remove hotspot token and mark that we need to calculate the bounds
         // Assumes a single text line has a single hotspot
-        uint32 hotspotPos = currentLine.find(hotspotToken);
+        uint32 hotspotPos = currentLine.find(_hotspotToken);
         if (hotspotPos != String::npos) {
-            currentLine.erase(hotspotPos, ARRAYSIZE(hotspotToken) - 1);
+            currentLine.erase(hotspotPos, ARRAYSIZE(_hotspotToken) - 1);
             hasHotspot = true;
         }
 
         // Subdivide current line into sublines for proper handling of the tab and color tokens
         // Assumes the tab token is on a new line
         while (!currentLine.empty()) {
-            if (currentLine.hasPrefix(tabToken)) {
+            if (currentLine.hasPrefix(_tabToken)) {
                 horizontalOffset += font->getStringWidth("    "); // Replace tab with 4 spaces
-                currentLine = currentLine.substr(ARRAYSIZE(tabToken) - 1);
+                currentLine = currentLine.substr(ARRAYSIZE(_tabToken) - 1);
             }
 
             String currentSubLine;
 
-            uint32 nextTabPos = currentLine.find(tabToken);
+            uint32 nextTabPos = currentLine.find(_tabToken);
             if (nextTabPos != String::npos) {
                 currentSubLine = currentLine.substr(0, nextTabPos);
                 currentLine = currentLine.substr(nextTabPos);
@@ -183,12 +183,12 @@ void Textbox::drawTextbox() {
             }
 
             // Assumes color token will be at the beginning of the line, and color string will not need wrapping
-            if (currentSubLine.hasPrefix(colorBeginToken)) {
+            if (currentSubLine.hasPrefix(_colorBeginToken)) {
                 // Found color string, look for end token
-                uint32 colorEndPos = currentSubLine.find(colorEndToken);
+                uint32 colorEndPos = currentSubLine.find(_colorEndToken);
 
-                Common::String colorSubLine = currentSubLine.substr(ARRAYSIZE(colorBeginToken) - 1, colorEndPos - ARRAYSIZE(colorBeginToken) + 1);
-                currentSubLine = currentSubLine.substr(ARRAYSIZE(colorBeginToken) + ARRAYSIZE(colorEndToken) + colorSubLine.size() - 2);
+                Common::String colorSubLine = currentSubLine.substr(ARRAYSIZE(_colorBeginToken) - 1, colorEndPos - ARRAYSIZE(_colorBeginToken) + 1);
+                currentSubLine = currentSubLine.substr(ARRAYSIZE(_colorBeginToken) + ARRAYSIZE(_colorEndToken) + colorSubLine.size() - 2);
 
                 // Draw the color line
                 font->drawString(&_fullSurface, colorSubLine, _borderWidth + horizontalOffset, _firstLineOffset - font->getFontHeight() + _numLines * lineDist, maxWidth, 1);
@@ -289,7 +289,7 @@ void Textbox::TextboxScrollbar::init() {
     Common::Rect &srcBounds = _parent->_scrollbarSourceBounds;
     Common::Point &topPosition = _parent->_scrollbarDefaultDest;
 
-    _drawSurface.create(g_nancy->graphicsManager->object0, srcBounds);
+    _drawSurface.create(g_nancy->_graphicsManager->_object0, srcBounds);
 
     _startPosition = topPosition;
     _startPosition.x -= srcBounds.width() / 2;
diff --git a/engines/nancy/ui/textbox.h b/engines/nancy/ui/textbox.h
index 59cbcf8705..e79355c3f6 100644
--- a/engines/nancy/ui/textbox.h
+++ b/engines/nancy/ui/textbox.h
@@ -107,16 +107,14 @@ private:
     bool _needsTextRedraw;
     float _scrollbarPos;
 
-    static const char CCBeginToken[];
-    static const char CCEndToken[];
-    static const char colorBeginToken[];
-    static const char colorEndToken[];
-    static const char hotspotToken[];
-    static const char newLineToken[];
-    static const char tabToken[];
-    static const char telephoneEndToken[];
-
-protected:
+    static const char _CCBeginToken[];
+    static const char _CCEndToken[];
+    static const char _colorBeginToken[];
+    static const char _colorEndToken[];
+    static const char _hotspotToken[];
+    static const char _newLineToken[];
+    static const char _tabToken[];
+    static const char _telephoneEndToken[];
 };
 
 } // End of namespace UI
diff --git a/engines/nancy/ui/viewport.cpp b/engines/nancy/ui/viewport.cpp
index cea93be76d..c972c7c1f1 100644
--- a/engines/nancy/ui/viewport.cpp
+++ b/engines/nancy/ui/viewport.cpp
@@ -63,7 +63,7 @@ void Viewport::handleInput(NancyInput &input) {
     byte direction = 0;
 
     if (_screenPosition.contains(input.mousePos)) {
-        g_nancy->cursorManager->setCursorType(CursorManager::kNormal);
+        g_nancy->_cursorManager->setCursorType(CursorManager::kNormal);
     }
 
     // Do not handle hotspots marked as incative and ignore diagonals if intersecting hotspots are not active
@@ -120,7 +120,7 @@ void Viewport::handleInput(NancyInput &input) {
     }
 
     if (direction) {
-        g_nancy->cursorManager->setCursorType(CursorManager::kMove);
+        g_nancy->_cursorManager->setCursorType(CursorManager::kMove);
 
         if (input.input & NancyInput::kRightMouseButton) {
             direction |= kMoveFast;


Commit: 54bdb420b0091a08209b10fa843e9130dc53ad93
    https://github.com/scummvm/scummvm/commit/54bdb420b0091a08209b10fa843e9130dc53ad93
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: String reading fixes

Added a \0 to every instance of a char buffer being loaded into a Common::String. Fixed an instance of a large buffer getting repeatedly allocated and deleted inside a for loop. Fixed an instance of a small char buffer being unnecessarily allocated on the heap. Also added a readFilename() convenience function to util.h for reading 8-character filenames found in the data.

Changed paths:
    engines/nancy/action/actionmanager.cpp
    engines/nancy/action/leverpuzzle.cpp
    engines/nancy/action/orderingpuzzle.cpp
    engines/nancy/action/passwordpuzzle.cpp
    engines/nancy/action/primaryvideo.cpp
    engines/nancy/action/recordtypes.cpp
    engines/nancy/action/rotatinglockpuzzle.cpp
    engines/nancy/action/secondarymovie.cpp
    engines/nancy/action/secondaryvideo.cpp
    engines/nancy/action/sliderpuzzle.cpp
    engines/nancy/action/staticbitmapanim.cpp
    engines/nancy/action/telephone.cpp
    engines/nancy/commontypes.cpp
    engines/nancy/font.cpp
    engines/nancy/state/credits.cpp
    engines/nancy/state/help.cpp
    engines/nancy/state/map.cpp
    engines/nancy/ui/inventorybox.cpp
    engines/nancy/util.h


diff --git a/engines/nancy/action/actionmanager.cpp b/engines/nancy/action/actionmanager.cpp
index 25803659f7..0b34beac8c 100644
--- a/engines/nancy/action/actionmanager.cpp
+++ b/engines/nancy/action/actionmanager.cpp
@@ -103,10 +103,10 @@ bool ActionManager::addNewActionRecord(Common::SeekableReadStream &inputData) {
     ActionRecord *newRecord = createActionRecord(ARType);
 
     inputData.seek(0);
-    char *descBuf = new char[0x30];
+    char descBuf[0x30];
     inputData.read(descBuf, 0x30);
-    newRecord->_description = Common::String(descBuf);
-    delete[] descBuf;
+    descBuf[0x2F] = '\0';
+    newRecord->_description = descBuf;
 
     newRecord->_type = inputData.readByte(); // redundant
     newRecord->_execType = (ActionRecord::ExecutionType)inputData.readByte();
diff --git a/engines/nancy/action/leverpuzzle.cpp b/engines/nancy/action/leverpuzzle.cpp
index d6799e772c..b27b07e7cd 100644
--- a/engines/nancy/action/leverpuzzle.cpp
+++ b/engines/nancy/action/leverpuzzle.cpp
@@ -42,9 +42,7 @@ void LeverPuzzle::init() {
 }
 
 void LeverPuzzle::readData(Common::SeekableReadStream &stream) {
-    char buf[10];
-    stream.read(buf, 10);
-    _imageName = buf;
+    readFilename(stream, _imageName);
 
     for (uint leverID = 0; leverID < 3; ++leverID) {
         _srcRects.push_back(Common::Array<Common::Rect>());
diff --git a/engines/nancy/action/orderingpuzzle.cpp b/engines/nancy/action/orderingpuzzle.cpp
index 4957bc85ea..6ea2fe90e7 100644
--- a/engines/nancy/action/orderingpuzzle.cpp
+++ b/engines/nancy/action/orderingpuzzle.cpp
@@ -53,10 +53,7 @@ void OrderingPuzzle::init() {
 }
 
 void OrderingPuzzle::readData(Common::SeekableReadStream &stream) {
-    char buf[10];
-
-    stream.read(buf, 10);
-    _imageName = buf;
+    readFilename(stream, _imageName);
     uint16 numElements = stream.readUint16LE();
 
     for (uint i = 0; i < numElements; ++i) {
diff --git a/engines/nancy/action/passwordpuzzle.cpp b/engines/nancy/action/passwordpuzzle.cpp
index 984cff6012..022b09a9ca 100644
--- a/engines/nancy/action/passwordpuzzle.cpp
+++ b/engines/nancy/action/passwordpuzzle.cpp
@@ -52,8 +52,10 @@ void PasswordPuzzle::readData(Common::SeekableReadStream &stream) {
 
     char buf[20];
     stream.read(buf, 20);
+    buf[19] = '\0';
     _name = buf;
     stream.read(buf, 20);
+    buf[19] = '\0';
     _password = buf;
     _solveExitScene.readData(stream);
     stream.skip(2);
diff --git a/engines/nancy/action/primaryvideo.cpp b/engines/nancy/action/primaryvideo.cpp
index 24008a9c10..8c3b912c9d 100644
--- a/engines/nancy/action/primaryvideo.cpp
+++ b/engines/nancy/action/primaryvideo.cpp
@@ -154,16 +154,14 @@ void PlayPrimaryVideoChan0::onPause(bool pause) {
 void PlayPrimaryVideoChan0::readData(Common::SeekableReadStream &stream) {
     uint16 beginOffset = stream.pos();
 
-    char name[10];
-    stream.read(name, 10);
-    _videoName = Common::String(name);
+    readFilename(stream, _videoName);
 
     stream.skip(0x13);
 
     readRect(stream, _src);
     readRect(stream, _screenPosition);
 
-    char *rawText = new char[1500]();
+    char *rawText = new char[1500];
     stream.read(rawText, 1500);
     UI::Textbox::assembleTextLine(rawText, _text, 1500);
     delete[] rawText;
@@ -180,18 +178,16 @@ void PlayPrimaryVideoChan0::readData(Common::SeekableReadStream &stream) {
     stream.seek(beginOffset + 0x69C);
 
     uint16 numResponses = stream.readUint16LE();
+    rawText = new char[400];
+
     if (numResponses > 0) {
         for (uint i = 0; i < numResponses; ++i) {
             _responses.push_back(ResponseStruct());
             ResponseStruct &response = _responses[i];
             response.conditionFlags.read(stream);
-            rawText = new char[400];
             stream.read(rawText, 400);
             UI::Textbox::assembleTextLine(rawText, response.text, 400);
-            delete[] rawText;
-
-            stream.read(name, 10);
-            response.soundName = name;
+            readFilename(stream, response.soundName);
             stream.skip(1);
             response._sceneChange.readData(stream);
             response.flagDesc.label = stream.readSint16LE();
@@ -200,6 +196,8 @@ void PlayPrimaryVideoChan0::readData(Common::SeekableReadStream &stream) {
             stream.skip(0x32);
         }
     }
+    
+    delete[] rawText;
 
     uint16 numSceneBranchStructs = stream.readUint16LE();
     if (numSceneBranchStructs > 0) {
@@ -336,11 +334,12 @@ void PlayPrimaryVideoChan0::addConditionalResponses() {
 
             if (isSatisfied) {
                 Common::File file;
-                char snd[10];
+                char snd[9];
 
                 file.open("game.exe");
                 file.seek(nancy1ResponseBaseFileOffset + res.fileOffset);
                 file.read(snd, 8);
+                snd[8] = '\0';
 
                 _responses.push_back(ResponseStruct());
                 ResponseStruct &newResponse = _responses.back();
@@ -359,11 +358,12 @@ void PlayPrimaryVideoChan0::addGoodbye() {
     for (auto &res : nancy1Goodbyes) {
         if (res.characterID == _goodbyeResponseCharacterID) {
             Common::File file;
-            char snd[10];
+            char snd[9];
 
             file.open("game.exe");
             file.seek(nancy1ResponseBaseFileOffset + res.fileOffset);
             file.read(snd, 8);
+            snd[8] = '\0';
 
             _responses.push_back(ResponseStruct());
             ResponseStruct &newResponse = _responses.back();
diff --git a/engines/nancy/action/recordtypes.cpp b/engines/nancy/action/recordtypes.cpp
index 97dd9bc29c..90a15de5c2 100644
--- a/engines/nancy/action/recordtypes.cpp
+++ b/engines/nancy/action/recordtypes.cpp
@@ -356,9 +356,7 @@ void ShowInventoryItem::init() {
 
 void ShowInventoryItem::readData(Common::SeekableReadStream &stream) {
     _objectID = stream.readUint16LE();
-    char name[10];
-    stream.read(name, 10);
-    _imageName = Common::String(name);
+    readFilename(stream, _imageName);
 
     uint16 numFrames = stream.readUint16LE();
 
@@ -595,15 +593,14 @@ void HintSystem::getHint(uint hint, uint difficulty) {
 
     file.seek(difficulty * 10, SEEK_CUR);
 
-    char soundName[10];
-    file.read(soundName, 10);
-    _genericSound.name = soundName;
+    readFilename(file, _genericSound.name);
 
     file.seek(-(difficulty * 10) - 10, SEEK_CUR);
     file.seek(30 + difficulty * 200, SEEK_CUR);
 
     char textBuf[200];
     file.read(textBuf, 200);
+    textBuf[199] = '\0';
     _text = textBuf;
 
     file.seek(-(difficulty * 200) - 200, SEEK_CUR);
diff --git a/engines/nancy/action/rotatinglockpuzzle.cpp b/engines/nancy/action/rotatinglockpuzzle.cpp
index 6661122ac1..d5568989d0 100644
--- a/engines/nancy/action/rotatinglockpuzzle.cpp
+++ b/engines/nancy/action/rotatinglockpuzzle.cpp
@@ -44,9 +44,7 @@ void RotatingLockPuzzle::init() {
 }
 
 void RotatingLockPuzzle::readData(Common::SeekableReadStream &stream) {
-    char buf[10];
-    stream.read(buf, 10);
-    _imageName = buf;
+    readFilename(stream, _imageName);
 
     uint numDials = stream.readUint16LE();
 
diff --git a/engines/nancy/action/secondarymovie.cpp b/engines/nancy/action/secondarymovie.cpp
index c0216ff1d2..ed5e160005 100644
--- a/engines/nancy/action/secondarymovie.cpp
+++ b/engines/nancy/action/secondarymovie.cpp
@@ -24,6 +24,7 @@
 
 #include "engines/nancy/graphics.h"
 #include "engines/nancy/nancy.h"
+#include "engines/nancy/util.h"
 #include "engines/nancy/state/scene.h"
 
 namespace Nancy {
@@ -38,9 +39,7 @@ PlaySecondaryMovie::~PlaySecondaryMovie() {
 }
 
 void PlaySecondaryMovie::readData(Common::SeekableReadStream &stream) {  
-    char name[10];
-    stream.read(name, 10);
-    _videoName = name;
+    readFilename(stream, _videoName);
 
     stream.skip(0x12);
     _unknown = stream.readUint16LE();
diff --git a/engines/nancy/action/secondaryvideo.cpp b/engines/nancy/action/secondaryvideo.cpp
index 2c702f6b46..cb7f3aa708 100644
--- a/engines/nancy/action/secondaryvideo.cpp
+++ b/engines/nancy/action/secondaryvideo.cpp
@@ -146,11 +146,8 @@ void PlaySecondaryVideo::handleInput(NancyInput &input) {
 }
 
 void PlaySecondaryVideo::readData(Common::SeekableReadStream &stream) {
-    char buf[10];
-    stream.read(buf, 10);
-    _filename = buf;
-    stream.read(buf, 10);
-    _paletteFilename = buf;
+    readFilename(stream, _filename);
+    readFilename(stream, _paletteFilename);
     stream.skip(10);
     
     if (_paletteFilename.size()) {
diff --git a/engines/nancy/action/sliderpuzzle.cpp b/engines/nancy/action/sliderpuzzle.cpp
index 548cd29445..a36417c720 100644
--- a/engines/nancy/action/sliderpuzzle.cpp
+++ b/engines/nancy/action/sliderpuzzle.cpp
@@ -47,9 +47,7 @@ void SliderPuzzle::init() {
 }
 
 void SliderPuzzle::readData(Common::SeekableReadStream &stream) {
-    char buf[10];
-    stream.read(buf, 10);
-    _imageName = buf;
+    readFilename(stream, _imageName);
 
     _width = stream.readUint16LE();
     _height = stream.readUint16LE();
diff --git a/engines/nancy/action/staticbitmapanim.cpp b/engines/nancy/action/staticbitmapanim.cpp
index 1c4ba93446..512ff83e41 100644
--- a/engines/nancy/action/staticbitmapanim.cpp
+++ b/engines/nancy/action/staticbitmapanim.cpp
@@ -43,9 +43,7 @@ void PlayStaticBitmapAnimation::init() {
 }
 
 void PlayStaticBitmapAnimation::readData(Common::SeekableReadStream &stream) {
-    char name[10];
-    stream.read(name, 10);
-    _imageName = Common::String(name);
+    readFilename(stream, _imageName);
 
     stream.skip(0x2);
     _isTransparent = (NancyFlag)(stream.readUint16LE());
diff --git a/engines/nancy/action/telephone.cpp b/engines/nancy/action/telephone.cpp
index dafb17e299..c3c4d88a1f 100644
--- a/engines/nancy/action/telephone.cpp
+++ b/engines/nancy/action/telephone.cpp
@@ -48,9 +48,7 @@ void Telephone::init() {
 }
 
 void Telephone::readData(Common::SeekableReadStream &stream) {
-    char buf[10];
-    stream.read(buf, 10);
-    _imageName = buf;
+    readFilename(stream, _imageName);
 
     for (uint i = 0; i < 12; ++i) {
         _srcRects.push_back(Common::Rect());
@@ -76,15 +74,18 @@ void Telephone::readData(Common::SeekableReadStream &stream) {
     _hangUpSound.read(stream, SoundDescription::kNormal);
 
     for (uint i = 0; i < 12; ++i) {
-        stream.read(buf, 10);
-        _buttonSoundNames.push_back(buf);
+        Common::String buttonSoundName;
+        readFilename(stream, buttonSoundName);
+        _buttonSoundNames.push_back(buttonSoundName);
     }
 
-    char buf2[200];
-    stream.read(buf2, 200);
-    _addressBookString = buf2;
-    stream.read(buf2, 200);
-    _dialAgainString = buf2;
+    char textBuf[200];
+    stream.read(textBuf, 200);
+    textBuf[199] = '\0';
+    _addressBookString = textBuf;
+    stream.read(textBuf, 200);
+    textBuf[199] = '\0';
+    _dialAgainString = textBuf;
     _reloadScene.readData(stream);
     stream.skip(2);
     _flagOnReload.label = stream.readSint16LE();
@@ -104,11 +105,11 @@ void Telephone::readData(Common::SeekableReadStream &stream) {
         for (uint j = 0; j < 11; ++j) {
             call.phoneNumber.push_back(stream.readByte());
         }
-        
-        stream.read(buf, 10);
-        call.soundName = buf;
-        stream.read(buf2, 200);
-        call.text = buf2;
+
+        readFilename(stream, call.soundName);
+        stream.read(textBuf, 200);
+        textBuf[199] = '\0';
+        call.text = textBuf;
         call._sceneChange.readData(stream);
         stream.skip(2);
         call.flag.label = stream.readSint16LE();
diff --git a/engines/nancy/commontypes.cpp b/engines/nancy/commontypes.cpp
index 0c4227af95..3cb2a767dc 100644
--- a/engines/nancy/commontypes.cpp
+++ b/engines/nancy/commontypes.cpp
@@ -69,10 +69,7 @@ void SecondaryVideoDescription::readData(Common::SeekableReadStream &stream) {
 }
 
 void SoundDescription::read(Common::SeekableReadStream &stream, Type type) {
-	char buf[10];
-
-	stream.read(buf, 10);
-	name = buf;
+	readFilename(stream, name);
 
 	if (type == SoundDescription::kScene) {
 		stream.skip(4);
diff --git a/engines/nancy/font.cpp b/engines/nancy/font.cpp
index 8fed0c995c..b630f10fef 100644
--- a/engines/nancy/font.cpp
+++ b/engines/nancy/font.cpp
@@ -34,14 +34,14 @@ void Font::read(Common::SeekableReadStream &stream) {
     _maxCharWidth = 0;
     _fontHeight = 0;
 
-    char name[10];
-    stream.read(name, 10);
-    Common::String imageName = name;
+    Common::String imageName;
+    readFilename(stream, imageName);
 
-    g_nancy->_resource->loadImage(name, _image);
+    g_nancy->_resource->loadImage(imageName, _image);
 
     char desc[0x20];
     stream.read(desc, 0x20);
+    desc[0x1F] = '\0';
     _description = desc;
     stream.skip(8);
     _colorCoordsOffset.x = stream.readUint16LE();
diff --git a/engines/nancy/state/credits.cpp b/engines/nancy/state/credits.cpp
index d8f4077e7e..588515f2dc 100644
--- a/engines/nancy/state/credits.cpp
+++ b/engines/nancy/state/credits.cpp
@@ -54,11 +54,12 @@ void Credits::init() {
     Common::SeekableReadStream *cred = g_nancy->getBootChunkStream("CRED");
     cred->seek(0);
 
-    char buf[10];
-    cred->read(buf, 10);
-    _background.init(buf);
+    Common::String imageName;
+    readFilename(*cred, imageName);
+    _background.init(imageName);
 
-    cred->read(buf, 10);
+    readFilename(*cred, imageName);
+    
     cred->skip(0x20); // Skip the src and dest rectangles
     readRect(*cred, _text._screenPosition);
     cred->skip(0x10);
@@ -66,7 +67,7 @@ void Credits::init() {
     _pixelsToScroll = cred->readUint16LE();
     _sound.read(*cred, SoundDescription::kMenu);
 
-    g_nancy->_resource->loadImage(buf, _fullTextSurface);
+    g_nancy->_resource->loadImage(imageName, _fullTextSurface);
     
     Common::Rect src = _text._screenPosition;
     src.moveTo(Common::Point());
diff --git a/engines/nancy/state/help.cpp b/engines/nancy/state/help.cpp
index 5795814fbd..249c885c5a 100644
--- a/engines/nancy/state/help.cpp
+++ b/engines/nancy/state/help.cpp
@@ -27,6 +27,7 @@
 #include "engines/nancy/sound.h"
 #include "engines/nancy/input.h"
 #include "engines/nancy/cursor.h"
+#include "engines/nancy/util.h"
 
 #include "common/stream.h"
 
@@ -58,9 +59,9 @@ void Help::init() {
     Common::SeekableReadStream *chunk = g_nancy->getBootChunkStream("HELP");
 
     chunk->seek(0);
-    char buf[10];
-    chunk->read(buf, 10);
-    _image.init(buf);
+    Common::String imageName;
+    readFilename(*chunk, imageName);
+    _image.init(imageName);
 
     chunk->skip(20);
     _hotspot.left = chunk->readUint16LE();
diff --git a/engines/nancy/state/map.cpp b/engines/nancy/state/map.cpp
index 55632b5416..6b26a17f7d 100644
--- a/engines/nancy/state/map.cpp
+++ b/engines/nancy/state/map.cpp
@@ -69,11 +69,10 @@ void Map::init() {
 
     // Load the video
     chunk->seek(_mapID * 10, SEEK_SET);
-    char name[10];
-    chunk->read(name, 10);
-    Common::String n(name);
+    Common::String videoName;
+    readFilename(*chunk, videoName);
 
-    _viewport.loadVideo(n, 0, 0);
+    _viewport.loadVideo(videoName, 0, 0);
     _viewport.setEdgesSize(0, 0, 0, 0);
 
     // Load the audio
diff --git a/engines/nancy/ui/inventorybox.cpp b/engines/nancy/ui/inventorybox.cpp
index 5636047b82..5a8c3cc7cc 100644
--- a/engines/nancy/ui/inventorybox.cpp
+++ b/engines/nancy/ui/inventorybox.cpp
@@ -54,11 +54,9 @@ void InventoryBox::init() {
     readRect(stream, _screenPosition);
     _shadesFrameTime = stream.readUint16LE();
 
-    char name[10];
-    stream.read(name, 10);
-    Common::String inventoryBoxIconsImageName = Common::String(name);
-    stream.read(name, 10);
-    _inventoryCursorsImageName = Common::String(name);
+    Common::String inventoryBoxIconsImageName;
+    readFilename(stream, inventoryBoxIconsImageName);
+    readFilename(stream, _inventoryCursorsImageName);
 
     stream.skip(8);
     readRect(stream, _emptySpace);
@@ -67,6 +65,7 @@ void InventoryBox::init() {
 
     for (uint i = 0; i < 11; ++i) {
         stream.read(itemName, 0x14);
+        itemName[0x13] = '\0';
         _itemDescriptions[i].name = Common::String(itemName);
         _itemDescriptions[i].oneTimeUse = stream.readUint16LE();
         readRect(stream, _itemDescriptions[i].sourceRect);
diff --git a/engines/nancy/util.h b/engines/nancy/util.h
index c1bc0711dc..a429fe2776 100644
--- a/engines/nancy/util.h
+++ b/engines/nancy/util.h
@@ -34,6 +34,14 @@ inline void readRect(Common::SeekableReadStream &stream, Common::Rect &inRect) {
     inRect.bottom = stream.readSint32LE();
 }
 
+// Reads an 8-character filename from a 10-character source
+inline void readFilename(Common::SeekableReadStream &stream, Common::String &inString) {
+    char buf[10];
+    stream.read(buf, 10);
+    buf[9] = '\0';
+    inString = buf;
+}
+
 } // End of namespace Nancy
 
 #endif // NANCY_UTIL_H


Commit: e9adea4c4e51c09226e0118ead352886a4dd3470
    https://github.com/scummvm/scummvm/commit/e9adea4c4e51c09226e0118ead352886a4dd3470
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Formatting fixes

Almost every file in the engine code was using spaces instead of tabs, which is now fixed. Also added some missing spaces in a couple of files.

Changed paths:
    engines/nancy/action/actionmanager.cpp
    engines/nancy/action/actionmanager.h
    engines/nancy/action/actionrecord.h
    engines/nancy/action/arfactory.cpp
    engines/nancy/action/leverpuzzle.cpp
    engines/nancy/action/leverpuzzle.h
    engines/nancy/action/orderingpuzzle.cpp
    engines/nancy/action/orderingpuzzle.h
    engines/nancy/action/passwordpuzzle.cpp
    engines/nancy/action/passwordpuzzle.h
    engines/nancy/action/primaryvideo.cpp
    engines/nancy/action/primaryvideo.h
    engines/nancy/action/recordtypes.cpp
    engines/nancy/action/recordtypes.h
    engines/nancy/action/responses.cpp
    engines/nancy/action/rotatinglockpuzzle.cpp
    engines/nancy/action/rotatinglockpuzzle.h
    engines/nancy/action/secondarymovie.cpp
    engines/nancy/action/secondarymovie.h
    engines/nancy/action/secondaryvideo.cpp
    engines/nancy/action/secondaryvideo.h
    engines/nancy/action/sliderpuzzle.cpp
    engines/nancy/action/sliderpuzzle.h
    engines/nancy/action/staticbitmapanim.cpp
    engines/nancy/action/staticbitmapanim.h
    engines/nancy/action/telephone.cpp
    engines/nancy/action/telephone.h
    engines/nancy/cheat.cpp
    engines/nancy/cheat.h
    engines/nancy/commontypes.cpp
    engines/nancy/commontypes.h
    engines/nancy/console.cpp
    engines/nancy/cursor.cpp
    engines/nancy/cursor.h
    engines/nancy/decompress.cpp
    engines/nancy/decompress.h
    engines/nancy/detection.cpp
    engines/nancy/font.cpp
    engines/nancy/font.h
    engines/nancy/graphics.cpp
    engines/nancy/graphics.h
    engines/nancy/input.cpp
    engines/nancy/input.h
    engines/nancy/metaengine.cpp
    engines/nancy/nancy.cpp
    engines/nancy/nancy.h
    engines/nancy/renderobject.cpp
    engines/nancy/renderobject.h
    engines/nancy/resource.cpp
    engines/nancy/sound.cpp
    engines/nancy/sound.h
    engines/nancy/state/credits.cpp
    engines/nancy/state/credits.h
    engines/nancy/state/help.cpp
    engines/nancy/state/help.h
    engines/nancy/state/logo.h
    engines/nancy/state/map.cpp
    engines/nancy/state/map.h
    engines/nancy/state/scene.cpp
    engines/nancy/state/scene.h
    engines/nancy/state/state.h
    engines/nancy/time.h
    engines/nancy/ui/button.cpp
    engines/nancy/ui/button.h
    engines/nancy/ui/fullscreenimage.cpp
    engines/nancy/ui/fullscreenimage.h
    engines/nancy/ui/inventorybox.cpp
    engines/nancy/ui/inventorybox.h
    engines/nancy/ui/scrollbar.cpp
    engines/nancy/ui/scrollbar.h
    engines/nancy/ui/textbox.cpp
    engines/nancy/ui/textbox.h
    engines/nancy/ui/viewport.cpp
    engines/nancy/ui/viewport.h
    engines/nancy/util.h
    engines/nancy/video.cpp


diff --git a/engines/nancy/action/actionmanager.cpp b/engines/nancy/action/actionmanager.cpp
index 0b34beac8c..eb284aa86f 100644
--- a/engines/nancy/action/actionmanager.cpp
+++ b/engines/nancy/action/actionmanager.cpp
@@ -40,426 +40,426 @@ namespace Nancy {
 namespace Action {
 
 void ActionManager::handleInput(NancyInput &input) {
-    for (auto &rec : _records) {
-        if (rec->_isActive) {
-            // Send input to all active records
-            rec->handleInput(input);
-        }
-
-        if (rec->_isActive && rec->_hasHotspot && NancySceneState.getViewport().convertViewportToScreen(rec->_hotspot).contains(input.mousePos)) {
-            g_nancy->_cursorManager->setCursorType(rec->getHoverCursor());
-
-            if (input.input & NancyInput::kLeftMouseButtonUp) {
-                input.input &= ~NancyInput::kLeftMouseButtonUp;
-
-                bool shouldTrigger = false;
-                int16 heldItem = NancySceneState.getHeldItem();
-                if (rec->_itemRequired != -1) {
-                    if (heldItem == -1 && rec->_itemRequired == -2) {
-                        shouldTrigger = true;
-                    } else {
-                        if (rec->_itemRequired <= 100) {
-                            if (heldItem == rec->_itemRequired) {
-                                shouldTrigger = true;
-                            }
-                        } else if (rec->_itemRequired <= 110 && rec->_itemRequired - 100 != heldItem) {
-                            // IDs 100 - 110 mean the record will activate when the object is _not_ the specified one
-                            shouldTrigger = true;
-                        }
-                    }
-
-                    if (!shouldTrigger) {
-                        g_nancy->_sound->playSound(17); // Hardcoded by original engine
-                    }
-                } else {
-                    shouldTrigger = true;
-                }
-                if (shouldTrigger) {
-                    rec->_state = ActionRecord::ExecutionState::kActionTrigger;
-                    
-                    if (rec->_itemRequired > 100 && rec->_itemRequired <= 110) {
-                        rec->_itemRequired -= 100;
-                    }
-
-                    // Re-add the object to the inventory unless it's marked as a one-time use
-                    if (rec->_itemRequired == heldItem && rec->_itemRequired != -1) {
-                        if (NancySceneState.getInventoryBox().getItemDescription(heldItem).oneTimeUse != 0) {
-                            NancySceneState.getInventoryBox().addItem(heldItem);
-                        }
-
-                        NancySceneState.setHeldItem(-1);
-                    }
-                }
-
-                break;
-            }
-        }
-    }
+	for (auto &rec : _records) {
+		if (rec->_isActive) {
+			// Send input to all active records
+			rec->handleInput(input);
+		}
+
+		if (rec->_isActive && rec->_hasHotspot && NancySceneState.getViewport().convertViewportToScreen(rec->_hotspot).contains(input.mousePos)) {
+			g_nancy->_cursorManager->setCursorType(rec->getHoverCursor());
+
+			if (input.input & NancyInput::kLeftMouseButtonUp) {
+				input.input &= ~NancyInput::kLeftMouseButtonUp;
+
+				bool shouldTrigger = false;
+				int16 heldItem = NancySceneState.getHeldItem();
+				if (rec->_itemRequired != -1) {
+					if (heldItem == -1 && rec->_itemRequired == -2) {
+						shouldTrigger = true;
+					} else {
+						if (rec->_itemRequired <= 100) {
+							if (heldItem == rec->_itemRequired) {
+								shouldTrigger = true;
+							}
+						} else if (rec->_itemRequired <= 110 && rec->_itemRequired - 100 != heldItem) {
+							// IDs 100 - 110 mean the record will activate when the object is _not_ the specified one
+							shouldTrigger = true;
+						}
+					}
+
+					if (!shouldTrigger) {
+						g_nancy->_sound->playSound(17); // Hardcoded by original engine
+					}
+				} else {
+					shouldTrigger = true;
+				}
+				if (shouldTrigger) {
+					rec->_state = ActionRecord::ExecutionState::kActionTrigger;
+
+					if (rec->_itemRequired > 100 && rec->_itemRequired <= 110) {
+						rec->_itemRequired -= 100;
+					}
+
+					// Re-add the object to the inventory unless it's marked as a one-time use
+					if (rec->_itemRequired == heldItem && rec->_itemRequired != -1) {
+						if (NancySceneState.getInventoryBox().getItemDescription(heldItem).oneTimeUse != 0) {
+							NancySceneState.getInventoryBox().addItem(heldItem);
+						}
+
+						NancySceneState.setHeldItem(-1);
+					}
+				}
+
+				break;
+			}
+		}
+	}
 }
 
 bool ActionManager::addNewActionRecord(Common::SeekableReadStream &inputData) {
-    inputData.seek(0x30);
-    byte ARType = inputData.readByte();
-    ActionRecord *newRecord = createActionRecord(ARType);
-
-    inputData.seek(0);
-    char descBuf[0x30];
-    inputData.read(descBuf, 0x30);
-    descBuf[0x2F] = '\0';
-    newRecord->_description = descBuf;
-
-    newRecord->_type = inputData.readByte(); // redundant
-    newRecord->_execType = (ActionRecord::ExecutionType)inputData.readByte();
-
-    uint16 localChunkSize = inputData.pos();
-    newRecord->readData(inputData);
-    localChunkSize = inputData.pos() - localChunkSize;
-    localChunkSize += 0x32;
-
-    // If the localChunkSize is less than the total data, there must be dependencies at the end of the chunk
-    uint16 depsDataSize = (uint16)inputData.size() - localChunkSize;
-    if (depsDataSize > 0) {
-        // Each dependency is 0x0C bytes long (in v1)
-        uint numDependencies = depsDataSize / 0xC;
-        if (depsDataSize % 0xC) {
-            error("Action record type %s has incorrect read size", newRecord->getRecordTypeName().c_str());;
-        }
-
-        // Initialize the dependencies data
-        inputData.seek(localChunkSize);
-        for (uint16 i = 0; i < numDependencies; ++i) {
-            newRecord->_dependencies.push_back(DependencyRecord());
-            DependencyRecord &dep = newRecord->_dependencies.back();
-
-            dep.type = (DependencyType)inputData.readByte();
-            dep.label = inputData.readByte();
-            dep.condition = inputData.readByte();
-            dep.orFlag = inputData.readByte();
-            dep.hours = inputData.readSint16LE();
-            dep.minutes = inputData.readSint16LE();
-            dep.seconds = inputData.readSint16LE();
-            dep.milliseconds = inputData.readSint16LE();
-
-            if (dep.type != kSceneCount || dep.hours != -1 || dep.minutes != -1 || dep.seconds != -1) {
-                dep.timeData = ((dep.hours * 60 + dep.minutes) * 60 + dep.seconds) * 1000 + dep.milliseconds;
-            }
-        }
-    } else {
-        // Set new record to active if it doesn't depend on anything
-        newRecord->_isActive = true;
-    }
-
-    _records.push_back(newRecord);
-
-    debugC(1, kDebugActionRecord, "Loaded action record %i, type %s, typeID %i, description \"%s\", execType == %s",
-            _records.size() - 1,
-            newRecord->getRecordTypeName().c_str(),
-            newRecord->_type,
-            newRecord->_description.c_str(),
-            newRecord->_execType == ActionRecord::kRepeating ? "kRepeating" : "kOneShot");
-    for (uint i = 0; i < newRecord->_dependencies.size(); ++i) {
-        debugCN(1, kDebugActionRecord, "\tDependency %i: type ", i);
-        switch (newRecord->_dependencies[i].type) {
-        case kNone : 
-            debugCN(1, kDebugActionRecord, "kNone");
-            break;
-        case kInventory :
-            debugCN(1, kDebugActionRecord, "kInventory, item ID %i %s",
-                        newRecord->_dependencies[i].label,
-                        newRecord->_dependencies[i].condition == kTrue ? "is in possession" : "is not in possession");
-            break;
-        case kEventFlag :
-            debugCN(1, kDebugActionRecord, "kEventFlag, flag ID %i == %s",
-                        newRecord->_dependencies[i].label,
-                        newRecord->_dependencies[i].condition == kTrue ? "true" : "false");
-            break;
-        case kLogicCondition :
-            debugCN(1, kDebugActionRecord, "kLogicCondition, logic condition ID %i == %s",
-                        newRecord->_dependencies[i].label,
-                        newRecord->_dependencies[i].condition == kTrue ? "true" : "false");
-            break;
-        case kTotalTime :
-            debugCN(1, kDebugActionRecord, "kTotalTime, %i hours, %i minutes, %i seconds, %i milliseconds",
-                        newRecord->_dependencies[i].hours,
-                        newRecord->_dependencies[i].minutes,
-                        newRecord->_dependencies[i].seconds,
-                        newRecord->_dependencies[i].milliseconds);
-            break;
-        case kSceneTime :
-            debugCN(1, kDebugActionRecord, "kSceneTime, %i hours, %i minutes, %i seconds, %i milliseconds",
-                        newRecord->_dependencies[i].hours,
-                        newRecord->_dependencies[i].minutes,
-                        newRecord->_dependencies[i].seconds,
-                        newRecord->_dependencies[i].milliseconds);
-            break;
-        case kPlayerTime :
-            debugCN(1, kDebugActionRecord, "kPlayerTime, %i days, %i hours, %i minutes, %i seconds",
-                        newRecord->_dependencies[i].hours,
-                        newRecord->_dependencies[i].minutes,
-                        newRecord->_dependencies[i].seconds,
-                        newRecord->_dependencies[i].milliseconds);
-            break;
-        case kSceneCount :
-            debugCN(1, kDebugActionRecord, "kSceneCount, scene ID %i, hit count %s %i",
-                        newRecord->_dependencies[i].hours,
-                        newRecord->_dependencies[i].milliseconds == 1 ? ">" : newRecord->_dependencies[i].milliseconds == 2 ? "<" : "==",
-                        newRecord->_dependencies[i].seconds);
-            break;
-        case kResetOnNewDay :
-            debugCN(1, kDebugActionRecord, "kResetOnNewDay");
-            break;
-        case kUseItem :
-            debugCN(1, kDebugActionRecord, "kUseItem, item ID %i %s",
-                        newRecord->_dependencies[i].label,
-                        newRecord->_dependencies[i].condition == kTrue ? "is held" : "is not held");
-            break;
-        case kTimeOfDay :
-            debugCN(1, kDebugActionRecord, "kTimeOfDay, %s",
-                        newRecord->_dependencies[i].label == 0 ? "day" : newRecord->_dependencies[i].label == 1 ? "night" : "dusk/dawn");
-            break;
-        case kTimerNotDone :
-            debugCN(1, kDebugActionRecord, "kTimerNotDone");
-            break;
-        case kTimerDone :
-            debugCN(1, kDebugActionRecord, "kTimerDone");
-            break;
-        case kDifficultyLevel :
-            debugCN(1, kDebugActionRecord, "kDifficultyLevel, level %i", newRecord->_dependencies[i].condition);
-            break;
-        default:
-            debugCN(1, kDebugActionRecord, "unknown");
-            break;
-        }
-        debugC(1, kDebugActionRecord, ", orFlag == %s", newRecord->_dependencies[i].orFlag == true ? "true" : "false");
-    }
-
-    return true;
+	inputData.seek(0x30);
+	byte ARType = inputData.readByte();
+	ActionRecord *newRecord = createActionRecord(ARType);
+
+	inputData.seek(0);
+	char descBuf[0x30];
+	inputData.read(descBuf, 0x30);
+	descBuf[0x2F] = '\0';
+	newRecord->_description = descBuf;
+
+	newRecord->_type = inputData.readByte(); // redundant
+	newRecord->_execType = (ActionRecord::ExecutionType)inputData.readByte();
+
+	uint16 localChunkSize = inputData.pos();
+	newRecord->readData(inputData);
+	localChunkSize = inputData.pos() - localChunkSize;
+	localChunkSize += 0x32;
+
+	// If the localChunkSize is less than the total data, there must be dependencies at the end of the chunk
+	uint16 depsDataSize = (uint16)inputData.size() - localChunkSize;
+	if (depsDataSize > 0) {
+		// Each dependency is 0x0C bytes long (in v1)
+		uint numDependencies = depsDataSize / 0xC;
+		if (depsDataSize % 0xC) {
+			error("Action record type %s has incorrect read size", newRecord->getRecordTypeName().c_str());;
+		}
+
+		// Initialize the dependencies data
+		inputData.seek(localChunkSize);
+		for (uint16 i = 0; i < numDependencies; ++i) {
+			newRecord->_dependencies.push_back(DependencyRecord());
+			DependencyRecord &dep = newRecord->_dependencies.back();
+
+			dep.type = (DependencyType)inputData.readByte();
+			dep.label = inputData.readByte();
+			dep.condition = inputData.readByte();
+			dep.orFlag = inputData.readByte();
+			dep.hours = inputData.readSint16LE();
+			dep.minutes = inputData.readSint16LE();
+			dep.seconds = inputData.readSint16LE();
+			dep.milliseconds = inputData.readSint16LE();
+
+			if (dep.type != kSceneCount || dep.hours != -1 || dep.minutes != -1 || dep.seconds != -1) {
+				dep.timeData = ((dep.hours * 60 + dep.minutes) * 60 + dep.seconds) * 1000 + dep.milliseconds;
+			}
+		}
+	} else {
+		// Set new record to active if it doesn't depend on anything
+		newRecord->_isActive = true;
+	}
+
+	_records.push_back(newRecord);
+
+	debugC(1, kDebugActionRecord, "Loaded action record %i, type %s, typeID %i, description \"%s\", execType == %s",
+			_records.size() - 1,
+			newRecord->getRecordTypeName().c_str(),
+			newRecord->_type,
+			newRecord->_description.c_str(),
+			newRecord->_execType == ActionRecord::kRepeating ? "kRepeating" : "kOneShot");
+	for (uint i = 0; i < newRecord->_dependencies.size(); ++i) {
+		debugCN(1, kDebugActionRecord, "\tDependency %i: type ", i);
+		switch (newRecord->_dependencies[i].type) {
+		case kNone :
+			debugCN(1, kDebugActionRecord, "kNone");
+			break;
+		case kInventory :
+			debugCN(1, kDebugActionRecord, "kInventory, item ID %i %s",
+						newRecord->_dependencies[i].label,
+						newRecord->_dependencies[i].condition == kTrue ? "is in possession" : "is not in possession");
+			break;
+		case kEventFlag :
+			debugCN(1, kDebugActionRecord, "kEventFlag, flag ID %i == %s",
+						newRecord->_dependencies[i].label,
+						newRecord->_dependencies[i].condition == kTrue ? "true" : "false");
+			break;
+		case kLogicCondition :
+			debugCN(1, kDebugActionRecord, "kLogicCondition, logic condition ID %i == %s",
+						newRecord->_dependencies[i].label,
+						newRecord->_dependencies[i].condition == kTrue ? "true" : "false");
+			break;
+		case kTotalTime :
+			debugCN(1, kDebugActionRecord, "kTotalTime, %i hours, %i minutes, %i seconds, %i milliseconds",
+						newRecord->_dependencies[i].hours,
+						newRecord->_dependencies[i].minutes,
+						newRecord->_dependencies[i].seconds,
+						newRecord->_dependencies[i].milliseconds);
+			break;
+		case kSceneTime :
+			debugCN(1, kDebugActionRecord, "kSceneTime, %i hours, %i minutes, %i seconds, %i milliseconds",
+						newRecord->_dependencies[i].hours,
+						newRecord->_dependencies[i].minutes,
+						newRecord->_dependencies[i].seconds,
+						newRecord->_dependencies[i].milliseconds);
+			break;
+		case kPlayerTime :
+			debugCN(1, kDebugActionRecord, "kPlayerTime, %i days, %i hours, %i minutes, %i seconds",
+						newRecord->_dependencies[i].hours,
+						newRecord->_dependencies[i].minutes,
+						newRecord->_dependencies[i].seconds,
+						newRecord->_dependencies[i].milliseconds);
+			break;
+		case kSceneCount :
+			debugCN(1, kDebugActionRecord, "kSceneCount, scene ID %i, hit count %s %i",
+						newRecord->_dependencies[i].hours,
+						newRecord->_dependencies[i].milliseconds == 1 ? ">" : newRecord->_dependencies[i].milliseconds == 2 ? "<" : "==",
+						newRecord->_dependencies[i].seconds);
+			break;
+		case kResetOnNewDay :
+			debugCN(1, kDebugActionRecord, "kResetOnNewDay");
+			break;
+		case kUseItem :
+			debugCN(1, kDebugActionRecord, "kUseItem, item ID %i %s",
+						newRecord->_dependencies[i].label,
+						newRecord->_dependencies[i].condition == kTrue ? "is held" : "is not held");
+			break;
+		case kTimeOfDay :
+			debugCN(1, kDebugActionRecord, "kTimeOfDay, %s",
+						newRecord->_dependencies[i].label == 0 ? "day" : newRecord->_dependencies[i].label == 1 ? "night" : "dusk/dawn");
+			break;
+		case kTimerNotDone :
+			debugCN(1, kDebugActionRecord, "kTimerNotDone");
+			break;
+		case kTimerDone :
+			debugCN(1, kDebugActionRecord, "kTimerDone");
+			break;
+		case kDifficultyLevel :
+			debugCN(1, kDebugActionRecord, "kDifficultyLevel, level %i", newRecord->_dependencies[i].condition);
+			break;
+		default:
+			debugCN(1, kDebugActionRecord, "unknown");
+			break;
+		}
+		debugC(1, kDebugActionRecord, ", orFlag == %s", newRecord->_dependencies[i].orFlag == true ? "true" : "false");
+	}
+
+	return true;
 }
 
-void ActionManager::processActionRecords() {    
-    for (auto record : _records) {
-        if (record->_isDone) {
-            continue;
-        }
-
-        if (!record->_isActive) {
-            for (uint i = 0; i < record->_dependencies.size(); ++i) {
-                DependencyRecord &dep = record->_dependencies[i];
-
-                if (!dep.satisfied) {
-                    switch (dep.type) {
-                    case kNone:
-                        dep.satisfied = true;
-                        break;
-                    case kInventory:
-                        switch (dep.condition) {
-                        case kFalse:
-                            // Item not in possession or held
-                            if (NancySceneState._flags.items[dep.label] == kFalse &&
-                                dep.label != NancySceneState._flags.heldItem) {
-                                dep.satisfied = true;
-                            }
-
-                            break;
-                        case kTrue:
-                            if (NancySceneState._flags.items[dep.label] == kTrue ||
-                                dep.label == NancySceneState._flags.heldItem) {
-                                dep.satisfied = true;
-                            }
-
-                            break;
-                        default:
-                            break;
-                        }
-
-                        break;
-                    case kEventFlag:
-                        if (NancySceneState.getEventFlag(dep.label, (NancyFlag)dep.condition)) {
-                            // nancy1 has code for some timer array that never gets used
-                            // and is discarded from nancy2 onward
-                            dep.satisfied = true;
-                        }
-
-                        break;
-                    case kLogicCondition:
-                        if (NancySceneState._flags.logicConditions[dep.label].flag == dep.condition) {
-                            // Wait for specified time before satisfying dependency condition
-                            Time elapsed = NancySceneState._timers.lastTotalTime - NancySceneState._flags.logicConditions[dep.label].timestamp;
-
-                            if (elapsed >= dep.timeData) {
-                                dep.satisfied = true;
-                            }
-                        }
-
-                        break;
-                    case kTotalTime:
-                        if (NancySceneState._timers.lastTotalTime >= dep.timeData) {
-                            dep.satisfied = true;
-                        }
-
-                        break;
-                    case kSceneTime:
-                        if (NancySceneState._timers.sceneTime >= dep.timeData) {
-                            dep.satisfied = true;
-                        }
-
-                        break;
-                    case kPlayerTime:
-                        // TODO almost definitely wrong, as the original engine treats player time differently
-                        if (NancySceneState._timers.playerTime >= dep.timeData) {
-                            dep.satisfied = true;
-                        }
-
-                        break;
-                    /*case 7:
-                        // TODO
-                        break;
-                    case 8:
-                        // TODO
-                        break;*/
-                    case kSceneCount:
-                        // This dependency type keeps its data in the time variables
-                        // Also, I'm pretty sure it never gets used
-                        switch (dep.milliseconds) {
-                        case 1:
-                            if (dep.seconds < NancySceneState._flags.sceneHitCount[dep.hours]) {
-                                dep.satisfied = true;
-                            }
-
-                            break;
-                        case 2:
-                            if (dep.seconds > NancySceneState._flags.sceneHitCount[dep.hours]) {
-                                dep.satisfied = true;
-                            }
-
-                            break;
-                        case 3:
-                            if (dep.seconds == NancySceneState._flags.sceneHitCount[dep.hours]) {
-                                dep.satisfied = true;
-                            }
-
-                            break;
-                        }
-
-                        break;
-                    case kResetOnNewDay:
-                        if (record->_days == -1) {
-                            record->_days = NancySceneState._timers.playerTime.getDays();
-                            dep.satisfied = true;
-                            break;
-                        }
-
-                        if (record->_days < NancySceneState._timers.playerTime.getDays()) {
-                            record->_days = NancySceneState._timers.playerTime.getDays();
-                            for (uint j = 0; j < record->_dependencies.size(); ++j) {
-                                if (record->_dependencies[j].type == kPlayerTime) {
-                                    record->_dependencies[j].satisfied = false;
-                                }
-                            }
-                        }
-
-                        break;
-                    case kUseItem: {
-                        bool hasUnsatisfiedDeps = false;
-                        for (uint j = 0; j < record->_dependencies.size(); ++j) {
-                            if (j != i && record->_dependencies[j].satisfied == false) {
-                                hasUnsatisfiedDeps = true;
-                            }
-                        }
-
-                        if (hasUnsatisfiedDeps) {
-                            break;
-                        }
-
-                        record->_itemRequired = dep.label;
-
-                        if (dep.condition == 1) {
-                            record->_itemRequired += 100;
-                        }
-                        
-                        dep.satisfied = true;
-                        break;
-                    }
-                    case kTimeOfDay:
-                        if (dep.label == (byte)NancySceneState._timers.timeOfDay) {
-                            dep.satisfied = true;
-                        }
-
-                        break;
-                    case kTimerNotDone:
-                        if (NancySceneState._timers.timerTime <= dep.timeData) {
-                            dep.satisfied = true;
-                        }
-
-                        break;
-                    case kTimerDone:
-                        if (NancySceneState._timers.timerTime > dep.timeData) {
-                            dep.satisfied = true;
-                        }
-
-                        break;
-                    case kDifficultyLevel:
-                        if (dep.condition == NancySceneState._difficulty) {
-                            dep.satisfied = true;
-                        }
-
-                        break;
-                    default:
-                        break;
-                    }
-                }
-            }
-
-            // An orFlag marks that its corresponding dependency and the one after it
-            // mutually satisfy each other; if one is satisfied, so is the other
-            for (uint i = 1; i < record->_dependencies.size(); ++i) {
-                if (record->_dependencies[i-1].orFlag) {
-                    if (record->_dependencies[i-1].satisfied)
-                        record->_dependencies[i].satisfied = true;
-                    if (record->_dependencies[i].satisfied)
-                        record->_dependencies[i-1].satisfied = true;
-                }
-            }
-
-            // Check if all dependencies have been satisfied, and activate the record if they have
-            uint satisfied = 0;
-            for (uint i = 0; i < record->_dependencies.size(); ++i) {
-                if (record->_dependencies[i].satisfied)
-                    ++satisfied;
-            }
-
-            if (satisfied == record->_dependencies.size())
-                record->_isActive = true;
-        
-        }
-
-        if (record->_isActive) {
-            record->execute();
-        }
-    }
+void ActionManager::processActionRecords() {
+	for (auto record : _records) {
+		if (record->_isDone) {
+			continue;
+		}
+
+		if (!record->_isActive) {
+			for (uint i = 0; i < record->_dependencies.size(); ++i) {
+				DependencyRecord &dep = record->_dependencies[i];
+
+				if (!dep.satisfied) {
+					switch (dep.type) {
+					case kNone:
+						dep.satisfied = true;
+						break;
+					case kInventory:
+						switch (dep.condition) {
+						case kFalse:
+							// Item not in possession or held
+							if (NancySceneState._flags.items[dep.label] == kFalse &&
+								dep.label != NancySceneState._flags.heldItem) {
+								dep.satisfied = true;
+							}
+
+							break;
+						case kTrue:
+							if (NancySceneState._flags.items[dep.label] == kTrue ||
+								dep.label == NancySceneState._flags.heldItem) {
+								dep.satisfied = true;
+							}
+
+							break;
+						default:
+							break;
+						}
+
+						break;
+					case kEventFlag:
+						if (NancySceneState.getEventFlag(dep.label, (NancyFlag)dep.condition)) {
+							// nancy1 has code for some timer array that never gets used
+							// and is discarded from nancy2 onward
+							dep.satisfied = true;
+						}
+
+						break;
+					case kLogicCondition:
+						if (NancySceneState._flags.logicConditions[dep.label].flag == dep.condition) {
+							// Wait for specified time before satisfying dependency condition
+							Time elapsed = NancySceneState._timers.lastTotalTime - NancySceneState._flags.logicConditions[dep.label].timestamp;
+
+							if (elapsed >= dep.timeData) {
+								dep.satisfied = true;
+							}
+						}
+
+						break;
+					case kTotalTime:
+						if (NancySceneState._timers.lastTotalTime >= dep.timeData) {
+							dep.satisfied = true;
+						}
+
+						break;
+					case kSceneTime:
+						if (NancySceneState._timers.sceneTime >= dep.timeData) {
+							dep.satisfied = true;
+						}
+
+						break;
+					case kPlayerTime:
+						// TODO almost definitely wrong, as the original engine treats player time differently
+						if (NancySceneState._timers.playerTime >= dep.timeData) {
+							dep.satisfied = true;
+						}
+
+						break;
+					/*case 7:
+						// TODO
+						break;
+					case 8:
+						// TODO
+						break;*/
+					case kSceneCount:
+						// This dependency type keeps its data in the time variables
+						// Also, I'm pretty sure it never gets used
+						switch (dep.milliseconds) {
+						case 1:
+							if (dep.seconds < NancySceneState._flags.sceneHitCount[dep.hours]) {
+								dep.satisfied = true;
+							}
+
+							break;
+						case 2:
+							if (dep.seconds > NancySceneState._flags.sceneHitCount[dep.hours]) {
+								dep.satisfied = true;
+							}
+
+							break;
+						case 3:
+							if (dep.seconds == NancySceneState._flags.sceneHitCount[dep.hours]) {
+								dep.satisfied = true;
+							}
+
+							break;
+						}
+
+						break;
+					case kResetOnNewDay:
+						if (record->_days == -1) {
+							record->_days = NancySceneState._timers.playerTime.getDays();
+							dep.satisfied = true;
+							break;
+						}
+
+						if (record->_days < NancySceneState._timers.playerTime.getDays()) {
+							record->_days = NancySceneState._timers.playerTime.getDays();
+							for (uint j = 0; j < record->_dependencies.size(); ++j) {
+								if (record->_dependencies[j].type == kPlayerTime) {
+									record->_dependencies[j].satisfied = false;
+								}
+							}
+						}
+
+						break;
+					case kUseItem: {
+						bool hasUnsatisfiedDeps = false;
+						for (uint j = 0; j < record->_dependencies.size(); ++j) {
+							if (j != i && record->_dependencies[j].satisfied == false) {
+								hasUnsatisfiedDeps = true;
+							}
+						}
+
+						if (hasUnsatisfiedDeps) {
+							break;
+						}
+
+						record->_itemRequired = dep.label;
+
+						if (dep.condition == 1) {
+							record->_itemRequired += 100;
+						}
+
+						dep.satisfied = true;
+						break;
+					}
+					case kTimeOfDay:
+						if (dep.label == (byte)NancySceneState._timers.timeOfDay) {
+							dep.satisfied = true;
+						}
+
+						break;
+					case kTimerNotDone:
+						if (NancySceneState._timers.timerTime <= dep.timeData) {
+							dep.satisfied = true;
+						}
+
+						break;
+					case kTimerDone:
+						if (NancySceneState._timers.timerTime > dep.timeData) {
+							dep.satisfied = true;
+						}
+
+						break;
+					case kDifficultyLevel:
+						if (dep.condition == NancySceneState._difficulty) {
+							dep.satisfied = true;
+						}
+
+						break;
+					default:
+						break;
+					}
+				}
+			}
+
+			// An orFlag marks that its corresponding dependency and the one after it
+			// mutually satisfy each other; if one is satisfied, so is the other
+			for (uint i = 1; i < record->_dependencies.size(); ++i) {
+				if (record->_dependencies[i - 1].orFlag) {
+					if (record->_dependencies[i - 1].satisfied)
+						record->_dependencies[i].satisfied = true;
+					if (record->_dependencies[i].satisfied)
+						record->_dependencies[i - 1].satisfied = true;
+				}
+			}
+
+			// Check if all dependencies have been satisfied, and activate the record if they have
+			uint satisfied = 0;
+			for (uint i = 0; i < record->_dependencies.size(); ++i) {
+				if (record->_dependencies[i].satisfied)
+					++satisfied;
+			}
+
+			if (satisfied == record->_dependencies.size())
+				record->_isActive = true;
+
+		}
+
+		if (record->_isActive) {
+			record->execute();
+		}
+	}
 }
 
 void ActionManager::clearActionRecords() {
-    for (auto &r : _records) {
-        delete r;
-    }
-    _records.clear();
+	for (auto &r : _records) {
+		delete r;
+	}
+	_records.clear();
 }
 
 void ActionManager::onPause(bool pause) {
-    for (auto &r : _records) {
-        if (r->_isActive && !r->_isDone) {
-            r->onPause(pause);
-        }
-    }
+	for (auto &r : _records) {
+		if (r->_isActive && !r->_isDone) {
+			r->onPause(pause);
+		}
+	}
 }
 
 void ActionManager::synchronize(Common::Serializer &ser) {
-    // When loading, the records should already have been initialized by scene
-    for (auto &rec : _records) {
-        ser.syncAsByte(rec->_isActive);
-        ser.syncAsByte(rec->_isDone);
-    }
+	// When loading, the records should already have been initialized by scene
+	for (auto &rec : _records) {
+		ser.syncAsByte(rec->_isActive);
+		ser.syncAsByte(rec->_isDone);
+	}
 }
 
 } // End of namespace Action
diff --git a/engines/nancy/action/actionmanager.h b/engines/nancy/action/actionmanager.h
index 7101ffb6f1..fcf16e5ed6 100644
--- a/engines/nancy/action/actionmanager.h
+++ b/engines/nancy/action/actionmanager.h
@@ -47,28 +47,28 @@ namespace Action {
 
 // The class that handles ActionRecords and their execution
 class ActionManager {
-    friend class Nancy::State::Scene;
+	friend class Nancy::State::Scene;
 
 public:
-    ActionManager() {}
-    virtual ~ActionManager() {}
+	ActionManager() {}
+	virtual ~ActionManager() {}
 
-    void handleInput(NancyInput &input);
+	void handleInput(NancyInput &input);
 
-    void processActionRecords();
-    bool addNewActionRecord(Common::SeekableReadStream &inputData);
-    Common::Array<ActionRecord *> &getActionRecords() { return _records; }
-    ActionRecord *getActionRecord(uint id) { if (id < _records.size()) return _records[id]; else return nullptr;}
-    void clearActionRecords();
+	void processActionRecords();
+	bool addNewActionRecord(Common::SeekableReadStream &inputData);
+	Common::Array<ActionRecord *> &getActionRecords() { return _records; }
+	ActionRecord *getActionRecord(uint id) { if (id < _records.size()) return _records[id]; else return nullptr;}
+	void clearActionRecords();
 
-    void onPause(bool pause);
+	void onPause(bool pause);
 
-    void synchronize(Common::Serializer &serializer);
+	void synchronize(Common::Serializer &serializer);
 
 protected:
-    virtual ActionRecord *createActionRecord(uint16 type);
+	virtual ActionRecord *createActionRecord(uint16 type);
 
-    Common::Array<ActionRecord *> _records;
+	Common::Array<ActionRecord *> _records;
 };
 
 } // End of namespace Action
diff --git a/engines/nancy/action/actionrecord.h b/engines/nancy/action/actionrecord.h
index f7e801a19e..fbaec75919 100644
--- a/engines/nancy/action/actionrecord.h
+++ b/engines/nancy/action/actionrecord.h
@@ -37,39 +37,39 @@ namespace Nancy {
 class NancyEngine;
 
 namespace Action {
-    
+
 enum DependencyType : byte {
-    kNone               = 0,
-    kInventory          = 1,
-    kEventFlag          = 2,
-    kLogicCondition     = 3,
-    kTotalTime          = 4,
-    kSceneTime          = 5,
-    kPlayerTime         = 6,
-    // ...
-    kSceneCount         = 9,
-    kResetOnNewDay      = 10,
-    kUseItem            = 11,
-    kTimeOfDay          = 12,
-    kTimerNotDone       = 13,
-    kTimerDone          = 14,
-    kDifficultyLevel    = 15
+	kNone               = 0,
+	kInventory          = 1,
+	kEventFlag          = 2,
+	kLogicCondition     = 3,
+	kTotalTime          = 4,
+	kSceneTime          = 5,
+	kPlayerTime         = 6,
+	// ...
+	kSceneCount         = 9,
+	kResetOnNewDay      = 10,
+	kUseItem            = 11,
+	kTimeOfDay          = 12,
+	kTimerNotDone       = 13,
+	kTimerDone          = 14,
+	kDifficultyLevel    = 15
 };
 
 // Describes a condition that needs to be fulfilled before the
 // action record can be executed
 struct DependencyRecord {
-    DependencyType type;    // 0x00
-    byte label;             // 0x01
-    byte condition;         // 0x02
-    bool orFlag;            // 0x03
-    int16 hours;            // 0x04
-    int16 minutes;          // 0x06
-    int16 seconds;          // 0x08
-    int16 milliseconds;     // 0x0A
-
-    bool satisfied;
-    Time timeData;
+	DependencyType type;    // 0x00
+	byte label;             // 0x01
+	byte condition;         // 0x02
+	bool orFlag;            // 0x03
+	int16 hours;            // 0x04
+	int16 minutes;          // 0x06
+	int16 seconds;          // 0x08
+	int16 milliseconds;     // 0x0A
+
+	bool satisfied;
+	Time timeData;
 };
 
 // Describes a single action that will be performed on every update.
@@ -78,71 +78,71 @@ struct DependencyRecord {
 // Does _not_ support drawing to screen, records that need this functionality
 // will have to also subclass RenderObject.
 class ActionRecord {
-    friend class ActionManager;
+	friend class ActionManager;
 public:
-    enum ExecutionState { kBegin, kRun, kActionTrigger };
-    enum ExecutionType { kOneShot = 1, kRepeating = 2 };
-    ActionRecord() :
-        _type(0),
-        _execType(kOneShot),
-        _isActive(false),
-        _isDone(false),
-        _hasHotspot(false),
-        _state(ExecutionState::kBegin),
-        _days(-1),
-        _itemRequired(-1) {}
-    virtual ~ActionRecord() {}
-
-    virtual void readData(Common::SeekableReadStream &stream) =0;
-    virtual void execute() {}
-    virtual void onPause(bool pause) {}
-
-    virtual CursorManager::CursorType getHoverCursor() const { return CursorManager::kHotspot; }
-    virtual void handleInput(NancyInput &input) {}
-
-protected:   
-    void finishExecution() {
-        switch (_execType) {
-        case kOneShot:
-            _isDone = true;
-            _state = kBegin;
-            break;
-        case kRepeating:
-            _isDone = false;
-            _isActive = false;
-            _state = kBegin;
-
-            for (uint i = 0; i < _dependencies.size(); ++i) {
-                _dependencies[i].satisfied = false;
-            }
-
-            break;
-        default:
-            _state = kBegin;
-            break;
-        }
-    }
-
-    // Used for debugging
-    virtual Common::String getRecordTypeName() const =0;
+	enum ExecutionState { kBegin, kRun, kActionTrigger };
+	enum ExecutionType { kOneShot = 1, kRepeating = 2 };
+	ActionRecord() :
+		_type(0),
+		_execType(kOneShot),
+		_isActive(false),
+		_isDone(false),
+		_hasHotspot(false),
+		_state(ExecutionState::kBegin),
+		_days(-1),
+		_itemRequired(-1) {}
+	virtual ~ActionRecord() {}
+
+	virtual void readData(Common::SeekableReadStream &stream) =0;
+	virtual void execute() {}
+	virtual void onPause(bool pause) {}
+
+	virtual CursorManager::CursorType getHoverCursor() const { return CursorManager::kHotspot; }
+	virtual void handleInput(NancyInput &input) {}
+
+protected:
+	void finishExecution() {
+		switch (_execType) {
+		case kOneShot:
+			_isDone = true;
+			_state = kBegin;
+			break;
+		case kRepeating:
+			_isDone = false;
+			_isActive = false;
+			_state = kBegin;
+
+			for (uint i = 0; i < _dependencies.size(); ++i) {
+				_dependencies[i].satisfied = false;
+			}
+
+			break;
+		default:
+			_state = kBegin;
+			break;
+		}
+	}
+
+	// Used for debugging
+	virtual Common::String getRecordTypeName() const =0;
 
 public:
-    Common::String _description;                    // 0x00
-    byte _type;                                     // 0x30
-    ExecutionType _execType;                        // 0x31
-    // 0x32 data
-    Common::Array<DependencyRecord> _dependencies;  // 0x36
-    // 0x3A numDependencies
-    bool _isActive;                                 // 0x3B
-    // 0x3C satisfiedDependencies[] 
-    // 0x48 timers[]
-    // 0x78 orFlags[]
-    bool _isDone;                                   // 0x84
-    bool _hasHotspot;                               // 0x85
-    Common::Rect _hotspot;                          // 0x89
-    ExecutionState _state;                          // 0x91
-    int16 _days;                                    // 0x95
-    int8 _itemRequired;                             // 0x97
+	Common::String _description;                    // 0x00
+	byte _type;                                     // 0x30
+	ExecutionType _execType;                        // 0x31
+	// 0x32 data
+	Common::Array<DependencyRecord> _dependencies;  // 0x36
+	// 0x3A numDependencies
+	bool _isActive;                                 // 0x3B
+	// 0x3C satisfiedDependencies[]
+	// 0x48 timers[]
+	// 0x78 orFlags[]
+	bool _isDone;                                   // 0x84
+	bool _hasHotspot;                               // 0x85
+	Common::Rect _hotspot;                          // 0x89
+	ExecutionState _state;                          // 0x91
+	int16 _days;                                    // 0x95
+	int8 _itemRequired;                             // 0x97
 };
 
 } // End of namespace Action
diff --git a/engines/nancy/action/arfactory.cpp b/engines/nancy/action/arfactory.cpp
index 8a34dbc676..4ce7848144 100644
--- a/engines/nancy/action/arfactory.cpp
+++ b/engines/nancy/action/arfactory.cpp
@@ -43,112 +43,112 @@ namespace Action {
 
 // TODO put this function in a subclass
 ActionRecord *ActionManager::createActionRecord(uint16 type) {
-    type -= 0xA;
-    switch (type) {
-    case 0x00:
-        return new Hot1FrSceneChange();
-    case 0x01:
-        return new HotMultiframeSceneChange();
-    case 0x02:
-        return new SceneChange();
-    case 0x03:
-        return new HotMultiframeMultisceneChange();
-    case 0x04:
-        return new Hot1FrExitSceneChange();
-    case 0x0C:
-        return new StartFrameNextScene();
-    case 0x14:
-        return new StartStopPlayerScrolling(); // TODO
-    case 0x15:
-        return new StartStopPlayerScrolling(); // TODO
-    case 0x28:
-        return new PlayPrimaryVideoChan0(NancySceneState.getViewport());
-    case 0x29:
-        return new PlaySecondaryVideo(0, NancySceneState.getViewport());
-    case 0x2A:
-        return new PlaySecondaryVideo(1, NancySceneState.getViewport());
-    case 0x2B:
-        return new PlaySecondaryMovie(NancySceneState.getViewport());
-    case 0x2C:
-        return new PlayStaticBitmapAnimation(false, NancySceneState.getViewport()); // PlayStaticBitmapAnimation
-    case 0x2D:
-        return new PlayStaticBitmapAnimation(true, NancySceneState.getViewport()); // PlayIntStaticBitmapAnimation
-    case 0x32:
-        return new MapCall();
-    case 0x33:
-        return new MapCallHot1Fr();
-    case 0x34:
-        return new MapCallHotMultiframe();
-    case 0x35:
-        return new MapLocationAccess();
-    case 0x38:
-        return new MapSound();
-    case 0x39:
-        return new MapAviOverride();
-    case 0x3A:
-        return new MapAviOverrideOff();
-    case 0x41:
-        return new TextBoxWrite();
-    case 0x42:
-        return new TextBoxClear();
-    case 0x5A:
-        return new BumpPlayerClock();
-    case 0x5B:
-        return new SaveContinueGame();
-    case 0x5C:
-        return new TurnOffMainRendering();
-    case 0x5D:
-        return new TurnOnMainRendering();
-    case 0x5E:
-        return new ResetAndStartTimer();
-    case 0x5F:
-        return new StopTimer();
-    case 0x60:
-        return new EventFlagsMultiHS();
-    case 0x61:
-        return new EventFlags();
-    case 0x62:
-        return new OrderingPuzzle(NancySceneState.getViewport());
-    case 0x63:
-        return new LoseGame();
-    case 0x64:
-        return new PushScene();
-    case 0x65:
-        return new PopScene();
-    case 0x66:
-        return new WinGame();
-    case 0x67:
-        return new DifficultyLevel();
-    case 0x68:
-        return new RotatingLockPuzzle(NancySceneState.getViewport());
-    case 0x69:
-        return new LeverPuzzle(NancySceneState.getViewport());
-    case 0x6A:
-        return new Telephone(NancySceneState.getViewport());
-    case 0x6B:
-        return new SliderPuzzle(NancySceneState.getViewport());
-    case 0x6C:
-        return new PasswordPuzzle(NancySceneState.getViewport());
-    case 0x6E:
-        return new AddInventoryNoHS();
-    case 0x6F:
-        return new RemoveInventoryNoHS();
-    case 0x70:
-        return new ShowInventoryItem(NancySceneState.getViewport());
-    case 0x8C:
-        return new PlayDigiSoundAndDie(); // TODO
-    case 0x8D:
-        return new PlayDigiSoundAndDie(); // TODO
-    case 0x8E:
-        return new PlaySoundPanFrameAnchorAndDie();
-    case 0x8F:
-        return new PlaySoundMultiHS();
-    case 0x96:
-        return new HintSystem();
-    default:
-        error("Action Record type %i is invalid!", type+0xA);
-        return nullptr;
-    }
+	type -= 0xA;
+	switch (type) {
+	case 0x00:
+		return new Hot1FrSceneChange();
+	case 0x01:
+		return new HotMultiframeSceneChange();
+	case 0x02:
+		return new SceneChange();
+	case 0x03:
+		return new HotMultiframeMultisceneChange();
+	case 0x04:
+		return new Hot1FrExitSceneChange();
+	case 0x0C:
+		return new StartFrameNextScene();
+	case 0x14:
+		return new StartStopPlayerScrolling(); // TODO
+	case 0x15:
+		return new StartStopPlayerScrolling(); // TODO
+	case 0x28:
+		return new PlayPrimaryVideoChan0(NancySceneState.getViewport());
+	case 0x29:
+		return new PlaySecondaryVideo(0, NancySceneState.getViewport());
+	case 0x2A:
+		return new PlaySecondaryVideo(1, NancySceneState.getViewport());
+	case 0x2B:
+		return new PlaySecondaryMovie(NancySceneState.getViewport());
+	case 0x2C:
+		return new PlayStaticBitmapAnimation(false, NancySceneState.getViewport()); // PlayStaticBitmapAnimation
+	case 0x2D:
+		return new PlayStaticBitmapAnimation(true, NancySceneState.getViewport()); // PlayIntStaticBitmapAnimation
+	case 0x32:
+		return new MapCall();
+	case 0x33:
+		return new MapCallHot1Fr();
+	case 0x34:
+		return new MapCallHotMultiframe();
+	case 0x35:
+		return new MapLocationAccess();
+	case 0x38:
+		return new MapSound();
+	case 0x39:
+		return new MapAviOverride();
+	case 0x3A:
+		return new MapAviOverrideOff();
+	case 0x41:
+		return new TextBoxWrite();
+	case 0x42:
+		return new TextBoxClear();
+	case 0x5A:
+		return new BumpPlayerClock();
+	case 0x5B:
+		return new SaveContinueGame();
+	case 0x5C:
+		return new TurnOffMainRendering();
+	case 0x5D:
+		return new TurnOnMainRendering();
+	case 0x5E:
+		return new ResetAndStartTimer();
+	case 0x5F:
+		return new StopTimer();
+	case 0x60:
+		return new EventFlagsMultiHS();
+	case 0x61:
+		return new EventFlags();
+	case 0x62:
+		return new OrderingPuzzle(NancySceneState.getViewport());
+	case 0x63:
+		return new LoseGame();
+	case 0x64:
+		return new PushScene();
+	case 0x65:
+		return new PopScene();
+	case 0x66:
+		return new WinGame();
+	case 0x67:
+		return new DifficultyLevel();
+	case 0x68:
+		return new RotatingLockPuzzle(NancySceneState.getViewport());
+	case 0x69:
+		return new LeverPuzzle(NancySceneState.getViewport());
+	case 0x6A:
+		return new Telephone(NancySceneState.getViewport());
+	case 0x6B:
+		return new SliderPuzzle(NancySceneState.getViewport());
+	case 0x6C:
+		return new PasswordPuzzle(NancySceneState.getViewport());
+	case 0x6E:
+		return new AddInventoryNoHS();
+	case 0x6F:
+		return new RemoveInventoryNoHS();
+	case 0x70:
+		return new ShowInventoryItem(NancySceneState.getViewport());
+	case 0x8C:
+		return new PlayDigiSoundAndDie(); // TODO
+	case 0x8D:
+		return new PlayDigiSoundAndDie(); // TODO
+	case 0x8E:
+		return new PlaySoundPanFrameAnchorAndDie();
+	case 0x8F:
+		return new PlaySoundMultiHS();
+	case 0x96:
+		return new HintSystem();
+	default:
+		error("Action Record type %i is invalid!", type+0xA);
+		return nullptr;
+	}
 }
 
 } // End of namespace Action
diff --git a/engines/nancy/action/leverpuzzle.cpp b/engines/nancy/action/leverpuzzle.cpp
index b27b07e7cd..bb9b4562cf 100644
--- a/engines/nancy/action/leverpuzzle.cpp
+++ b/engines/nancy/action/leverpuzzle.cpp
@@ -33,202 +33,202 @@ namespace Nancy {
 namespace Action {
 
 void LeverPuzzle::init() {
-    _drawSurface.create(_screenPosition.width(), _screenPosition.height(), GraphicsManager::getInputPixelFormat());
-    _drawSurface.clear(GraphicsManager::getTransColor());
-    
-    setTransparent(true);
+	_drawSurface.create(_screenPosition.width(), _screenPosition.height(), GraphicsManager::getInputPixelFormat());
+	_drawSurface.clear(GraphicsManager::getTransColor());
 
-    g_nancy->_resource->loadImage(_imageName, _image);
+	setTransparent(true);
+
+	g_nancy->_resource->loadImage(_imageName, _image);
 }
 
 void LeverPuzzle::readData(Common::SeekableReadStream &stream) {
-    readFilename(stream, _imageName);
-
-    for (uint leverID = 0; leverID < 3; ++leverID) {
-        _srcRects.push_back(Common::Array<Common::Rect>());
-        for (uint i = 0; i < 4; ++i) {
-            _srcRects.back().push_back(Common::Rect());
-            readRect(stream, _srcRects.back().back());
-        }
-    }
-
-    for (uint leverID = 0; leverID < 3; ++leverID) {
-        _destRects.push_back(Common::Rect());
-        readRect(stream, _destRects.back());
-
-        if (leverID == 0) {
-            _screenPosition = _destRects.back();
-        } else {
-            _screenPosition.extend(_destRects.back());
-        }
-    }
-
-    for (uint leverID = 0; leverID < 3; ++leverID) {
-        _playerSequence.push_back(stream.readByte());
-        _leverDirection.push_back(true);
-    }
-
-    for (uint leverID = 0; leverID < 3; ++leverID) {
-        _correctSequence.push_back(stream.readByte());
-    }
-
-    _moveSound.read(stream, SoundDescription::kNormal);
-    _noMoveSound.read(stream, SoundDescription::kNormal);
-    _solveExitScene.readData(stream);
-    stream.skip(2);
-    _flagOnSolve.label = stream.readSint16LE();
-    _flagOnSolve.flag = (NancyFlag)stream.readByte();
-    _solveSoundDelay = stream.readUint16LE();
-    _solveSound.read(stream, SoundDescription::kNormal);
-    _exitScene.readData(stream);
-    stream.skip(2);
-    _flagOnExit.label = stream.readSint16LE();
-    _flagOnExit.flag = (NancyFlag)stream.readByte();
-    readRect(stream, _exitHotspot);
+	readFilename(stream, _imageName);
+
+	for (uint leverID = 0; leverID < 3; ++leverID) {
+		_srcRects.push_back(Common::Array<Common::Rect>());
+		for (uint i = 0; i < 4; ++i) {
+			_srcRects.back().push_back(Common::Rect());
+			readRect(stream, _srcRects.back().back());
+		}
+	}
+
+	for (uint leverID = 0; leverID < 3; ++leverID) {
+		_destRects.push_back(Common::Rect());
+		readRect(stream, _destRects.back());
+
+		if (leverID == 0) {
+			_screenPosition = _destRects.back();
+		} else {
+			_screenPosition.extend(_destRects.back());
+		}
+	}
+
+	for (uint leverID = 0; leverID < 3; ++leverID) {
+		_playerSequence.push_back(stream.readByte());
+		_leverDirection.push_back(true);
+	}
+
+	for (uint leverID = 0; leverID < 3; ++leverID) {
+		_correctSequence.push_back(stream.readByte());
+	}
+
+	_moveSound.read(stream, SoundDescription::kNormal);
+	_noMoveSound.read(stream, SoundDescription::kNormal);
+	_solveExitScene.readData(stream);
+	stream.skip(2);
+	_flagOnSolve.label = stream.readSint16LE();
+	_flagOnSolve.flag = (NancyFlag)stream.readByte();
+	_solveSoundDelay = stream.readUint16LE();
+	_solveSound.read(stream, SoundDescription::kNormal);
+	_exitScene.readData(stream);
+	stream.skip(2);
+	_flagOnExit.label = stream.readSint16LE();
+	_flagOnExit.flag = (NancyFlag)stream.readByte();
+	readRect(stream, _exitHotspot);
 }
 
 void LeverPuzzle::execute() {
-    switch (_state) {
-    case kBegin:
-        init();
-        registerGraphics();
-        g_nancy->_sound->loadSound(_moveSound);
-        g_nancy->_sound->loadSound(_noMoveSound);
-
-        for (uint i = 0; i < 3; ++i) {
-            drawLever(i);
-        }
-
-        _state = kRun;
-        // fall through
-    case kRun:
-        switch (_solveState) {
-        case kNotSolved:
-            for (uint i = 0; i < 3; ++i) {
-                if (_playerSequence[i] != _correctSequence[i]) {
-                    return;
-                }
-            }
-            
-            NancySceneState.setEventFlag(_flagOnSolve);
-            _solveSoundPlayTime = g_nancy->getTotalPlayTime() + _solveSoundDelay * 1000;
-            _solveState = kPlaySound;
-            break;
-        case kPlaySound:
-            if (g_nancy->getTotalPlayTime() <= _solveSoundPlayTime) {
-                break;
-            }
-
-            g_nancy->_sound->loadSound(_solveSound);
-            g_nancy->_sound->playSound(_solveSound);
-            _solveState = kWaitForSound;
-            break;
-        case kWaitForSound:
-            if (!g_nancy->_sound->isSoundPlaying(_solveSound)) {
-                g_nancy->_sound->stopSound(_solveSound);
-                _state = kActionTrigger;
-            }
-
-            break;
-        }
-
-        break;
-    case kActionTrigger:
-        g_nancy->_sound->stopSound(_moveSound);
-        g_nancy->_sound->stopSound(_noMoveSound);
-        
-        if (_solveState == kNotSolved) {
-            NancySceneState.changeScene(_exitScene);
-            NancySceneState.setEventFlag(_flagOnExit);
-        } else {
-            NancySceneState.changeScene(_solveExitScene);
-        }
-
-        finishExecution();
-    }
+	switch (_state) {
+	case kBegin:
+		init();
+		registerGraphics();
+		g_nancy->_sound->loadSound(_moveSound);
+		g_nancy->_sound->loadSound(_noMoveSound);
+
+		for (uint i = 0; i < 3; ++i) {
+			drawLever(i);
+		}
+
+		_state = kRun;
+		// fall through
+	case kRun:
+		switch (_solveState) {
+		case kNotSolved:
+			for (uint i = 0; i < 3; ++i) {
+				if (_playerSequence[i] != _correctSequence[i]) {
+					return;
+				}
+			}
+
+			NancySceneState.setEventFlag(_flagOnSolve);
+			_solveSoundPlayTime = g_nancy->getTotalPlayTime() + _solveSoundDelay * 1000;
+			_solveState = kPlaySound;
+			break;
+		case kPlaySound:
+			if (g_nancy->getTotalPlayTime() <= _solveSoundPlayTime) {
+				break;
+			}
+
+			g_nancy->_sound->loadSound(_solveSound);
+			g_nancy->_sound->playSound(_solveSound);
+			_solveState = kWaitForSound;
+			break;
+		case kWaitForSound:
+			if (!g_nancy->_sound->isSoundPlaying(_solveSound)) {
+				g_nancy->_sound->stopSound(_solveSound);
+				_state = kActionTrigger;
+			}
+
+			break;
+		}
+
+		break;
+	case kActionTrigger:
+		g_nancy->_sound->stopSound(_moveSound);
+		g_nancy->_sound->stopSound(_noMoveSound);
+
+		if (_solveState == kNotSolved) {
+			NancySceneState.changeScene(_exitScene);
+			NancySceneState.setEventFlag(_flagOnExit);
+		} else {
+			NancySceneState.changeScene(_solveExitScene);
+		}
+
+		finishExecution();
+	}
 }
 
 void LeverPuzzle::handleInput(NancyInput &input) {
-    if (_solveState != kNotSolved) {
-        return;
-    }
-
-    if (NancySceneState.getViewport().convertViewportToScreen(_exitHotspot).contains(input.mousePos)) {
-        g_nancy->_cursorManager->setCursorType(CursorManager::kExitArrow);
-
-        if (input.input & NancyInput::kLeftMouseButtonUp) {
-            _state = kActionTrigger;
-        }
-        return;
-    }
-
-    for (uint i = 0; i < 3; ++i) {
-        if (NancySceneState.getViewport().convertViewportToScreen(_destRects[i]).contains(input.mousePos)) {
-            g_nancy->_cursorManager->setCursorType(CursorManager::kHotspot);
-            
-            if (input.input & NancyInput::kLeftMouseButtonUp) {
-                bool isMoving = false;
-                // Hardcoded by the original engine
-                switch (i) {
-                case 0:
-                    isMoving = true;
-                    break;
-                case 1:
-                    if (_playerSequence[0] == 1) {
-                        isMoving = true;
-                    }
-
-                    break;
-                case 2:
-                    if (_playerSequence[0] == 2) {
-                        isMoving = true;
-                    }
-                    
-                    break;
-                }
-
-                if (isMoving) {
-                    g_nancy->_sound->playSound(_moveSound);
-
-                    if (_leverDirection[i]) {
-                        // Moving down
-                        if (_playerSequence[i] == 3) {
-                            --_playerSequence[i];
-                            _leverDirection[i] = false;
-                        } else {
-                            ++_playerSequence[i];
-                        }
-                    } else {
-                        // Moving up
-                        if (_playerSequence[i] == 0) {
-                            ++_playerSequence[i];
-                            _leverDirection[i] = true;
-                        } else {
-                            --_playerSequence[i];
-                        }
-                    }
-
-                    drawLever(i);
-                } else {
-                    g_nancy->_sound->playSound(_noMoveSound);
-                    return;
-                }
-            }
-        }
-    }
+	if (_solveState != kNotSolved) {
+		return;
+	}
+
+	if (NancySceneState.getViewport().convertViewportToScreen(_exitHotspot).contains(input.mousePos)) {
+		g_nancy->_cursorManager->setCursorType(CursorManager::kExitArrow);
+
+		if (input.input & NancyInput::kLeftMouseButtonUp) {
+			_state = kActionTrigger;
+		}
+		return;
+	}
+
+	for (uint i = 0; i < 3; ++i) {
+		if (NancySceneState.getViewport().convertViewportToScreen(_destRects[i]).contains(input.mousePos)) {
+			g_nancy->_cursorManager->setCursorType(CursorManager::kHotspot);
+
+			if (input.input & NancyInput::kLeftMouseButtonUp) {
+				bool isMoving = false;
+				// Hardcoded by the original engine
+				switch (i) {
+				case 0:
+					isMoving = true;
+					break;
+				case 1:
+					if (_playerSequence[0] == 1) {
+						isMoving = true;
+					}
+
+					break;
+				case 2:
+					if (_playerSequence[0] == 2) {
+						isMoving = true;
+					}
+
+					break;
+				}
+
+				if (isMoving) {
+					g_nancy->_sound->playSound(_moveSound);
+
+					if (_leverDirection[i]) {
+						// Moving down
+						if (_playerSequence[i] == 3) {
+							--_playerSequence[i];
+							_leverDirection[i] = false;
+						} else {
+							++_playerSequence[i];
+						}
+					} else {
+						// Moving up
+						if (_playerSequence[i] == 0) {
+							++_playerSequence[i];
+							_leverDirection[i] = true;
+						} else {
+							--_playerSequence[i];
+						}
+					}
+
+					drawLever(i);
+				} else {
+					g_nancy->_sound->playSound(_noMoveSound);
+					return;
+				}
+			}
+		}
+	}
 }
 
 void LeverPuzzle::onPause(bool pause) {
-    if (pause) {
-        registerGraphics();
-    }
+	if (pause) {
+		registerGraphics();
+	}
 }
 
 void LeverPuzzle::drawLever(uint id) {
-    Common::Point destPoint(_destRects[id].left - _screenPosition.left, _destRects[id].top - _screenPosition.top);
-    _drawSurface.blitFrom(_image, _srcRects[id][_playerSequence[id]], destPoint);
-    
-    _needsRedraw = true;
+	Common::Point destPoint(_destRects[id].left - _screenPosition.left, _destRects[id].top - _screenPosition.top);
+	_drawSurface.blitFrom(_image, _srcRects[id][_playerSequence[id]], destPoint);
+
+	_needsRedraw = true;
 }
 
 } // End of namespace Action
diff --git a/engines/nancy/action/leverpuzzle.h b/engines/nancy/action/leverpuzzle.h
index 78e08c0a62..2de2a9c15b 100644
--- a/engines/nancy/action/leverpuzzle.h
+++ b/engines/nancy/action/leverpuzzle.h
@@ -38,44 +38,44 @@ namespace Action {
 
 class LeverPuzzle : public ActionRecord, public RenderObject {
 public:
-    enum SolveState { kNotSolved, kPlaySound, kWaitForSound };
-    LeverPuzzle(RenderObject &redrawFrom) : RenderObject(redrawFrom) {}
-    virtual ~LeverPuzzle() {}
-
-    virtual void init() override;
-    
-    virtual void readData(Common::SeekableReadStream &stream) override;
-    virtual void execute() override;
-    virtual void handleInput(NancyInput &input) override;
-    virtual void onPause(bool pause) override;
-
-    Common::String _imageName; // 0x0
-    Common::Array<Common::Array<Common::Rect>> _srcRects; // 0xA, 0xC0 bytes
-    Common::Array<Common::Rect> _destRects; // 0xCA, 0x30 bytes
-    Common::Array<byte> _correctSequence; // 0xFA, 3 bytes
-    SoundDescription _moveSound; // 0x100
-    SoundDescription _noMoveSound; // 0x122
-    SceneChangeDescription _solveExitScene; // 0x144
-    EventFlagDescription _flagOnSolve; // 0x14E
-    uint16 _solveSoundDelay; // 0x151
-    SoundDescription _solveSound; // 0x153
-    SceneChangeDescription _exitScene; // 0x175
-    EventFlagDescription _flagOnExit; // 0x17F
-    Common::Rect _exitHotspot; // 0x182
-
-    Common::Array<byte> _playerSequence;
-    Common::Array<bool> _leverDirection;
-    Graphics::ManagedSurface _image;
-    Time _solveSoundPlayTime;
-    SolveState _solveState = kNotSolved;
+	enum SolveState { kNotSolved, kPlaySound, kWaitForSound };
+	LeverPuzzle(RenderObject &redrawFrom) : RenderObject(redrawFrom) {}
+	virtual ~LeverPuzzle() {}
+
+	virtual void init() override;
+
+	virtual void readData(Common::SeekableReadStream &stream) override;
+	virtual void execute() override;
+	virtual void handleInput(NancyInput &input) override;
+	virtual void onPause(bool pause) override;
+
+	Common::String _imageName; // 0x0
+	Common::Array<Common::Array<Common::Rect>> _srcRects; // 0xA, 0xC0 bytes
+	Common::Array<Common::Rect> _destRects; // 0xCA, 0x30 bytes
+	Common::Array<byte> _correctSequence; // 0xFA, 3 bytes
+	SoundDescription _moveSound; // 0x100
+	SoundDescription _noMoveSound; // 0x122
+	SceneChangeDescription _solveExitScene; // 0x144
+	EventFlagDescription _flagOnSolve; // 0x14E
+	uint16 _solveSoundDelay; // 0x151
+	SoundDescription _solveSound; // 0x153
+	SceneChangeDescription _exitScene; // 0x175
+	EventFlagDescription _flagOnExit; // 0x17F
+	Common::Rect _exitHotspot; // 0x182
+
+	Common::Array<byte> _playerSequence;
+	Common::Array<bool> _leverDirection;
+	Graphics::ManagedSurface _image;
+	Time _solveSoundPlayTime;
+	SolveState _solveState = kNotSolved;
 
 protected:
-    virtual Common::String getRecordTypeName() const override { return "LeverPuzzle"; }
+	virtual Common::String getRecordTypeName() const override { return "LeverPuzzle"; }
 
-    virtual uint16 getZOrder() const override { return 7; }
-    virtual bool isViewportRelative() const override { return true; }
+	virtual uint16 getZOrder() const override { return 7; }
+	virtual bool isViewportRelative() const override { return true; }
 
-    void drawLever(uint id);
+	void drawLever(uint id);
 };
 
 } // End of namespace Action
diff --git a/engines/nancy/action/orderingpuzzle.cpp b/engines/nancy/action/orderingpuzzle.cpp
index 6ea2fe90e7..3480ae6e5b 100644
--- a/engines/nancy/action/orderingpuzzle.cpp
+++ b/engines/nancy/action/orderingpuzzle.cpp
@@ -38,196 +38,196 @@ namespace Nancy {
 namespace Action {
 
 void OrderingPuzzle::init() {
-    // Screen position is initialized in readData and fits exactly the bounds of all elements on screen.
-    // This is a hacky way to make this particular action record work with this implementation's graphics manager
-    _drawSurface.create(_screenPosition.width(), _screenPosition.height(), GraphicsManager::getInputPixelFormat());
-    clearAllElements();
-    
-    setTransparent(true);
+	// Screen position is initialized in readData and fits exactly the bounds of all elements on screen.
+	// This is a hacky way to make this particular action record work with this implementation's graphics manager
+	_drawSurface.create(_screenPosition.width(), _screenPosition.height(), GraphicsManager::getInputPixelFormat());
+	clearAllElements();
 
-    g_nancy->_resource->loadImage(_imageName, _image);
+	setTransparent(true);
 
-    setVisible(false);
+	g_nancy->_resource->loadImage(_imageName, _image);
 
-    RenderObject::init();
+	setVisible(false);
+
+	RenderObject::init();
 }
 
 void OrderingPuzzle::readData(Common::SeekableReadStream &stream) {
-    readFilename(stream, _imageName);
-    uint16 numElements = stream.readUint16LE();
-
-    for (uint i = 0; i < numElements; ++i) {
-        _srcRects.push_back(Common::Rect());
-        readRect(stream, _srcRects.back());
-    }
-    
-    stream.skip(16 * (15 - numElements));
-
-    for (uint i = 0; i < numElements; ++i) {
-        _destRects.push_back(Common::Rect());
-        readRect(stream, _destRects.back());
-
-        if (i == 0) {
-            _screenPosition = _destRects[i];
-        } else {
-            _screenPosition.extend(_destRects[i]);
-        }
-
-        _drawnElements.push_back(false);
-    }
-
-    stream.skip(16 * (15 - numElements));
-
-    _sequenceLength = stream.readUint16LE();
-
-    for (uint i = 0; i < 15; ++i) {
-        _correctSequence.push_back(stream.readByte());
-    }
-
-    _clickSound.read(stream, SoundDescription::kNormal);
-    _solveExitScene.readData(stream);
-    stream.skip(2); // shouldStopRendering, useless
-    _flagOnSolve.label = stream.readSint16LE();
-    _flagOnSolve.flag = (NancyFlag)stream.readByte();
-    _solveSoundDelay = stream.readUint16LE();
-    _solveSound.read(stream, SoundDescription::kNormal);
-    _exitScene.readData(stream);
-    stream.skip(2); // shouldStopRendering, useless
-    _flagOnExit.label = stream.readSint16LE();
-    _flagOnExit.flag = (NancyFlag)stream.readByte();
-    readRect(stream, _exitHotspot);
+	readFilename(stream, _imageName);
+	uint16 numElements = stream.readUint16LE();
+
+	for (uint i = 0; i < numElements; ++i) {
+		_srcRects.push_back(Common::Rect());
+		readRect(stream, _srcRects.back());
+	}
+
+	stream.skip(16 * (15 - numElements));
+
+	for (uint i = 0; i < numElements; ++i) {
+		_destRects.push_back(Common::Rect());
+		readRect(stream, _destRects.back());
+
+		if (i == 0) {
+			_screenPosition = _destRects[i];
+		} else {
+			_screenPosition.extend(_destRects[i]);
+		}
+
+		_drawnElements.push_back(false);
+	}
+
+	stream.skip(16 * (15 - numElements));
+
+	_sequenceLength = stream.readUint16LE();
+
+	for (uint i = 0; i < 15; ++i) {
+		_correctSequence.push_back(stream.readByte());
+	}
+
+	_clickSound.read(stream, SoundDescription::kNormal);
+	_solveExitScene.readData(stream);
+	stream.skip(2); // shouldStopRendering, useless
+	_flagOnSolve.label = stream.readSint16LE();
+	_flagOnSolve.flag = (NancyFlag)stream.readByte();
+	_solveSoundDelay = stream.readUint16LE();
+	_solveSound.read(stream, SoundDescription::kNormal);
+	_exitScene.readData(stream);
+	stream.skip(2); // shouldStopRendering, useless
+	_flagOnExit.label = stream.readSint16LE();
+	_flagOnExit.flag = (NancyFlag)stream.readByte();
+	readRect(stream, _exitHotspot);
 }
 
 void OrderingPuzzle::execute() {
-    switch (_state) {
-    case kBegin:
-        init();
-        registerGraphics();
-        g_nancy->_sound->loadSound(_clickSound);
-        g_nancy->_sound->loadSound(_solveSound);
-        _state = kRun;
-        // fall through
-    case kRun:
-        switch (_solveState) {
-        case kNotSolved:
-            if (_clickedSequence.size() != _sequenceLength) {
-                return;
-            }
-
-            for (uint i = 0; i < _sequenceLength; ++i) {
-                if (_clickedSequence[i] != (int16)_correctSequence[i]) {
-                    return;
-                }
-            }
-
-            NancySceneState.setEventFlag(_flagOnSolve);
-            _solveSoundPlayTime = g_nancy->getTotalPlayTime() + _solveSoundDelay * 1000;
-            _solveState = kPlaySound;
-            // fall through
-        case kPlaySound:
-            if (g_nancy->getTotalPlayTime() <= _solveSoundPlayTime) {
-                break;
-            }
-
-            g_nancy->_sound->playSound(_solveSound);
-            _solveState = kWaitForSound;
-            break;
-        case kWaitForSound:
-            if (!g_nancy->_sound->isSoundPlaying(_solveSound)) {
-                _state = kActionTrigger;
-            }
-
-            break;
-        }
-        break;
-    case kActionTrigger:
-        g_nancy->_sound->stopSound(_clickSound);
-        g_nancy->_sound->stopSound(_solveSound);
-
-        if (_solveState == kNotSolved) {
-            NancySceneState.changeScene(_exitScene);
-            NancySceneState.setEventFlag(_flagOnExit);
-        } else {
-            NancySceneState.changeScene(_solveExitScene);
-        }
-
-        finishExecution();
-        break;
-    }
+	switch (_state) {
+	case kBegin:
+		init();
+		registerGraphics();
+		g_nancy->_sound->loadSound(_clickSound);
+		g_nancy->_sound->loadSound(_solveSound);
+		_state = kRun;
+		// fall through
+	case kRun:
+		switch (_solveState) {
+		case kNotSolved:
+			if (_clickedSequence.size() != _sequenceLength) {
+				return;
+			}
+
+			for (uint i = 0; i < _sequenceLength; ++i) {
+				if (_clickedSequence[i] != (int16)_correctSequence[i]) {
+					return;
+				}
+			}
+
+			NancySceneState.setEventFlag(_flagOnSolve);
+			_solveSoundPlayTime = g_nancy->getTotalPlayTime() + _solveSoundDelay * 1000;
+			_solveState = kPlaySound;
+			// fall through
+		case kPlaySound:
+			if (g_nancy->getTotalPlayTime() <= _solveSoundPlayTime) {
+				break;
+			}
+
+			g_nancy->_sound->playSound(_solveSound);
+			_solveState = kWaitForSound;
+			break;
+		case kWaitForSound:
+			if (!g_nancy->_sound->isSoundPlaying(_solveSound)) {
+				_state = kActionTrigger;
+			}
+
+			break;
+		}
+		break;
+	case kActionTrigger:
+		g_nancy->_sound->stopSound(_clickSound);
+		g_nancy->_sound->stopSound(_solveSound);
+
+		if (_solveState == kNotSolved) {
+			NancySceneState.changeScene(_exitScene);
+			NancySceneState.setEventFlag(_flagOnExit);
+		} else {
+			NancySceneState.changeScene(_solveExitScene);
+		}
+
+		finishExecution();
+		break;
+	}
 }
 
 void OrderingPuzzle::handleInput(NancyInput &input) {
-    if (_solveState != kNotSolved) {
-        return;
-    }
-
-    if (NancySceneState.getViewport().convertViewportToScreen(_exitHotspot).contains(input.mousePos)) {
-        g_nancy->_cursorManager->setCursorType(CursorManager::kExitArrow);
-
-        if (input.input & NancyInput::kLeftMouseButtonUp) {
-            _state = kActionTrigger;
-        }
-        return;
-    }
-
-    for (int i = 0; i < (int)_destRects.size(); ++i) {
-        if (NancySceneState.getViewport().convertViewportToScreen(_destRects[i]).contains(input.mousePos)) {
-            g_nancy->_cursorManager->setCursorType(CursorManager::kHotspot);
-
-            if (input.input & NancyInput::kLeftMouseButtonUp) {
-                g_nancy->_sound->playSound(_clickSound);
-                
-                for (uint j = 0; j < _clickedSequence.size(); ++j) {
-                    if (_clickedSequence[j] == i && _drawnElements[i] == true) {
-                        undrawElement(i);
-                        if (_clickedSequence.back() == i) {
-                            _clickedSequence.pop_back();
-                        }
-                        
-                        return;
-                    }
-                }
-
-                _clickedSequence.push_back(i);
-
-                if (_clickedSequence.size() > (uint)_sequenceLength + 1) {
-                    clearAllElements();
-                } else {
-                    drawElement(i);
-                }
-            }
-            return;
-        }
-    }
+	if (_solveState != kNotSolved) {
+		return;
+	}
+
+	if (NancySceneState.getViewport().convertViewportToScreen(_exitHotspot).contains(input.mousePos)) {
+		g_nancy->_cursorManager->setCursorType(CursorManager::kExitArrow);
+
+		if (input.input & NancyInput::kLeftMouseButtonUp) {
+			_state = kActionTrigger;
+		}
+		return;
+	}
+
+	for (int i = 0; i < (int)_destRects.size(); ++i) {
+		if (NancySceneState.getViewport().convertViewportToScreen(_destRects[i]).contains(input.mousePos)) {
+			g_nancy->_cursorManager->setCursorType(CursorManager::kHotspot);
+
+			if (input.input & NancyInput::kLeftMouseButtonUp) {
+				g_nancy->_sound->playSound(_clickSound);
+
+				for (uint j = 0; j < _clickedSequence.size(); ++j) {
+					if (_clickedSequence[j] == i && _drawnElements[i] == true) {
+						undrawElement(i);
+						if (_clickedSequence.back() == i) {
+							_clickedSequence.pop_back();
+						}
+
+						return;
+					}
+				}
+
+				_clickedSequence.push_back(i);
+
+				if (_clickedSequence.size() > (uint)_sequenceLength + 1) {
+					clearAllElements();
+				} else {
+					drawElement(i);
+				}
+			}
+			return;
+		}
+	}
 }
 
 void OrderingPuzzle::onPause(bool pause) {
-    if (pause) {
-        registerGraphics();
-    }
+	if (pause) {
+		registerGraphics();
+	}
 }
 
 void OrderingPuzzle::drawElement(uint id) {
-    _drawnElements[id] = true;
-    Common::Point destPoint(_destRects[id].left - _screenPosition.left, _destRects[id].top - _screenPosition.top);
-    _drawSurface.blitFrom(_image, _srcRects[id], destPoint);
-    setVisible(true);
+	_drawnElements[id] = true;
+	Common::Point destPoint(_destRects[id].left - _screenPosition.left, _destRects[id].top - _screenPosition.top);
+	_drawSurface.blitFrom(_image, _srcRects[id], destPoint);
+	setVisible(true);
 }
 
 void OrderingPuzzle::undrawElement(uint id) {
-    _drawnElements[id] = false;
-    Common::Rect bounds = _destRects[id];
-    bounds.translate(-_screenPosition.left, -_screenPosition.top);
+	_drawnElements[id] = false;
+	Common::Rect bounds = _destRects[id];
+	bounds.translate(-_screenPosition.left, -_screenPosition.top);
 
-    _drawSurface.fillRect(bounds, GraphicsManager::getTransColor());
-    _needsRedraw = true;
+	_drawSurface.fillRect(bounds, GraphicsManager::getTransColor());
+	_needsRedraw = true;
 }
 
 void OrderingPuzzle::clearAllElements() {
-    _drawSurface.clear(GraphicsManager::getTransColor());
-    setVisible(false);
-    _clickedSequence.clear();
-    return;
+	_drawSurface.clear(GraphicsManager::getTransColor());
+	setVisible(false);
+	_clickedSequence.clear();
+	return;
 }
 
 } // End of namespace Action
diff --git a/engines/nancy/action/orderingpuzzle.h b/engines/nancy/action/orderingpuzzle.h
index c676d6b696..1330a7347a 100644
--- a/engines/nancy/action/orderingpuzzle.h
+++ b/engines/nancy/action/orderingpuzzle.h
@@ -39,49 +39,49 @@ namespace Action {
 
 class OrderingPuzzle : public ActionRecord, public RenderObject {
 public:
-    enum SolveState { kNotSolved, kPlaySound, kWaitForSound };
-    OrderingPuzzle(RenderObject &redrawFrom) : RenderObject(redrawFrom) {}
-    virtual ~OrderingPuzzle() {}
-
-    virtual void init() override;
-    
-    virtual void readData(Common::SeekableReadStream &stream) override;
-    virtual void execute() override;
-    virtual void handleInput(NancyInput &input) override;
-    virtual void onPause(bool pause) override;
-
-    Common::String _imageName; // 0x00
-    Common::Array<Common::Rect> _srcRects; // 0xC, 15
-    Common::Array<Common::Rect> _destRects; // 0xFC, 15
-    uint16 _sequenceLength; // 0x1EC;
-    Common::Array<byte> _correctSequence; // 0x1EE, 15 bytes
-    Nancy::SoundDescription _clickSound; // 0x1FD, kNormal
-    SceneChangeDescription _solveExitScene; // 0x21F
-    EventFlagDescription _flagOnSolve; // 0x229
-    uint16 _solveSoundDelay; // 0x22C 
-    Nancy::SoundDescription _solveSound; // 0x22E
-    SceneChangeDescription _exitScene; // 0x250
-    EventFlagDescription _flagOnExit; // 0x25A
-    Common::Rect _exitHotspot; // 0x25D
-
-    SolveState _solveState = kNotSolved;
-    Graphics::ManagedSurface _image;
-    Common::Array<int16> _clickedSequence;
-    Common::Array<bool> _drawnElements;
-    Time _solveSoundPlayTime;
+	enum SolveState { kNotSolved, kPlaySound, kWaitForSound };
+	OrderingPuzzle(RenderObject &redrawFrom) : RenderObject(redrawFrom) {}
+	virtual ~OrderingPuzzle() {}
+
+	virtual void init() override;
+
+	virtual void readData(Common::SeekableReadStream &stream) override;
+	virtual void execute() override;
+	virtual void handleInput(NancyInput &input) override;
+	virtual void onPause(bool pause) override;
+
+	Common::String _imageName; // 0x00
+	Common::Array<Common::Rect> _srcRects; // 0xC, 15
+	Common::Array<Common::Rect> _destRects; // 0xFC, 15
+	uint16 _sequenceLength; // 0x1EC;
+	Common::Array<byte> _correctSequence; // 0x1EE, 15 bytes
+	Nancy::SoundDescription _clickSound; // 0x1FD, kNormal
+	SceneChangeDescription _solveExitScene; // 0x21F
+	EventFlagDescription _flagOnSolve; // 0x229
+	uint16 _solveSoundDelay; // 0x22C
+	Nancy::SoundDescription _solveSound; // 0x22E
+	SceneChangeDescription _exitScene; // 0x250
+	EventFlagDescription _flagOnExit; // 0x25A
+	Common::Rect _exitHotspot; // 0x25D
+
+	SolveState _solveState = kNotSolved;
+	Graphics::ManagedSurface _image;
+	Common::Array<int16> _clickedSequence;
+	Common::Array<bool> _drawnElements;
+	Time _solveSoundPlayTime;
 
 protected:
-    virtual Common::String getRecordTypeName() const override { return "OrderingPuzzle"; }
-    
-    virtual uint16 getZOrder() const override { return 7; }
-    virtual bool isViewportRelative() const override { return true; }
-
-    void drawElement(uint id);
-    void undrawElement(uint id);
-    void clearAllElements();
+	virtual Common::String getRecordTypeName() const override { return "OrderingPuzzle"; }
+
+	virtual uint16 getZOrder() const override { return 7; }
+	virtual bool isViewportRelative() const override { return true; }
+
+	void drawElement(uint id);
+	void undrawElement(uint id);
+	void clearAllElements();
 };
 
-} // End of namespace Action 
+} // End of namespace Action
 } // End of namespace Nancy
 
 #endif // NANCY_ACTION_ORDERINGPUZZLE_H
diff --git a/engines/nancy/action/passwordpuzzle.cpp b/engines/nancy/action/passwordpuzzle.cpp
index 022b09a9ca..101df86ae5 100644
--- a/engines/nancy/action/passwordpuzzle.cpp
+++ b/engines/nancy/action/passwordpuzzle.cpp
@@ -35,207 +35,207 @@ namespace Nancy {
 namespace Action {
 
 void PasswordPuzzle::init() {
-    _drawSurface.create(_screenPosition.width(), _screenPosition.height(), GraphicsManager::getInputPixelFormat());
-    _drawSurface.clear(GraphicsManager::getTransColor());
-    
-    setTransparent(true);
+	_drawSurface.create(_screenPosition.width(), _screenPosition.height(), GraphicsManager::getInputPixelFormat());
+	_drawSurface.clear(GraphicsManager::getTransColor());
 
-    RenderObject::init();
+	setTransparent(true);
+
+	RenderObject::init();
 }
 
 void PasswordPuzzle::readData(Common::SeekableReadStream &stream) {
-    _fontID = stream.readUint16LE();
-    _cursorBlinkTime = stream.readUint16LE();
-    readRect(stream, _nameBounds);
-    readRect(stream, _passwordBounds);
-    readRect(stream, _screenPosition);
-
-    char buf[20];
-    stream.read(buf, 20);
-    buf[19] = '\0';
-    _name = buf;
-    stream.read(buf, 20);
-    buf[19] = '\0';
-    _password = buf;
-    _solveExitScene.readData(stream);
-    stream.skip(2);
-    _flagOnSolve.label = stream.readSint16LE();
-    _flagOnSolve.flag = (NancyFlag)stream.readByte();
-    _solveSound.read(stream, SoundDescription::kNormal);
-    _failExitScene.readData(stream);
-    stream.skip(2);
-    _flagOnFail.label = stream.readSint16LE();
-    _flagOnFail.flag = (NancyFlag)stream.readByte();
-    _failSound.read(stream, SoundDescription::kNormal);
-    _exitScene.readData(stream);
-    stream.skip(2);
-    _flagOnExit.label = stream.readSint16LE();
-    _flagOnExit.flag = (NancyFlag)stream.readByte();
-    readRect(stream, _exitHotspot);
+	_fontID = stream.readUint16LE();
+	_cursorBlinkTime = stream.readUint16LE();
+	readRect(stream, _nameBounds);
+	readRect(stream, _passwordBounds);
+	readRect(stream, _screenPosition);
+
+	char buf[20];
+	stream.read(buf, 20);
+	buf[19] = '\0';
+	_name = buf;
+	stream.read(buf, 20);
+	buf[19] = '\0';
+	_password = buf;
+	_solveExitScene.readData(stream);
+	stream.skip(2);
+	_flagOnSolve.label = stream.readSint16LE();
+	_flagOnSolve.flag = (NancyFlag)stream.readByte();
+	_solveSound.read(stream, SoundDescription::kNormal);
+	_failExitScene.readData(stream);
+	stream.skip(2);
+	_flagOnFail.label = stream.readSint16LE();
+	_flagOnFail.flag = (NancyFlag)stream.readByte();
+	_failSound.read(stream, SoundDescription::kNormal);
+	_exitScene.readData(stream);
+	stream.skip(2);
+	_flagOnExit.label = stream.readSint16LE();
+	_flagOnExit.flag = (NancyFlag)stream.readByte();
+	readRect(stream, _exitHotspot);
 }
 
 void PasswordPuzzle::execute() {
-    switch (_state) {
-    case kBegin:
-        init();
-        registerGraphics();
-        _nextBlinkTime = g_nancy->getTotalPlayTime() + _cursorBlinkTime;
-        _state = kRun;
-        // fall through
-    case kRun:
-        switch (_solveState) {
-        case kNotSolved: {
-            Common::String &activeField = _passwordFieldIsActive ? _playerPasswordInput : _playerNameInput;
-            Common::String &correctField = _passwordFieldIsActive ? _password : _name;
-            Time currentTime = g_nancy->getTotalPlayTime();
-
-            if (_playerHasHitReturn) {
-                _playerHasHitReturn = false;
-
-                if (activeField.lastChar() == '-') {
-                    activeField.deleteLastChar();
-                    drawText();
-                }
-
-                if (activeField.equalsIgnoreCase(correctField)) {
-                    if (!_passwordFieldIsActive) {
-                        _passwordFieldIsActive = true;
-                    } else {
-                        g_nancy->_sound->loadSound(_solveSound);
-                        g_nancy->_sound->playSound(_solveSound);
-                        _solveState = kSolved;
-                    }
-                } else {
-                    g_nancy->_sound->loadSound(_failSound);
-                    g_nancy->_sound->playSound(_failSound);
-                    _solveState = kFailed;
-                }
-                
-                
-            } else if (currentTime >= _nextBlinkTime) {
-                _nextBlinkTime = currentTime + _cursorBlinkTime;
-
-                if (activeField.size() && activeField.lastChar() == '-') {
-                    activeField.deleteLastChar();
-                } else {
-                    activeField += '-';
-                }
-
-                drawText();
-            }
-
-            break;
-        }
-        case kFailed:
-            if (!g_nancy->_sound->isSoundPlaying(_failSound)) {
-                g_nancy->_sound->stopSound(_failSound);
-                _state = kActionTrigger;
-            }
-
-            break;
-        case kSolved:
-            if (!g_nancy->_sound->isSoundPlaying(_solveSound)) {
-                g_nancy->_sound->stopSound(_solveSound);
-                _state = kActionTrigger;
-            }
-
-            break;
-        }
-
-        break;
-    case kActionTrigger:
-        switch (_solveState) {
-        case kNotSolved:
-            NancySceneState.changeScene(_exitScene);
-            NancySceneState.setEventFlag(_flagOnExit);
-            break;
-        case kFailed:
-            NancySceneState.changeScene(_failExitScene);
-            NancySceneState.setEventFlag(_flagOnFail.label);
-            break;
-        case kSolved:
-            NancySceneState.changeScene(_solveExitScene);
-            NancySceneState.setEventFlag(_flagOnSolve.label);
-            break;
-        }
-
-        finishExecution();
-    }
+	switch (_state) {
+	case kBegin:
+		init();
+		registerGraphics();
+		_nextBlinkTime = g_nancy->getTotalPlayTime() + _cursorBlinkTime;
+		_state = kRun;
+		// fall through
+	case kRun:
+		switch (_solveState) {
+		case kNotSolved: {
+			Common::String &activeField = _passwordFieldIsActive ? _playerPasswordInput : _playerNameInput;
+			Common::String &correctField = _passwordFieldIsActive ? _password : _name;
+			Time currentTime = g_nancy->getTotalPlayTime();
+
+			if (_playerHasHitReturn) {
+				_playerHasHitReturn = false;
+
+				if (activeField.lastChar() == '-') {
+					activeField.deleteLastChar();
+					drawText();
+				}
+
+				if (activeField.equalsIgnoreCase(correctField)) {
+					if (!_passwordFieldIsActive) {
+						_passwordFieldIsActive = true;
+					} else {
+						g_nancy->_sound->loadSound(_solveSound);
+						g_nancy->_sound->playSound(_solveSound);
+						_solveState = kSolved;
+					}
+				} else {
+					g_nancy->_sound->loadSound(_failSound);
+					g_nancy->_sound->playSound(_failSound);
+					_solveState = kFailed;
+				}
+
+
+			} else if (currentTime >= _nextBlinkTime) {
+				_nextBlinkTime = currentTime + _cursorBlinkTime;
+
+				if (activeField.size() && activeField.lastChar() == '-') {
+					activeField.deleteLastChar();
+				} else {
+					activeField += '-';
+				}
+
+				drawText();
+			}
+
+			break;
+		}
+		case kFailed:
+			if (!g_nancy->_sound->isSoundPlaying(_failSound)) {
+				g_nancy->_sound->stopSound(_failSound);
+				_state = kActionTrigger;
+			}
+
+			break;
+		case kSolved:
+			if (!g_nancy->_sound->isSoundPlaying(_solveSound)) {
+				g_nancy->_sound->stopSound(_solveSound);
+				_state = kActionTrigger;
+			}
+
+			break;
+		}
+
+		break;
+	case kActionTrigger:
+		switch (_solveState) {
+		case kNotSolved:
+			NancySceneState.changeScene(_exitScene);
+			NancySceneState.setEventFlag(_flagOnExit);
+			break;
+		case kFailed:
+			NancySceneState.changeScene(_failExitScene);
+			NancySceneState.setEventFlag(_flagOnFail.label);
+			break;
+		case kSolved:
+			NancySceneState.changeScene(_solveExitScene);
+			NancySceneState.setEventFlag(_flagOnSolve.label);
+			break;
+		}
+
+		finishExecution();
+	}
 }
 
 void PasswordPuzzle::handleInput(NancyInput &input) {
-    if (_solveState != kNotSolved) {
-        return;
-    }
-
-    if (NancySceneState.getViewport().convertViewportToScreen(_exitHotspot).contains(input.mousePos)) {
-        g_nancy->_cursorManager->setCursorType(CursorManager::kExitArrow);
-
-        if (input.input & NancyInput::kLeftMouseButtonUp) {
-            _state = kActionTrigger;
-        }
-        
-        return;
-    }
-
-    for (uint i = 0; i < input.otherKbdInput.size(); ++i) {
-        Common::KeyState &key = input.otherKbdInput[i];
-        Common::String &activeField = _passwordFieldIsActive ? _playerPasswordInput : _playerNameInput;
-        Common::String &correctField = _passwordFieldIsActive ? _password : _name;
-        if (key.keycode == Common::KEYCODE_BACKSPACE) {
-            if (activeField.size() && activeField.lastChar() == '-' ? activeField.size() > 1 : true) {
-                if (activeField.lastChar() == '-') {
-                    activeField.deleteChar(activeField.size() -2);
-                } else {
-                    activeField.deleteLastChar();
-                }
-
-                drawText();
-            }
-        } else if (key.keycode == Common::KEYCODE_RETURN) {
-            _playerHasHitReturn = true;
-        } else if (Common::isAlnum(key.ascii) || Common::isSpace(key.ascii)) {
-            if (activeField.size() && activeField.lastChar() == '-') {
-                if (activeField.size() <= correctField.size() + 2) {
-                    activeField.deleteLastChar();
-                    activeField += key.ascii;
-                    activeField += '-';
-                }
-            } else {
-                if (activeField.size() <= correctField.size() + 1) {
-                    activeField += key.ascii;
-                }
-            }
-
-            drawText();
-        }
-    }
+	if (_solveState != kNotSolved) {
+		return;
+	}
+
+	if (NancySceneState.getViewport().convertViewportToScreen(_exitHotspot).contains(input.mousePos)) {
+		g_nancy->_cursorManager->setCursorType(CursorManager::kExitArrow);
+
+		if (input.input & NancyInput::kLeftMouseButtonUp) {
+			_state = kActionTrigger;
+		}
+
+		return;
+	}
+
+	for (uint i = 0; i < input.otherKbdInput.size(); ++i) {
+		Common::KeyState &key = input.otherKbdInput[i];
+		Common::String &activeField = _passwordFieldIsActive ? _playerPasswordInput : _playerNameInput;
+		Common::String &correctField = _passwordFieldIsActive ? _password : _name;
+		if (key.keycode == Common::KEYCODE_BACKSPACE) {
+			if (activeField.size() && activeField.lastChar() == '-' ? activeField.size() > 1 : true) {
+				if (activeField.lastChar() == '-') {
+					activeField.deleteChar(activeField.size() -2);
+				} else {
+					activeField.deleteLastChar();
+				}
+
+				drawText();
+			}
+		} else if (key.keycode == Common::KEYCODE_RETURN) {
+			_playerHasHitReturn = true;
+		} else if (Common::isAlnum(key.ascii) || Common::isSpace(key.ascii)) {
+			if (activeField.size() && activeField.lastChar() == '-') {
+				if (activeField.size() <= correctField.size() + 2) {
+					activeField.deleteLastChar();
+					activeField += key.ascii;
+					activeField += '-';
+				}
+			} else {
+				if (activeField.size() <= correctField.size() + 1) {
+					activeField += key.ascii;
+				}
+			}
+
+			drawText();
+		}
+	}
 }
 
 void PasswordPuzzle::onPause(bool pause) {
-    if (pause) {
-        registerGraphics();
-    }
+	if (pause) {
+		registerGraphics();
+	}
 }
 
 void PasswordPuzzle::drawText() {
-    _drawSurface.clear(GraphicsManager::getTransColor());
-    Graphics::Font *font = g_nancy->_graphicsManager->getFont(_fontID);
-
-    Common::Rect bounds = _nameBounds;
-    bounds = NancySceneState.getViewport().convertViewportToScreen(bounds);
-    bounds = convertToLocal(bounds);
-    Common::Point destPoint(bounds.left, bounds.bottom + 1 - font->getFontHeight());
-    font->drawString(&_drawSurface, _playerNameInput, destPoint.x, destPoint.y, bounds.width(), 0);
-
-    bounds = _passwordBounds;
-    bounds = NancySceneState.getViewport().convertViewportToScreen(bounds);
-    bounds = convertToLocal(bounds);
-    destPoint.x = bounds.left;
-    destPoint.y = bounds.bottom + 1 - font->getFontHeight();
-    font->drawString(&_drawSurface, _playerPasswordInput, destPoint.x, destPoint.y, bounds.width(), 0);
-
-    _needsRedraw = true;
+	_drawSurface.clear(GraphicsManager::getTransColor());
+	Graphics::Font *font = g_nancy->_graphicsManager->getFont(_fontID);
+
+	Common::Rect bounds = _nameBounds;
+	bounds = NancySceneState.getViewport().convertViewportToScreen(bounds);
+	bounds = convertToLocal(bounds);
+	Common::Point destPoint(bounds.left, bounds.bottom + 1 - font->getFontHeight());
+	font->drawString(&_drawSurface, _playerNameInput, destPoint.x, destPoint.y, bounds.width(), 0);
+
+	bounds = _passwordBounds;
+	bounds = NancySceneState.getViewport().convertViewportToScreen(bounds);
+	bounds = convertToLocal(bounds);
+	destPoint.x = bounds.left;
+	destPoint.y = bounds.bottom + 1 - font->getFontHeight();
+	font->drawString(&_drawSurface, _playerPasswordInput, destPoint.x, destPoint.y, bounds.width(), 0);
+
+	_needsRedraw = true;
 }
 
 } // End of namespace Action
diff --git a/engines/nancy/action/passwordpuzzle.h b/engines/nancy/action/passwordpuzzle.h
index c727c153d2..7a18836485 100644
--- a/engines/nancy/action/passwordpuzzle.h
+++ b/engines/nancy/action/passwordpuzzle.h
@@ -37,52 +37,52 @@ namespace Action {
 
 class PasswordPuzzle : public ActionRecord, public RenderObject {
 public:
-    enum SolveState { kNotSolved, kFailed, kSolved };
-    PasswordPuzzle(RenderObject &redrawFrom) :
-        RenderObject(redrawFrom),
-        _passwordFieldIsActive(false),
-        _playerHasHitReturn(false),
-        _solveState(kNotSolved) {}
-    virtual ~PasswordPuzzle() {}
-
-    virtual void init() override;
-    
-    virtual void readData(Common::SeekableReadStream &stream) override;
-    virtual void execute() override;
-    virtual void handleInput(NancyInput &input) override;
-    virtual void onPause(bool pause) override;
-
-    uint16 _fontID; // 0x00
-    Time _cursorBlinkTime; // 0x2
-    Common::Rect _nameBounds; // 0x4
-    Common::Rect _passwordBounds; // 0x14
-    // _screenPosition 0x24
-    Common::String _name; // 0x34, 20 bytes long
-    Common::String _password; // 0x48, 20 bytes long
-    SceneChangeDescription _solveExitScene; // 0x5A
-    EventFlagDescription _flagOnSolve; // 0x66
-    SoundDescription _solveSound; // 0x69
-    SceneChangeDescription _failExitScene; // 0x8B
-    EventFlagDescription _flagOnFail; // 0x95
-    SoundDescription _failSound; // 0x98
-    SceneChangeDescription _exitScene; // 0xBA
-    EventFlagDescription _flagOnExit; // 0xC4
-    Common::Rect _exitHotspot; // 0xC7
-
-    Common::String _playerNameInput;
-    Common::String _playerPasswordInput;
-    Time _nextBlinkTime;
-    bool _passwordFieldIsActive;
-    bool _playerHasHitReturn;
-    SolveState _solveState;
+	enum SolveState { kNotSolved, kFailed, kSolved };
+	PasswordPuzzle(RenderObject &redrawFrom) :
+		RenderObject(redrawFrom),
+		_passwordFieldIsActive(false),
+		_playerHasHitReturn(false),
+		_solveState(kNotSolved) {}
+	virtual ~PasswordPuzzle() {}
+
+	virtual void init() override;
+
+	virtual void readData(Common::SeekableReadStream &stream) override;
+	virtual void execute() override;
+	virtual void handleInput(NancyInput &input) override;
+	virtual void onPause(bool pause) override;
+
+	uint16 _fontID; // 0x00
+	Time _cursorBlinkTime; // 0x2
+	Common::Rect _nameBounds; // 0x4
+	Common::Rect _passwordBounds; // 0x14
+	// _screenPosition 0x24
+	Common::String _name; // 0x34, 20 bytes long
+	Common::String _password; // 0x48, 20 bytes long
+	SceneChangeDescription _solveExitScene; // 0x5A
+	EventFlagDescription _flagOnSolve; // 0x66
+	SoundDescription _solveSound; // 0x69
+	SceneChangeDescription _failExitScene; // 0x8B
+	EventFlagDescription _flagOnFail; // 0x95
+	SoundDescription _failSound; // 0x98
+	SceneChangeDescription _exitScene; // 0xBA
+	EventFlagDescription _flagOnExit; // 0xC4
+	Common::Rect _exitHotspot; // 0xC7
+
+	Common::String _playerNameInput;
+	Common::String _playerPasswordInput;
+	Time _nextBlinkTime;
+	bool _passwordFieldIsActive;
+	bool _playerHasHitReturn;
+	SolveState _solveState;
 
 protected:
-    virtual Common::String getRecordTypeName() const override { return "PasswordPuzzle"; }
-    
-    virtual uint16 getZOrder() const override { return 7; }
-    virtual bool isViewportRelative() const override { return true; }
+	virtual Common::String getRecordTypeName() const override { return "PasswordPuzzle"; }
+
+	virtual uint16 getZOrder() const override { return 7; }
+	virtual bool isViewportRelative() const override { return true; }
 
-    void drawText();
+	void drawText();
 };
 
 } // End of namespace Action
diff --git a/engines/nancy/action/primaryvideo.cpp b/engines/nancy/action/primaryvideo.cpp
index 8c3b912c9d..2fc01cc406 100644
--- a/engines/nancy/action/primaryvideo.cpp
+++ b/engines/nancy/action/primaryvideo.cpp
@@ -40,342 +40,342 @@ namespace Action {
 PlayPrimaryVideoChan0 *PlayPrimaryVideoChan0::_activePrimaryVideo = nullptr;
 
 void PlayPrimaryVideoChan0::ConditionFlag::read(Common::SeekableReadStream &stream) {
-    type = (ConditionType)stream.readByte();
-    flag.label = stream.readSint16LE();
-    flag.flag = (NancyFlag)stream.readByte();
-    orFlag = stream.readByte();
+	type = (ConditionType)stream.readByte();
+	flag.label = stream.readSint16LE();
+	flag.flag = (NancyFlag)stream.readByte();
+	orFlag = stream.readByte();
 }
 
 bool PlayPrimaryVideoChan0::ConditionFlag::isSatisfied() const {
-    switch (type) {
-    case ConditionFlag::kEventFlags:
-        return NancySceneState.getEventFlag(flag);
-    case ConditionFlag::kInventory:
-        return NancySceneState.hasItem(flag.label) == flag.flag;
-    default:
-        return false;
-    }
+	switch (type) {
+	case ConditionFlag::kEventFlags:
+		return NancySceneState.getEventFlag(flag);
+	case ConditionFlag::kInventory:
+		return NancySceneState.hasItem(flag.label) == flag.flag;
+	default:
+		return false;
+	}
 }
 
 void PlayPrimaryVideoChan0::ConditionFlag::set() const {
-    switch (type) {
-    case ConditionFlag::kEventFlags:
-        NancySceneState.setEventFlag(flag);
-        break;
-    case ConditionFlag::kInventory:
-        if (flag.flag == kTrue) {
-            NancySceneState.addItemToInventory(flag.label);
-        } else {
-            NancySceneState.removeItemFromInventory(flag.label);
-        }
-
-        break;
-    default:
-        break;
-    }
+	switch (type) {
+	case ConditionFlag::kEventFlags:
+		NancySceneState.setEventFlag(flag);
+		break;
+	case ConditionFlag::kInventory:
+		if (flag.flag == kTrue) {
+			NancySceneState.addItemToInventory(flag.label);
+		} else {
+			NancySceneState.removeItemFromInventory(flag.label);
+		}
+
+		break;
+	default:
+		break;
+	}
 }
 
 void PlayPrimaryVideoChan0::ConditionFlags::read(Common::SeekableReadStream &stream) {
-    uint16 numFlags = stream.readUint16LE();
-    
-    for (uint i = 0; i < numFlags; ++i) {
-        conditionFlags.push_back(ConditionFlag());
-        conditionFlags.back().read(stream);
-    }
+	uint16 numFlags = stream.readUint16LE();
+
+	for (uint i = 0; i < numFlags; ++i) {
+		conditionFlags.push_back(ConditionFlag());
+		conditionFlags.back().read(stream);
+	}
 }
 
 bool PlayPrimaryVideoChan0::ConditionFlags::isSatisfied() const {
-    bool orFlag = false;
-
-    for (uint i = 0; i < conditionFlags.size(); ++i) {
-        const ConditionFlag &cur = conditionFlags[i];
-        
-        if (!cur.isSatisfied()) {
-            if (orFlag) {
-                return false;
-            } else {
-                orFlag = true;
-            }
-        }
-    }
-
-    if (orFlag) {
-        return false;
-    } else {
-        return true;
-    }
+	bool orFlag = false;
+
+	for (uint i = 0; i < conditionFlags.size(); ++i) {
+		const ConditionFlag &cur = conditionFlags[i];
+
+		if (!cur.isSatisfied()) {
+			if (orFlag) {
+				return false;
+			} else {
+				orFlag = true;
+			}
+		}
+	}
+
+	if (orFlag) {
+		return false;
+	} else {
+		return true;
+	}
 }
 
 PlayPrimaryVideoChan0::~PlayPrimaryVideoChan0() {
-    _decoder.close();
+	_decoder.close();
 
 	if (_activePrimaryVideo == this) {
 		_activePrimaryVideo = nullptr;
 	}
-    
-    NancySceneState.setShouldClearTextbox(true);
-    NancySceneState.getTextbox().setVisible(false);
+
+	NancySceneState.setShouldClearTextbox(true);
+	NancySceneState.getTextbox().setVisible(false);
 }
 
 void PlayPrimaryVideoChan0::init() {
-    _decoder.loadFile(_videoName + ".avf");
-    _drawSurface.create(_src.width(), _src.height(), _decoder.getPixelFormat());
+	_decoder.loadFile(_videoName + ".avf");
+	_drawSurface.create(_src.width(), _src.height(), _decoder.getPixelFormat());
+
+	RenderObject::init();
 
-    RenderObject::init();
-    
-    NancySceneState.setShouldClearTextbox(false);
+	NancySceneState.setShouldClearTextbox(false);
 }
 
 void PlayPrimaryVideoChan0::updateGraphics() {
-    if (!_decoder.isVideoLoaded()) {
-        return;
-    }
+	if (!_decoder.isVideoLoaded()) {
+		return;
+	}
 
-    if (!_decoder.isPlaying()) {
-        _decoder.start();
-    }
+	if (!_decoder.isPlaying()) {
+		_decoder.start();
+	}
 
-    if (_decoder.needsUpdate()) {
-        _drawSurface.blitFrom(*_decoder.decodeNextFrame(), _src, Common::Point());
-        _needsRedraw = true;
-    }
+	if (_decoder.needsUpdate()) {
+		_drawSurface.blitFrom(*_decoder.decodeNextFrame(), _src, Common::Point());
+		_needsRedraw = true;
+	}
 
-    RenderObject::updateGraphics();
+	RenderObject::updateGraphics();
 }
 
 void PlayPrimaryVideoChan0::onPause(bool pause) {
-    _decoder.pauseVideo(pause);
+	_decoder.pauseVideo(pause);
 
-    if (pause) {
-        registerGraphics();
-    }
+	if (pause) {
+		registerGraphics();
+	}
 }
 
 void PlayPrimaryVideoChan0::readData(Common::SeekableReadStream &stream) {
-    uint16 beginOffset = stream.pos();
-
-    readFilename(stream, _videoName);
-
-    stream.skip(0x13);
-
-    readRect(stream, _src);
-    readRect(stream, _screenPosition);
-
-    char *rawText = new char[1500];
-    stream.read(rawText, 1500);
-    UI::Textbox::assembleTextLine(rawText, _text, 1500);
-    delete[] rawText;
-
-    _sound.read(stream, SoundDescription::kNormal);
-    _responseGenericSound.read(stream, SoundDescription::kNormal);
-    stream.skip(1);
-    _conditionalResponseCharacterID = stream.readByte();
-    _goodbyeResponseCharacterID = stream.readByte();
-    _isDialogueExitScene = (NancyFlag)stream.readByte();
-    _doNotPop = (NancyFlag)stream.readByte();
-    _sceneChange.readData(stream);
-
-    stream.seek(beginOffset + 0x69C);
-
-    uint16 numResponses = stream.readUint16LE();
-    rawText = new char[400];
-
-    if (numResponses > 0) {
-        for (uint i = 0; i < numResponses; ++i) {
-            _responses.push_back(ResponseStruct());
-            ResponseStruct &response = _responses[i];
-            response.conditionFlags.read(stream);
-            stream.read(rawText, 400);
-            UI::Textbox::assembleTextLine(rawText, response.text, 400);
-            readFilename(stream, response.soundName);
-            stream.skip(1);
-            response._sceneChange.readData(stream);
-            response.flagDesc.label = stream.readSint16LE();
-            response.flagDesc.flag = (NancyFlag)stream.readByte();
-
-            stream.skip(0x32);
-        }
-    }
-    
-    delete[] rawText;
-
-    uint16 numSceneBranchStructs = stream.readUint16LE();
-    if (numSceneBranchStructs > 0) {
-        // TODO
-    }
-
-    uint16 numFlagsStructs = stream.readUint16LE();
-    if (numFlagsStructs > 0) {
-        for (uint16 i = 0; i < numFlagsStructs; ++i) {
-            _flagsStructs.push_back(FlagsStruct());
-            FlagsStruct &flagsStruct = _flagsStructs.back();
-            flagsStruct.conditions.read(stream);
-            flagsStruct.flagToSet.type = (ConditionFlag::ConditionType)stream.readByte();
-            flagsStruct.flagToSet.flag.label = stream.readSint16LE();
-            flagsStruct.flagToSet.flag.flag = (NancyFlag)stream.readByte();
-        }
-    }
+	uint16 beginOffset = stream.pos();
+
+	readFilename(stream, _videoName);
+
+	stream.skip(0x13);
+
+	readRect(stream, _src);
+	readRect(stream, _screenPosition);
+
+	char *rawText = new char[1500];
+	stream.read(rawText, 1500);
+	UI::Textbox::assembleTextLine(rawText, _text, 1500);
+	delete[] rawText;
+
+	_sound.read(stream, SoundDescription::kNormal);
+	_responseGenericSound.read(stream, SoundDescription::kNormal);
+	stream.skip(1);
+	_conditionalResponseCharacterID = stream.readByte();
+	_goodbyeResponseCharacterID = stream.readByte();
+	_isDialogueExitScene = (NancyFlag)stream.readByte();
+	_doNotPop = (NancyFlag)stream.readByte();
+	_sceneChange.readData(stream);
+
+	stream.seek(beginOffset + 0x69C);
+
+	uint16 numResponses = stream.readUint16LE();
+	rawText = new char[400];
+
+	if (numResponses > 0) {
+		for (uint i = 0; i < numResponses; ++i) {
+			_responses.push_back(ResponseStruct());
+			ResponseStruct &response = _responses[i];
+			response.conditionFlags.read(stream);
+			stream.read(rawText, 400);
+			UI::Textbox::assembleTextLine(rawText, response.text, 400);
+			readFilename(stream, response.soundName);
+			stream.skip(1);
+			response._sceneChange.readData(stream);
+			response.flagDesc.label = stream.readSint16LE();
+			response.flagDesc.flag = (NancyFlag)stream.readByte();
+
+			stream.skip(0x32);
+		}
+	}
+
+	delete[] rawText;
+
+	uint16 numSceneBranchStructs = stream.readUint16LE();
+	if (numSceneBranchStructs > 0) {
+		// TODO
+	}
+
+	uint16 numFlagsStructs = stream.readUint16LE();
+	if (numFlagsStructs > 0) {
+		for (uint16 i = 0; i < numFlagsStructs; ++i) {
+			_flagsStructs.push_back(FlagsStruct());
+			FlagsStruct &flagsStruct = _flagsStructs.back();
+			flagsStruct.conditions.read(stream);
+			flagsStruct.flagToSet.type = (ConditionFlag::ConditionType)stream.readByte();
+			flagsStruct.flagToSet.flag.label = stream.readSint16LE();
+			flagsStruct.flagToSet.flag.flag = (NancyFlag)stream.readByte();
+		}
+	}
 }
 
 void PlayPrimaryVideoChan0::execute() {
 	if (_activePrimaryVideo != this && _activePrimaryVideo != nullptr) {
-        return;
-    }
-
-    switch (_state) {
-    case kBegin:
-        init();
-        registerGraphics();
-        g_nancy->_sound->loadSound(_sound);
-        g_nancy->_sound->playSound(_sound);
-        _state = kRun;
-        _activePrimaryVideo = this;
-        // fall through
-    case kRun:
-        if (!_hasDrawnTextbox) {
-            _hasDrawnTextbox = true;
-            NancySceneState.getTextbox().clear();
-            NancySceneState.getTextbox().addTextLine(_text);
-
-            // Add responses when conditions have been satisfied
-            if (_conditionalResponseCharacterID != 10) {
-                addConditionalResponses();
-            }
-
-            if (_goodbyeResponseCharacterID != 10) {
-                addGoodbye();
-            }
-
-            for (uint i = 0; i < _responses.size(); ++i) {
-                auto &res = _responses[i];
-
-                if (res.conditionFlags.isSatisfied()) {
-                    NancySceneState.getTextbox().addTextLine(res.text);
-                }
-            }
-        }
-
-        if (!g_nancy->_sound->isSoundPlaying(_sound) && _decoder.endOfVideo()) {
-            g_nancy->_sound->stopSound(_sound);
-            
-            if (_responses.size() == 0) {
-                // NPC has finished talking with no responses available, auto-advance to next scene
-                _state = kActionTrigger;
-            } else {
-                // NPC has finished talking, we have responses
-                for (uint i = 0; i < 30; ++i) {
-                    if (NancySceneState.getLogicCondition(i, kTrue)) {
-                        _pickedResponse = i;
-                        break;
-                    }
-                }
-
-                if (_pickedResponse != -1) {
-                    // Player has picked response, play sound file and change _state
-                    _responseGenericSound.name = _responses[_pickedResponse].soundName;
-                    // TODO this is probably not correct
-                    g_nancy->_sound->loadSound(_responseGenericSound);
-                    g_nancy->_sound->playSound(_responseGenericSound);
-                    _state = kActionTrigger;
-                }
-            }
-        }
-        break;
-    case kActionTrigger:
-        // process flags structs
-        for (auto flags : _flagsStructs) {
-            if (flags.conditions.isSatisfied()) {
-                flags.flagToSet.set();
-            }
-        }
-        
-        if (_pickedResponse != -1) {
-            // Set response's event flag, if any
-            NancySceneState.setEventFlag(_responses[_pickedResponse].flagDesc);
-        }
-
-        if (!g_nancy->_sound->isSoundPlaying(_responseGenericSound)) {
-            g_nancy->_sound->stopSound(_responseGenericSound);
-            
-            if (_pickedResponse != -1) {
-                NancySceneState.changeScene(_responses[_pickedResponse]._sceneChange);
-            } else {
-                // Evaluate scene branch structs here
-
-                if (_isDialogueExitScene == kFalse) {
-                    NancySceneState.changeScene(_sceneChange);
-                } else if (_doNotPop == kFalse) {
-                    // Exit dialogue
-                    NancySceneState.popScene();
-                }
-            }
-            
-            finishExecution();
-        }
-
-        break;
-    }
+		return;
+	}
+
+	switch (_state) {
+	case kBegin:
+		init();
+		registerGraphics();
+		g_nancy->_sound->loadSound(_sound);
+		g_nancy->_sound->playSound(_sound);
+		_state = kRun;
+		_activePrimaryVideo = this;
+		// fall through
+	case kRun:
+		if (!_hasDrawnTextbox) {
+			_hasDrawnTextbox = true;
+			NancySceneState.getTextbox().clear();
+			NancySceneState.getTextbox().addTextLine(_text);
+
+			// Add responses when conditions have been satisfied
+			if (_conditionalResponseCharacterID != 10) {
+				addConditionalResponses();
+			}
+
+			if (_goodbyeResponseCharacterID != 10) {
+				addGoodbye();
+			}
+
+			for (uint i = 0; i < _responses.size(); ++i) {
+				auto &res = _responses[i];
+
+				if (res.conditionFlags.isSatisfied()) {
+					NancySceneState.getTextbox().addTextLine(res.text);
+				}
+			}
+		}
+
+		if (!g_nancy->_sound->isSoundPlaying(_sound) && _decoder.endOfVideo()) {
+			g_nancy->_sound->stopSound(_sound);
+
+			if (_responses.size() == 0) {
+				// NPC has finished talking with no responses available, auto-advance to next scene
+				_state = kActionTrigger;
+			} else {
+				// NPC has finished talking, we have responses
+				for (uint i = 0; i < 30; ++i) {
+					if (NancySceneState.getLogicCondition(i, kTrue)) {
+						_pickedResponse = i;
+						break;
+					}
+				}
+
+				if (_pickedResponse != -1) {
+					// Player has picked response, play sound file and change _state
+					_responseGenericSound.name = _responses[_pickedResponse].soundName;
+					// TODO this is probably not correct
+					g_nancy->_sound->loadSound(_responseGenericSound);
+					g_nancy->_sound->playSound(_responseGenericSound);
+					_state = kActionTrigger;
+				}
+			}
+		}
+		break;
+	case kActionTrigger:
+		// process flags structs
+		for (auto flags : _flagsStructs) {
+			if (flags.conditions.isSatisfied()) {
+				flags.flagToSet.set();
+			}
+		}
+
+		if (_pickedResponse != -1) {
+			// Set response's event flag, if any
+			NancySceneState.setEventFlag(_responses[_pickedResponse].flagDesc);
+		}
+
+		if (!g_nancy->_sound->isSoundPlaying(_responseGenericSound)) {
+			g_nancy->_sound->stopSound(_responseGenericSound);
+
+			if (_pickedResponse != -1) {
+				NancySceneState.changeScene(_responses[_pickedResponse]._sceneChange);
+			} else {
+				// Evaluate scene branch structs here
+
+				if (_isDialogueExitScene == kFalse) {
+					NancySceneState.changeScene(_sceneChange);
+				} else if (_doNotPop == kFalse) {
+					// Exit dialogue
+					NancySceneState.popScene();
+				}
+			}
+
+			finishExecution();
+		}
+
+		break;
+	}
 }
 
 void PlayPrimaryVideoChan0::addConditionalResponses() {
-    for (auto &res : nancy1ConditionalResponses) {
-        if (res.characterID == _conditionalResponseCharacterID) {
-            bool isSatisfied = true;
-            for (auto & cond : res.conditions) {
-                if (cond.label == -1) {
-                    break;
-                }
-
-                if (!NancySceneState.getEventFlag(cond.label, cond.flag)) {
-                    isSatisfied = false;
-                    break;
-                }
-            }
-
-            if (isSatisfied) {
-                Common::File file;
-                char snd[9];
-
-                file.open("game.exe");
-                file.seek(nancy1ResponseBaseFileOffset + res.fileOffset);
-                file.read(snd, 8);
-                snd[8] = '\0';
-
-                _responses.push_back(ResponseStruct());
-                ResponseStruct &newResponse = _responses.back();
-                newResponse.soundName = snd;
-                newResponse.text = file.readString();
-                newResponse._sceneChange.sceneID = res.sceneID;
-                newResponse._sceneChange.doNotStartSound = true;
-
-                file.close();
-            }
-        }
-    }
+	for (auto &res : nancy1ConditionalResponses) {
+		if (res.characterID == _conditionalResponseCharacterID) {
+			bool isSatisfied = true;
+			for (auto & cond : res.conditions) {
+				if (cond.label == -1) {
+					break;
+				}
+
+				if (!NancySceneState.getEventFlag(cond.label, cond.flag)) {
+					isSatisfied = false;
+					break;
+				}
+			}
+
+			if (isSatisfied) {
+				Common::File file;
+				char snd[9];
+
+				file.open("game.exe");
+				file.seek(nancy1ResponseBaseFileOffset + res.fileOffset);
+				file.read(snd, 8);
+				snd[8] = '\0';
+
+				_responses.push_back(ResponseStruct());
+				ResponseStruct &newResponse = _responses.back();
+				newResponse.soundName = snd;
+				newResponse.text = file.readString();
+				newResponse._sceneChange.sceneID = res.sceneID;
+				newResponse._sceneChange.doNotStartSound = true;
+
+				file.close();
+			}
+		}
+	}
 }
 
 void PlayPrimaryVideoChan0::addGoodbye() {
-    for (auto &res : nancy1Goodbyes) {
-        if (res.characterID == _goodbyeResponseCharacterID) {
-            Common::File file;
-            char snd[9];
-
-            file.open("game.exe");
-            file.seek(nancy1ResponseBaseFileOffset + res.fileOffset);
-            file.read(snd, 8);
-            snd[8] = '\0';
-
-            _responses.push_back(ResponseStruct());
-            ResponseStruct &newResponse = _responses.back();
-            newResponse.soundName = snd;
-            newResponse.text = file.readString();
-            // response is picked randomly
-            newResponse._sceneChange.sceneID = res.sceneIDs[g_nancy->_randomSource->getRandomNumber(3)];
-            newResponse._sceneChange.doNotStartSound = true;
-
-            file.close();
-        }
-    }
+	for (auto &res : nancy1Goodbyes) {
+		if (res.characterID == _goodbyeResponseCharacterID) {
+			Common::File file;
+			char snd[9];
+
+			file.open("game.exe");
+			file.seek(nancy1ResponseBaseFileOffset + res.fileOffset);
+			file.read(snd, 8);
+			snd[8] = '\0';
+
+			_responses.push_back(ResponseStruct());
+			ResponseStruct &newResponse = _responses.back();
+			newResponse.soundName = snd;
+			newResponse.text = file.readString();
+			// response is picked randomly
+			newResponse._sceneChange.sceneID = res.sceneIDs[g_nancy->_randomSource->getRandomNumber(3)];
+			newResponse._sceneChange.doNotStartSound = true;
+
+			file.close();
+		}
+	}
 }
 
 } // End of namespace Action
diff --git a/engines/nancy/action/primaryvideo.h b/engines/nancy/action/primaryvideo.h
index c5fcb13fc8..5861d9f612 100644
--- a/engines/nancy/action/primaryvideo.h
+++ b/engines/nancy/action/primaryvideo.h
@@ -44,80 +44,80 @@ class PlayPrimaryVideoChan0 : public ActionRecord, public RenderObject {
 struct ConditionFlag {
 enum ConditionType : byte { kNone = 0, kEventFlags = 1, kInventory = 2 };
 
-    ConditionType type;
-    EventFlagDescription flag;
-    bool orFlag;
+	ConditionType type;
+	EventFlagDescription flag;
+	bool orFlag;
 
-    void read(Common::SeekableReadStream &stream);
-    bool isSatisfied() const;
-    void set() const;
+	void read(Common::SeekableReadStream &stream);
+	bool isSatisfied() const;
+	void set() const;
 };
 
 struct ConditionFlags {
-    Common::Array<ConditionFlag> conditionFlags;
+	Common::Array<ConditionFlag> conditionFlags;
 
-    void read(Common::SeekableReadStream &stream);
-    bool isSatisfied() const;
+	void read(Common::SeekableReadStream &stream);
+	bool isSatisfied() const;
 };
 
 struct ResponseStruct {
-    ConditionFlags conditionFlags; // 0x01
-    Common::String text; // 0x06
-    Common::String soundName; // 0x196
-    SceneChangeDescription _sceneChange; // 0x1A0
-    EventFlagDescription flagDesc; // 0x1A8
+	ConditionFlags conditionFlags; // 0x01
+	Common::String text; // 0x06
+	Common::String soundName; // 0x196
+	SceneChangeDescription _sceneChange; // 0x1A0
+	EventFlagDescription flagDesc; // 0x1A8
 };
 
 struct FlagsStruct {
-    ConditionFlags conditions;
-    ConditionFlag flagToSet;
+	ConditionFlags conditions;
+	ConditionFlag flagToSet;
 };
 
 public:
-    PlayPrimaryVideoChan0(RenderObject &redrawFrom) : RenderObject(redrawFrom) {}
-    virtual ~PlayPrimaryVideoChan0();
+	PlayPrimaryVideoChan0(RenderObject &redrawFrom) : RenderObject(redrawFrom) {}
+	virtual ~PlayPrimaryVideoChan0();
 
-    virtual void init() override;
-    virtual void updateGraphics() override;
-    virtual void onPause(bool pause) override;
+	virtual void init() override;
+	virtual void updateGraphics() override;
+	virtual void onPause(bool pause) override;
 
-    virtual void readData(Common::SeekableReadStream &stream) override;
-    virtual void execute() override;
-    
-    // Functions for handling the built-in dialogue responses found in the executable
-    void addConditionalResponses();
-    void addGoodbye();
+	virtual void readData(Common::SeekableReadStream &stream) override;
+	virtual void execute() override;
 
-    Common::String _videoName; // 0x00
-    Common::Rect _src; // 0x1D
-    // _screenPosition 0x2D
-    Common::String _text; // 0x3D
+	// Functions for handling the built-in dialogue responses found in the executable
+	void addConditionalResponses();
+	void addGoodbye();
 
-    SoundDescription _sound; // 0x619
-    SoundDescription _responseGenericSound; // 0x63B
+	Common::String _videoName; // 0x00
+	Common::Rect _src; // 0x1D
+	// _screenPosition 0x2D
+	Common::String _text; // 0x3D
 
-    byte _conditionalResponseCharacterID; // 0x65E
-    byte _goodbyeResponseCharacterID; // 0x65F
-    NancyFlag _isDialogueExitScene; // 0x660
-    NancyFlag _doNotPop; // 0x661
-    SceneChangeDescription _sceneChange; // 0x662
+	SoundDescription _sound; // 0x619
+	SoundDescription _responseGenericSound; // 0x63B
 
-    Common::Array<ResponseStruct> _responses; // 0x69E
-    Common::Array<FlagsStruct> _flagsStructs; // 0x6AA
+	byte _conditionalResponseCharacterID; // 0x65E
+	byte _goodbyeResponseCharacterID; // 0x65F
+	NancyFlag _isDialogueExitScene; // 0x660
+	NancyFlag _doNotPop; // 0x661
+	SceneChangeDescription _sceneChange; // 0x662
 
-    AVFDecoder _decoder;
+	Common::Array<ResponseStruct> _responses; // 0x69E
+	Common::Array<FlagsStruct> _flagsStructs; // 0x6AA
 
-    bool _hasDrawnTextbox = false;
-    int16 _pickedResponse = -1;
+	AVFDecoder _decoder;
 
-    // Used to avoid clashes between multiple instances in the same scene
+	bool _hasDrawnTextbox = false;
+	int16 _pickedResponse = -1;
+
+	// Used to avoid clashes between multiple instances in the same scene
 	static PlayPrimaryVideoChan0 *_activePrimaryVideo;
 
 protected:
-    virtual Common::String getRecordTypeName() const override { return "PlayPrimaryVideoChan0"; }
-    
-    virtual uint16 getZOrder() const override { return 8; }
-    virtual bool isViewportRelative() const override { return true; }
+	virtual Common::String getRecordTypeName() const override { return "PlayPrimaryVideoChan0"; }
+
+	virtual uint16 getZOrder() const override { return 8; }
+	virtual bool isViewportRelative() const override { return true; }
 };
 
 } // End of namespace Action
diff --git a/engines/nancy/action/recordtypes.cpp b/engines/nancy/action/recordtypes.cpp
index 90a15de5c2..97e122617b 100644
--- a/engines/nancy/action/recordtypes.cpp
+++ b/engines/nancy/action/recordtypes.cpp
@@ -41,572 +41,572 @@ namespace Nancy {
 namespace Action {
 
 void SceneChange::readData(Common::SeekableReadStream &stream) {
-    _sceneChange.readData(stream);
+	_sceneChange.readData(stream);
 }
 
 void SceneChange::execute() {
-    NancySceneState.changeScene(_sceneChange);
-    _isDone = true;
+	NancySceneState.changeScene(_sceneChange);
+	_isDone = true;
 }
 
 void HotMultiframeSceneChange::readData(Common::SeekableReadStream &stream) {
-    SceneChange::readData(stream);
-    uint16 numHotspots = stream.readUint16LE();
+	SceneChange::readData(stream);
+	uint16 numHotspots = stream.readUint16LE();
 
-    for (uint i = 0; i < numHotspots; ++i) {
-        _hotspots.push_back(HotspotDescription());
-        HotspotDescription &newDesc = _hotspots[i];
-        newDesc.readData(stream);
-    }
+	for (uint i = 0; i < numHotspots; ++i) {
+		_hotspots.push_back(HotspotDescription());
+		HotspotDescription &newDesc = _hotspots[i];
+		newDesc.readData(stream);
+	}
 }
 
 void HotMultiframeSceneChange::execute() {
-    switch (_state) {
-    case kBegin:
-        // turn main rendering on
-        _state = kRun;
-        // fall through
-    case kRun:
-        _hasHotspot = false;
-        for (uint i = 0; i < _hotspots.size(); ++i) {
-            if (_hotspots[i].frameID == NancySceneState.getSceneInfo().frameID) {
-                _hasHotspot = true;
-                _hotspot = _hotspots[i].coords;
-            }
-        }
-        break;
-    case kActionTrigger:
-        SceneChange::execute();
-        break;
-    }
+	switch (_state) {
+	case kBegin:
+		// turn main rendering on
+		_state = kRun;
+		// fall through
+	case kRun:
+		_hasHotspot = false;
+		for (uint i = 0; i < _hotspots.size(); ++i) {
+			if (_hotspots[i].frameID == NancySceneState.getSceneInfo().frameID) {
+				_hasHotspot = true;
+				_hotspot = _hotspots[i].coords;
+			}
+		}
+		break;
+	case kActionTrigger:
+		SceneChange::execute();
+		break;
+	}
 }
 
 void Hot1FrSceneChange::readData(Common::SeekableReadStream &stream) {
-    SceneChange::readData(stream);
-    _hotspotDesc.readData(stream);
+	SceneChange::readData(stream);
+	_hotspotDesc.readData(stream);
 }
 
 void Hot1FrSceneChange::execute() {
-    switch (_state) {
-    case kBegin:
-        _hotspot = _hotspotDesc.coords;
-        _state = kRun;
-        // fall through
-    case kRun:
-        if (_hotspotDesc.frameID == NancySceneState.getSceneInfo().frameID) {
-            _hasHotspot = true;
-        } else {
-            _hasHotspot = false;
-        }
-        break;
-    case kActionTrigger:
-        SceneChange::execute();
-        break;
-    }
+	switch (_state) {
+	case kBegin:
+		_hotspot = _hotspotDesc.coords;
+		_state = kRun;
+		// fall through
+	case kRun:
+		if (_hotspotDesc.frameID == NancySceneState.getSceneInfo().frameID) {
+			_hasHotspot = true;
+		} else {
+			_hasHotspot = false;
+		}
+		break;
+	case kActionTrigger:
+		SceneChange::execute();
+		break;
+	}
 }
 
 void HotMultiframeMultisceneChange::readData(Common::SeekableReadStream &stream) {
-    stream.seek(0x14, SEEK_CUR);
-    uint size = stream.readUint16LE() * 0x12;
-    stream.skip(size);
+	stream.seek(0x14, SEEK_CUR);
+	uint size = stream.readUint16LE() * 0x12;
+	stream.skip(size);
 }
 
 void StartFrameNextScene::readData(Common::SeekableReadStream &stream) {
-    stream.skip(4);
+	stream.skip(4);
 }
 
 void StartStopPlayerScrolling::readData(Common::SeekableReadStream &stream) {
-    stream.skip(1);
+	stream.skip(1);
 }
 
 void MapCall::readData(Common::SeekableReadStream &stream) {
-    stream.skip(1);
+	stream.skip(1);
 }
 
 void MapCall::execute() {
-    _execType = kRepeating;
-    NancySceneState.requestStateChange(NancyEngine::kMap);
-    finishExecution();
+	_execType = kRepeating;
+	NancySceneState.requestStateChange(NancyEngine::kMap);
+	finishExecution();
 }
 
 void MapCallHot1Fr::readData(Common::SeekableReadStream &stream) {
-    stream.skip(0x12);
+	stream.skip(0x12);
 }
 
 void MapCallHot1Fr::execute() {
-    switch (_state) {
-    case kBegin:
-        _hotspot = _hotspotDesc.coords;
-        _state = kRun;
-        // fall through
-    case kRun:
-        if (_hotspotDesc.frameID == NancySceneState.getSceneInfo().frameID) {
-            _hasHotspot = true;
-        }
-        break;
-    case kActionTrigger:
-        MapCall::execute();
-        break;
-    }
+	switch (_state) {
+	case kBegin:
+		_hotspot = _hotspotDesc.coords;
+		_state = kRun;
+		// fall through
+	case kRun:
+		if (_hotspotDesc.frameID == NancySceneState.getSceneInfo().frameID) {
+			_hasHotspot = true;
+		}
+		break;
+	case kActionTrigger:
+		MapCall::execute();
+		break;
+	}
 }
 
 void MapCallHotMultiframe::readData(Common::SeekableReadStream &stream) {
-    uint16 numDescs = stream.readUint16LE();
-    for (uint i = 0; i < numDescs; ++i) {
-        _hotspots.push_back(HotspotDescription());
-        _hotspots[i].readData(stream);
-    }
+	uint16 numDescs = stream.readUint16LE();
+	for (uint i = 0; i < numDescs; ++i) {
+		_hotspots.push_back(HotspotDescription());
+		_hotspots[i].readData(stream);
+	}
 }
 
 void MapCallHotMultiframe::execute() {
-    switch (_state) {
-    case kBegin:
-        _state = kRun;
-        // fall through
-    case kRun:
-        _hasHotspot = false;
-        for (uint i = 0; i < _hotspots.size(); ++i) {
-            if (_hotspots[i].frameID == NancySceneState.getSceneInfo().frameID) {
-                _hasHotspot = true;
-                _hotspot = _hotspots[i].coords;
-            }
-        }
-        break;
-    case kActionTrigger:
-        MapCall::execute();
-        break;  
-    }
+	switch (_state) {
+	case kBegin:
+		_state = kRun;
+		// fall through
+	case kRun:
+		_hasHotspot = false;
+		for (uint i = 0; i < _hotspots.size(); ++i) {
+			if (_hotspots[i].frameID == NancySceneState.getSceneInfo().frameID) {
+				_hasHotspot = true;
+				_hotspot = _hotspots[i].coords;
+			}
+		}
+		break;
+	case kActionTrigger:
+		MapCall::execute();
+		break;
+	}
 }
 
 void MapLocationAccess::readData(Common::SeekableReadStream &stream) {
-    stream.skip(4);
+	stream.skip(4);
 }
 
 void MapSound::readData(Common::SeekableReadStream &stream) {
-    stream.skip(0x10);
+	stream.skip(0x10);
 }
 
 void MapAviOverride::readData(Common::SeekableReadStream &stream) {
-    stream.skip(2);
+	stream.skip(2);
 }
 
 void MapAviOverrideOff::readData(Common::SeekableReadStream &stream) {
-    stream.skip(1);
+	stream.skip(1);
 }
 
 void TextBoxWrite::readData(Common::SeekableReadStream &stream) {
-    uint16 size = stream.readUint16LE();
-    stream.skip(size);
+	uint16 size = stream.readUint16LE();
+	stream.skip(size);
 
-    if (size > 10000) {
-        error("Action Record atTextboxWrite has too many text box chars: %d", size);;
-    }
+	if (size > 10000) {
+		error("Action Record atTextboxWrite has too many text box chars: %d", size);;
+	}
 }
 
 void TextBoxClear::readData(Common::SeekableReadStream &stream) {
-    stream.skip(1);
+	stream.skip(1);
 }
 
 void BumpPlayerClock::readData(Common::SeekableReadStream &stream) {
-    stream.skip(5);
+	stream.skip(5);
 }
 
 void SaveContinueGame::readData(Common::SeekableReadStream &stream) {
-    stream.skip(1);
+	stream.skip(1);
 }
 
 void TurnOffMainRendering::readData(Common::SeekableReadStream &stream) {
-    stream.skip(1);
+	stream.skip(1);
 }
 
 void TurnOnMainRendering::readData(Common::SeekableReadStream &stream) {
-    stream.skip(1);
+	stream.skip(1);
 }
 
 void ResetAndStartTimer::readData(Common::SeekableReadStream &stream) {
-    stream.skip(1);
+	stream.skip(1);
 }
 
 void ResetAndStartTimer::execute() {
-    NancySceneState.resetAndStartTimer();
-    _isDone = true;
+	NancySceneState.resetAndStartTimer();
+	_isDone = true;
 }
 
 void StopTimer::readData(Common::SeekableReadStream &stream) {
-    stream.skip(1);
+	stream.skip(1);
 }
 
 void StopTimer::execute() {
-    NancySceneState.stopTimer();
-    _isDone = true;
+	NancySceneState.stopTimer();
+	_isDone = true;
 }
 
 void EventFlags::readData(Common::SeekableReadStream &stream) {
-    _flags.readData(stream);
+	_flags.readData(stream);
 }
 
 void EventFlags::execute() {
-    _flags.execute();
-    _isDone = true;
+	_flags.execute();
+	_isDone = true;
 }
 
 void EventFlagsMultiHS::readData(Common::SeekableReadStream &stream) {
-    EventFlags::readData(stream);
-    uint16 numHotspots = stream.readUint16LE();
+	EventFlags::readData(stream);
+	uint16 numHotspots = stream.readUint16LE();
 
-    for (uint16 i = 0; i < numHotspots; ++i) {
-        _hotspots.push_back(HotspotDescription());
-        HotspotDescription &newDesc = _hotspots[i];
-        newDesc.readData(stream);
-    }
+	for (uint16 i = 0; i < numHotspots; ++i) {
+		_hotspots.push_back(HotspotDescription());
+		HotspotDescription &newDesc = _hotspots[i];
+		newDesc.readData(stream);
+	}
 }
 
 void EventFlagsMultiHS::execute() {
-    switch (_state) {
-    case kBegin:
-        // turn main rendering on
-        _state = kRun;
-        // fall through
-    case kRun:
-        _hasHotspot = false;
-
-        for (uint i = 0; i < _hotspots.size(); ++i) {
-            if (_hotspots[i].frameID == NancySceneState.getSceneInfo().frameID) {
-                _hasHotspot = true;
-                _hotspot = _hotspots[i].coords;
-            }
-        }
-
-        break;
-    case kActionTrigger:
-        _hasHotspot = false;
-        EventFlags::execute();
-        finishExecution();
-        break;
-    }
+	switch (_state) {
+	case kBegin:
+		// turn main rendering on
+		_state = kRun;
+		// fall through
+	case kRun:
+		_hasHotspot = false;
+
+		for (uint i = 0; i < _hotspots.size(); ++i) {
+			if (_hotspots[i].frameID == NancySceneState.getSceneInfo().frameID) {
+				_hasHotspot = true;
+				_hotspot = _hotspots[i].coords;
+			}
+		}
+
+		break;
+	case kActionTrigger:
+		_hasHotspot = false;
+		EventFlags::execute();
+		finishExecution();
+		break;
+	}
 }
 
 void LoseGame::readData(Common::SeekableReadStream &stream) {
-    stream.skip(1);
+	stream.skip(1);
 }
 
 void LoseGame::execute() {
-    g_nancy->_sound->stopAndUnloadSpecificSounds();
-    g_nancy->setState(NancyEngine::kMainMenu);
-    NancySceneState.resetStateToInit();
-    _isDone = true;
+	g_nancy->_sound->stopAndUnloadSpecificSounds();
+	g_nancy->setState(NancyEngine::kMainMenu);
+	NancySceneState.resetStateToInit();
+	_isDone = true;
 }
 
 void PushScene::readData(Common::SeekableReadStream &stream) {
-    stream.skip(1);
+	stream.skip(1);
 }
 
 void PopScene::readData(Common::SeekableReadStream &stream) {
-    stream.skip(1);
+	stream.skip(1);
 }
 
 void WinGame::readData(Common::SeekableReadStream &stream) {
-    stream.skip(1);
+	stream.skip(1);
 }
 
 void WinGame::execute() {
-    g_nancy->_sound->stopAndUnloadSpecificSounds();
-    g_nancy->setState(NancyEngine::kCredits, NancyEngine::kMainMenu);
-    
-    // TODO replace with destroy()?
-    NancySceneState.resetStateToInit();
-    _isDone = true;
+	g_nancy->_sound->stopAndUnloadSpecificSounds();
+	g_nancy->setState(NancyEngine::kCredits, NancyEngine::kMainMenu);
+
+	// TODO replace with destroy()?
+	NancySceneState.resetStateToInit();
+	_isDone = true;
 }
 
 void AddInventoryNoHS::readData(Common::SeekableReadStream &stream) {
-    _itemID = stream.readUint16LE();
+	_itemID = stream.readUint16LE();
 }
 
 void AddInventoryNoHS::execute() {
-    if (NancySceneState.hasItem(_itemID) == kFalse) {
-        NancySceneState.addItemToInventory(_itemID);
-    }
+	if (NancySceneState.hasItem(_itemID) == kFalse) {
+		NancySceneState.addItemToInventory(_itemID);
+	}
 
-    _isDone = true;
+	_isDone = true;
 }
 
 void RemoveInventoryNoHS::readData(Common::SeekableReadStream &stream) {
-    stream.skip(2);
+	stream.skip(2);
 }
 
 void DifficultyLevel::readData(Common::SeekableReadStream &stream) {
-    _difficulty = stream.readUint16LE();
-    _flag.label = stream.readSint16LE();
-    _flag.flag = (NancyFlag)stream.readUint16LE();
+	_difficulty = stream.readUint16LE();
+	_flag.label = stream.readSint16LE();
+	_flag.flag = (NancyFlag)stream.readUint16LE();
 }
 
 void DifficultyLevel::execute() {
-    NancySceneState.setDifficulty(_difficulty);
-    NancySceneState.setEventFlag(_flag);
-    _isDone = true;
+	NancySceneState.setDifficulty(_difficulty);
+	NancySceneState.setEventFlag(_flag);
+	_isDone = true;
 }
 
 void ShowInventoryItem::init() {
-    g_nancy->_resource->loadImage(_imageName, _fullSurface);
+	g_nancy->_resource->loadImage(_imageName, _fullSurface);
 
-    _drawSurface.create(_fullSurface, _bitmaps[0].src);
+	_drawSurface.create(_fullSurface, _bitmaps[0].src);
 
-    RenderObject::init();
+	RenderObject::init();
 }
 
 void ShowInventoryItem::readData(Common::SeekableReadStream &stream) {
-    _objectID = stream.readUint16LE();
-    readFilename(stream, _imageName);
+	_objectID = stream.readUint16LE();
+	readFilename(stream, _imageName);
 
-    uint16 numFrames = stream.readUint16LE();
+	uint16 numFrames = stream.readUint16LE();
 
-    for (uint i = 0; i < numFrames; ++i) {
-        _bitmaps.push_back(BitmapDescription());
-        _bitmaps[i].readData(stream);
-    }
+	for (uint i = 0; i < numFrames; ++i) {
+		_bitmaps.push_back(BitmapDescription());
+		_bitmaps[i].readData(stream);
+	}
 }
 
 void ShowInventoryItem::execute() {
-    switch (_state) {
-    case kBegin:
-        init();
-        registerGraphics();
-        _state = kRun;
-        // fall through
-    case kRun: {
-        int newFrame = -1;
-
-        for (uint i = 0; i < _bitmaps.size(); ++i) {
-            if (_bitmaps[i].frameID == NancySceneState.getSceneInfo().frameID) {
-                newFrame = i;
-                break;
-            }
-        }
-
-        if (newFrame != _drawnFrameID) {
-            _drawnFrameID = newFrame;
-
-            if (newFrame != -1) {
-                _hasHotspot = true;
-                _hotspot = _bitmaps[newFrame].dest;
-                _drawSurface.create(_fullSurface, _bitmaps[newFrame].src);
-                _screenPosition = _bitmaps[newFrame].dest;
-                setVisible(true);
-            } else {
-                _hasHotspot = false;
-                setVisible(false);
-            }
-        }
-                
-        break;
-    }
-    case kActionTrigger:
-        g_nancy->_sound->playSound(24); // Hardcoded by original engine
-        NancySceneState.addItemToInventory(_objectID);
-        setVisible(false);
-        _hasHotspot = false;
-        finishExecution();
-        break;
-    }
+	switch (_state) {
+	case kBegin:
+		init();
+		registerGraphics();
+		_state = kRun;
+		// fall through
+	case kRun: {
+		int newFrame = -1;
+
+		for (uint i = 0; i < _bitmaps.size(); ++i) {
+			if (_bitmaps[i].frameID == NancySceneState.getSceneInfo().frameID) {
+				newFrame = i;
+				break;
+			}
+		}
+
+		if (newFrame != _drawnFrameID) {
+			_drawnFrameID = newFrame;
+
+			if (newFrame != -1) {
+				_hasHotspot = true;
+				_hotspot = _bitmaps[newFrame].dest;
+				_drawSurface.create(_fullSurface, _bitmaps[newFrame].src);
+				_screenPosition = _bitmaps[newFrame].dest;
+				setVisible(true);
+			} else {
+				_hasHotspot = false;
+				setVisible(false);
+			}
+		}
+
+		break;
+	}
+	case kActionTrigger:
+		g_nancy->_sound->playSound(24); // Hardcoded by original engine
+		NancySceneState.addItemToInventory(_objectID);
+		setVisible(false);
+		_hasHotspot = false;
+		finishExecution();
+		break;
+	}
 }
 
 void ShowInventoryItem::onPause(bool pause) {
-    if (pause) {
-        registerGraphics();
-    }
+	if (pause) {
+		registerGraphics();
+	}
 }
 
 void PlayDigiSoundAndDie::readData(Common::SeekableReadStream &stream) {
-    _sound.read(stream, SoundDescription::kDIGI);
-    _sceneChange.readData(stream);
-    _flagOnTrigger.label = stream.readSint16LE();
-    _flagOnTrigger.flag = (NancyFlag)stream.readByte();
-    stream.skip(2);
+	_sound.read(stream, SoundDescription::kDIGI);
+	_sceneChange.readData(stream);
+	_flagOnTrigger.label = stream.readSint16LE();
+	_flagOnTrigger.flag = (NancyFlag)stream.readByte();
+	stream.skip(2);
 }
 
 void PlayDigiSoundAndDie::execute() {
-    switch (_state) {
-    case kBegin:
-        g_nancy->_sound->loadSound(_sound);
-        g_nancy->_sound->playSound(_sound);
-        _state = kRun;
-        break;
-    case kRun:
-        if (!g_nancy->_sound->isSoundPlaying(_sound)) {
-            _state = kActionTrigger;
-        }
-
-        break;
-    case kActionTrigger:
-        if (_sceneChange.sceneID != 9999) {
-            NancySceneState.changeScene(_sceneChange);
-        }
-        
-        NancySceneState.setEventFlag(_flagOnTrigger);
-        g_nancy->_sound->stopSound(_sound);
-
-        finishExecution();
-        break;
-    }
+	switch (_state) {
+	case kBegin:
+		g_nancy->_sound->loadSound(_sound);
+		g_nancy->_sound->playSound(_sound);
+		_state = kRun;
+		break;
+	case kRun:
+		if (!g_nancy->_sound->isSoundPlaying(_sound)) {
+			_state = kActionTrigger;
+		}
+
+		break;
+	case kActionTrigger:
+		if (_sceneChange.sceneID != 9999) {
+			NancySceneState.changeScene(_sceneChange);
+		}
+
+		NancySceneState.setEventFlag(_flagOnTrigger);
+		g_nancy->_sound->stopSound(_sound);
+
+		finishExecution();
+		break;
+	}
 }
 
 void PlaySoundPanFrameAnchorAndDie::readData(Common::SeekableReadStream &stream) {
-    stream.skip(0x20);
+	stream.skip(0x20);
 }
 
 void PlaySoundMultiHS::readData(Common::SeekableReadStream &stream) {
-    _sound.read(stream, SoundDescription::kNormal);
-    _sceneChange.readData(stream);
-    _flag.label = stream.readSint16LE();
-    _flag.flag = (NancyFlag)stream.readByte();
-    stream.skip(2);
-    uint16 numHotspots = stream.readUint16LE();
+	_sound.read(stream, SoundDescription::kNormal);
+	_sceneChange.readData(stream);
+	_flag.label = stream.readSint16LE();
+	_flag.flag = (NancyFlag)stream.readByte();
+	stream.skip(2);
+	uint16 numHotspots = stream.readUint16LE();
 
-    for (uint i = 0; i < numHotspots; ++i) {
-        _hotspots.push_back(HotspotDescription());
-        _hotspots.back().frameID = stream.readUint16LE();
-        readRect(stream, _hotspots.back().coords);
-    }
+	for (uint i = 0; i < numHotspots; ++i) {
+		_hotspots.push_back(HotspotDescription());
+		_hotspots.back().frameID = stream.readUint16LE();
+		readRect(stream, _hotspots.back().coords);
+	}
 }
 
 void PlaySoundMultiHS::execute() {
-    switch (_state) {
-    case kBegin:
-        _state = kRun;
-        // fall through
-    case kRun: {
-        _hasHotspot = false;
-        uint currentFrame = NancySceneState.getSceneInfo().frameID;
-
-        for (uint i = 0; i < _hotspots.size(); ++i) {
-            if (_hotspots[i].frameID == currentFrame) {
-                _hotspot = _hotspots[i].coords;
-                _hasHotspot = true;
-                break;
-            }
-        }
-
-        break;
-    }
-    case kActionTrigger:
-        g_nancy->_sound->loadSound(_sound);
-        g_nancy->_sound->playSound(_sound);
-        NancySceneState.changeScene(_sceneChange);
-        NancySceneState.setEventFlag(_flag);
-        finishExecution();
-        break;
-    }
+	switch (_state) {
+	case kBegin:
+		_state = kRun;
+		// fall through
+	case kRun: {
+		_hasHotspot = false;
+		uint currentFrame = NancySceneState.getSceneInfo().frameID;
+
+		for (uint i = 0; i < _hotspots.size(); ++i) {
+			if (_hotspots[i].frameID == currentFrame) {
+				_hotspot = _hotspots[i].coords;
+				_hasHotspot = true;
+				break;
+			}
+		}
+
+		break;
+	}
+	case kActionTrigger:
+		g_nancy->_sound->loadSound(_sound);
+		g_nancy->_sound->playSound(_sound);
+		NancySceneState.changeScene(_sceneChange);
+		NancySceneState.setEventFlag(_flag);
+		finishExecution();
+		break;
+	}
 }
 
 void HintSystem::readData(Common::SeekableReadStream &stream) {
-    _characterID = stream.readByte();
-    _genericSound.read(stream, SoundDescription::kNormal);
+	_characterID = stream.readByte();
+	_genericSound.read(stream, SoundDescription::kNormal);
 }
 
 void HintSystem::execute() {
-    switch (_state) {
-    case kBegin:
-        if (NancySceneState.getHintsRemaining() > 0) {
-            selectHint();
-        } else {
-            getHint(0, NancySceneState.getDifficulty());
-        }
-
-        NancySceneState.getTextbox().clear();
-        NancySceneState.getTextbox().addTextLine(_text);
-
-        g_nancy->_sound->loadSound(_genericSound);
-        g_nancy->_sound->playSound(_genericSound);
-        _state = kRun;
-        break;
-    case kRun:
-        if (!g_nancy->_sound->isSoundPlaying(_genericSound)) {
-            g_nancy->_sound->stopSound(_genericSound);
-            _state = kActionTrigger;
-        } else {
-            break;
-        }
-
-        // fall through
-    case kActionTrigger:
-        NancySceneState.useHint(_hintID, _hintWeight);
-        NancySceneState.getTextbox().clear();
-
-        NancySceneState.changeScene(_sceneChange);
-
-        _isDone = true;
-        break;
-    }
+	switch (_state) {
+	case kBegin:
+		if (NancySceneState.getHintsRemaining() > 0) {
+			selectHint();
+		} else {
+			getHint(0, NancySceneState.getDifficulty());
+		}
+
+		NancySceneState.getTextbox().clear();
+		NancySceneState.getTextbox().addTextLine(_text);
+
+		g_nancy->_sound->loadSound(_genericSound);
+		g_nancy->_sound->playSound(_genericSound);
+		_state = kRun;
+		break;
+	case kRun:
+		if (!g_nancy->_sound->isSoundPlaying(_genericSound)) {
+			g_nancy->_sound->stopSound(_genericSound);
+			_state = kActionTrigger;
+		} else {
+			break;
+		}
+
+		// fall through
+	case kActionTrigger:
+		NancySceneState.useHint(_hintID, _hintWeight);
+		NancySceneState.getTextbox().clear();
+
+		NancySceneState.changeScene(_sceneChange);
+
+		_isDone = true;
+		break;
+	}
 }
 
 void HintSystem::selectHint() {
-    for (auto &hint : nancy1Hints) {
-        if (hint.characterID != _characterID) {
-            continue;
-        }
-
-        bool satisfied = true;
-
-        for (auto &flag : hint.flagConditions) {
-            if (flag.label == -1) {
-                break;
-            }
-            
-            if (!NancySceneState.getEventFlag(flag.label, flag.flag)) {
-                satisfied = false;
-                break;
-            }
-        }
-
-        for (auto &inv : hint.inventoryCondition) {
-            if (inv.label == -1) {
-                break;
-            }
-
-            if (NancySceneState.hasItem(inv.label) != inv.flag) {
-                satisfied = false;
-                break;
-            }
-        }
-
-        if (satisfied) {
-            getHint(hint.hintID, NancySceneState.getDifficulty());
-            break;
-        }
-    }
+	for (auto &hint : nancy1Hints) {
+		if (hint.characterID != _characterID) {
+			continue;
+		}
+
+		bool satisfied = true;
+
+		for (auto &flag : hint.flagConditions) {
+			if (flag.label == -1) {
+				break;
+			}
+
+			if (!NancySceneState.getEventFlag(flag.label, flag.flag)) {
+				satisfied = false;
+				break;
+			}
+		}
+
+		for (auto &inv : hint.inventoryCondition) {
+			if (inv.label == -1) {
+				break;
+			}
+
+			if (NancySceneState.hasItem(inv.label) != inv.flag) {
+				satisfied = false;
+				break;
+			}
+		}
+
+		if (satisfied) {
+			getHint(hint.hintID, NancySceneState.getDifficulty());
+			break;
+		}
+	}
 }
 
 void HintSystem::getHint(uint hint, uint difficulty) {
-    uint fileOffset;
-    if (_characterID < 3) {
-        fileOffset = nancy1HintOffsets[_characterID];
-    }
+	uint fileOffset;
+	if (_characterID < 3) {
+		fileOffset = nancy1HintOffsets[_characterID];
+	}
 
-    fileOffset += 0x288 * hint;
+	fileOffset += 0x288 * hint;
 
-    Common::File file;
-    file.open("game.exe");
-    file.seek(fileOffset);
+	Common::File file;
+	file.open("game.exe");
+	file.seek(fileOffset);
 
-    _hintID = file.readSint16LE();
-    _hintWeight = file.readSint16LE();
+	_hintID = file.readSint16LE();
+	_hintWeight = file.readSint16LE();
 
-    file.seek(difficulty * 10, SEEK_CUR);
+	file.seek(difficulty * 10, SEEK_CUR);
 
-    readFilename(file, _genericSound.name);
+	readFilename(file, _genericSound.name);
 
-    file.seek(-(difficulty * 10) - 10, SEEK_CUR);
-    file.seek(30 + difficulty * 200, SEEK_CUR);
+	file.seek(-(difficulty * 10) - 10, SEEK_CUR);
+	file.seek(30 + difficulty * 200, SEEK_CUR);
 
-    char textBuf[200];
-    file.read(textBuf, 200);
-    textBuf[199] = '\0';
-    _text = textBuf;
+	char textBuf[200];
+	file.read(textBuf, 200);
+	textBuf[199] = '\0';
+	_text = textBuf;
 
-    file.seek(-(difficulty * 200) - 200, SEEK_CUR);
-    file.seek(600, SEEK_CUR);
+	file.seek(-(difficulty * 200) - 200, SEEK_CUR);
+	file.seek(600, SEEK_CUR);
 
-    _sceneChange.readData(file);
+	_sceneChange.readData(file);
 }
 
 }
diff --git a/engines/nancy/action/recordtypes.h b/engines/nancy/action/recordtypes.h
index 0ab9a0544c..b653ffc5cf 100644
--- a/engines/nancy/action/recordtypes.h
+++ b/engines/nancy/action/recordtypes.h
@@ -41,368 +41,368 @@ namespace Action {
 
 class SceneChange : public ActionRecord {
 public:
-    virtual void readData(Common::SeekableReadStream &stream) override;
-    virtual void execute() override;
+	virtual void readData(Common::SeekableReadStream &stream) override;
+	virtual void execute() override;
 
-    SceneChangeDescription _sceneChange;
+	SceneChangeDescription _sceneChange;
 
 protected:
-    virtual Common::String getRecordTypeName() const override { return "SceneChange"; }
+	virtual Common::String getRecordTypeName() const override { return "SceneChange"; }
 };
 
 class HotMultiframeSceneChange : public SceneChange {
 public:
-    virtual void readData(Common::SeekableReadStream &stream) override;
-    virtual void execute() override;
+	virtual void readData(Common::SeekableReadStream &stream) override;
+	virtual void execute() override;
 
-    Common::Array<HotspotDescription> _hotspots;
+	Common::Array<HotspotDescription> _hotspots;
 
 protected:
-    virtual Common::String getRecordTypeName() const override { return "HotMultiframeSceneChange"; }
+	virtual Common::String getRecordTypeName() const override { return "HotMultiframeSceneChange"; }
 };
 
 class Hot1FrSceneChange : public SceneChange {
 public:
-    virtual void readData(Common::SeekableReadStream &stream) override;
-    virtual void execute() override;
+	virtual void readData(Common::SeekableReadStream &stream) override;
+	virtual void execute() override;
 
-    HotspotDescription _hotspotDesc;
+	HotspotDescription _hotspotDesc;
 
 protected:
-    virtual Common::String getRecordTypeName() const override { return "Hot1FrSceneChange"; }
+	virtual Common::String getRecordTypeName() const override { return "Hot1FrSceneChange"; }
 };
 
 class Hot1FrExitSceneChange : public Hot1FrSceneChange {
-    virtual CursorManager::CursorType getHoverCursor() const override { return CursorManager::kExitArrow; }
+	virtual CursorManager::CursorType getHoverCursor() const override { return CursorManager::kExitArrow; }
 
 protected:
-    virtual Common::String getRecordTypeName() const override { return "Hot1FrExitSceneChange"; }
+	virtual Common::String getRecordTypeName() const override { return "Hot1FrExitSceneChange"; }
 };
 
 class HotMultiframeMultisceneChange : public ActionRecord {
 public:
-    virtual void readData(Common::SeekableReadStream &stream) override;
+	virtual void readData(Common::SeekableReadStream &stream) override;
 
 protected:
-    virtual Common::String getRecordTypeName() const override { return "HotMultiframeMultisceneChange"; }
+	virtual Common::String getRecordTypeName() const override { return "HotMultiframeMultisceneChange"; }
 };
 
 class StartFrameNextScene : public ActionRecord {
 public:
-    virtual void readData(Common::SeekableReadStream &stream) override;
+	virtual void readData(Common::SeekableReadStream &stream) override;
 
 protected:
-    virtual Common::String getRecordTypeName() const override { return "StartFrameNextScene"; }
+	virtual Common::String getRecordTypeName() const override { return "StartFrameNextScene"; }
 };
 
 class StartStopPlayerScrolling : public ActionRecord {
 public:
-    virtual void readData(Common::SeekableReadStream &stream) override;
-    // TODO add a Start and Stop subclass
+	virtual void readData(Common::SeekableReadStream &stream) override;
+	// TODO add a Start and Stop subclass
 
-    byte _type = 0;
+	byte _type = 0;
 
 protected:
-    virtual Common::String getRecordTypeName() const override { return "StartStopPlayerScrolling"; }
+	virtual Common::String getRecordTypeName() const override { return "StartStopPlayerScrolling"; }
 };
 
 class MapCall : public ActionRecord {
 public:
-    virtual void readData(Common::SeekableReadStream &stream) override;
-    virtual void execute() override;
+	virtual void readData(Common::SeekableReadStream &stream) override;
+	virtual void execute() override;
 
-    virtual CursorManager::CursorType getHoverCursor() const override { return CursorManager::kExitArrow; }
+	virtual CursorManager::CursorType getHoverCursor() const override { return CursorManager::kExitArrow; }
 
 protected:
-    virtual Common::String getRecordTypeName() const override { return "MapCall"; }
+	virtual Common::String getRecordTypeName() const override { return "MapCall"; }
 };
 
 class MapCallHot1Fr : public MapCall {
 public:
-    virtual void readData(Common::SeekableReadStream &stream) override;
-    virtual void execute() override;
+	virtual void readData(Common::SeekableReadStream &stream) override;
+	virtual void execute() override;
+
+	HotspotDescription _hotspotDesc;
 
-    HotspotDescription _hotspotDesc;
-    
 protected:
-    virtual Common::String getRecordTypeName() const override { return "MapCallHot1Fr"; }
+	virtual Common::String getRecordTypeName() const override { return "MapCallHot1Fr"; }
 };
 
 class MapCallHotMultiframe : public MapCall {
 public:
-    virtual void readData(Common::SeekableReadStream &stream) override;
-    virtual void execute() override;
+	virtual void readData(Common::SeekableReadStream &stream) override;
+	virtual void execute() override;
+
+	Common::Array<HotspotDescription> _hotspots;
 
-    Common::Array<HotspotDescription> _hotspots;
-    
 protected:
-    virtual Common::String getRecordTypeName() const override { return "MapCallHotMultiframe"; }
+	virtual Common::String getRecordTypeName() const override { return "MapCallHotMultiframe"; }
 };
 
 class MapLocationAccess : public ActionRecord {
 public:
-    virtual void readData(Common::SeekableReadStream &stream) override;
-    
+	virtual void readData(Common::SeekableReadStream &stream) override;
+
 protected:
-    virtual Common::String getRecordTypeName() const override { return "MapLocationAccess"; }
+	virtual Common::String getRecordTypeName() const override { return "MapLocationAccess"; }
 };
 
 class MapSound : public ActionRecord {
 public:
-    virtual void readData(Common::SeekableReadStream &stream) override;
-    
+	virtual void readData(Common::SeekableReadStream &stream) override;
+
 protected:
-    virtual Common::String getRecordTypeName() const override { return "MapSound"; }
+	virtual Common::String getRecordTypeName() const override { return "MapSound"; }
 };
 
 class MapAviOverride : public ActionRecord {
 public:
-    virtual void readData(Common::SeekableReadStream &stream) override;
-    
+	virtual void readData(Common::SeekableReadStream &stream) override;
+
 protected:
-    virtual Common::String getRecordTypeName() const override { return "MapAviOverride"; }
+	virtual Common::String getRecordTypeName() const override { return "MapAviOverride"; }
 };
 
 class MapAviOverrideOff : public ActionRecord {
 public:
-    virtual void readData(Common::SeekableReadStream &stream) override;
-    
+	virtual void readData(Common::SeekableReadStream &stream) override;
+
 protected:
-    virtual Common::String getRecordTypeName() const override { return "MapAviOverrideOff"; }
+	virtual Common::String getRecordTypeName() const override { return "MapAviOverrideOff"; }
 };
 
 class TextBoxWrite : public ActionRecord {
 public:
-    virtual void readData(Common::SeekableReadStream &stream) override;
-    
+	virtual void readData(Common::SeekableReadStream &stream) override;
+
 protected:
-    virtual Common::String getRecordTypeName() const override { return "TextBoxWrite"; }
+	virtual Common::String getRecordTypeName() const override { return "TextBoxWrite"; }
 };
 
 class TextBoxClear : public ActionRecord {
 public:
-    virtual void readData(Common::SeekableReadStream &stream) override;
-    
+	virtual void readData(Common::SeekableReadStream &stream) override;
+
 protected:
-    virtual Common::String getRecordTypeName() const override { return "TextBoxClear"; }
+	virtual Common::String getRecordTypeName() const override { return "TextBoxClear"; }
 };
 
 class BumpPlayerClock : public ActionRecord {
 public:
-    virtual void readData(Common::SeekableReadStream &stream) override;
-    
+	virtual void readData(Common::SeekableReadStream &stream) override;
+
 protected:
-    virtual Common::String getRecordTypeName() const override { return "BumpPlayerClock"; }
+	virtual Common::String getRecordTypeName() const override { return "BumpPlayerClock"; }
 };
 
 class SaveContinueGame : public ActionRecord {
 public:
-    virtual void readData(Common::SeekableReadStream &stream) override;
-    
+	virtual void readData(Common::SeekableReadStream &stream) override;
+
 protected:
-    virtual Common::String getRecordTypeName() const override { return "SaveContinueGame"; }
+	virtual Common::String getRecordTypeName() const override { return "SaveContinueGame"; }
 };
 
 class TurnOffMainRendering : public ActionRecord {
 public:
-    virtual void readData(Common::SeekableReadStream &stream) override;
-    
+	virtual void readData(Common::SeekableReadStream &stream) override;
+
 protected:
-    virtual Common::String getRecordTypeName() const override { return "TurnOffMainRendering"; }
+	virtual Common::String getRecordTypeName() const override { return "TurnOffMainRendering"; }
 };
 
 class TurnOnMainRendering : public ActionRecord {
 public:
-    virtual void readData(Common::SeekableReadStream &stream) override;
-    
+	virtual void readData(Common::SeekableReadStream &stream) override;
+
 protected:
-    virtual Common::String getRecordTypeName() const override { return "TurnOnMainRendering"; }
+	virtual Common::String getRecordTypeName() const override { return "TurnOnMainRendering"; }
 };
 
 class ResetAndStartTimer : public ActionRecord {
 public:
-    virtual void readData(Common::SeekableReadStream &stream) override;
-    virtual void execute() override;
-    
+	virtual void readData(Common::SeekableReadStream &stream) override;
+	virtual void execute() override;
+
 protected:
-    virtual Common::String getRecordTypeName() const override { return "ResetAndStartTimer"; }
+	virtual Common::String getRecordTypeName() const override { return "ResetAndStartTimer"; }
 };
 
 class StopTimer : public ActionRecord {
 public:
-    virtual void readData(Common::SeekableReadStream &stream) override;
-    virtual void execute() override;
-    
+	virtual void readData(Common::SeekableReadStream &stream) override;
+	virtual void execute() override;
+
 protected:
-    virtual Common::String getRecordTypeName() const override { return "StopTimer"; }
+	virtual Common::String getRecordTypeName() const override { return "StopTimer"; }
 };
 
 class EventFlags : public ActionRecord {
 public:
-    virtual void readData(Common::SeekableReadStream &stream) override;
-    virtual void execute() override;
+	virtual void readData(Common::SeekableReadStream &stream) override;
+	virtual void execute() override;
+
+	MultiEventFlagDescription _flags;
 
-    MultiEventFlagDescription _flags;
-    
 protected:
-    virtual Common::String getRecordTypeName() const override { return "EventFlags"; }
+	virtual Common::String getRecordTypeName() const override { return "EventFlags"; }
 };
 
 class EventFlagsMultiHS : public EventFlags {
 public:
-    virtual void readData(Common::SeekableReadStream &stream) override;
-    virtual void execute() override;
+	virtual void readData(Common::SeekableReadStream &stream) override;
+	virtual void execute() override;
+
+	Common::Array<HotspotDescription> _hotspots;
 
-    Common::Array<HotspotDescription> _hotspots;
-    
 protected:
-    virtual Common::String getRecordTypeName() const override { return "EventFlagsMultiHS"; }
+	virtual Common::String getRecordTypeName() const override { return "EventFlagsMultiHS"; }
 };
 
 class LoseGame : public ActionRecord {
 public:
-    virtual void readData(Common::SeekableReadStream &stream) override;
-    virtual void execute() override;
-    
+	virtual void readData(Common::SeekableReadStream &stream) override;
+	virtual void execute() override;
+
 protected:
-    virtual Common::String getRecordTypeName() const override { return "LoseGame"; }
+	virtual Common::String getRecordTypeName() const override { return "LoseGame"; }
 };
 
 class PushScene : public ActionRecord {
 public:
-    virtual void readData(Common::SeekableReadStream &stream) override;
-    
+	virtual void readData(Common::SeekableReadStream &stream) override;
+
 protected:
-    virtual Common::String getRecordTypeName() const override { return "PushScene"; }
+	virtual Common::String getRecordTypeName() const override { return "PushScene"; }
 };
 
 class PopScene : public ActionRecord {
 public:
-    virtual void readData(Common::SeekableReadStream &stream) override;
-    
+	virtual void readData(Common::SeekableReadStream &stream) override;
+
 protected:
-    virtual Common::String getRecordTypeName() const override { return "PopScene"; }
+	virtual Common::String getRecordTypeName() const override { return "PopScene"; }
 };
 
 class WinGame : public ActionRecord {
 public:
-    virtual void readData(Common::SeekableReadStream &stream) override;
-    virtual void execute() override;
-    
+	virtual void readData(Common::SeekableReadStream &stream) override;
+	virtual void execute() override;
+
 protected:
-    virtual Common::String getRecordTypeName() const override { return "WinGame"; }
+	virtual Common::String getRecordTypeName() const override { return "WinGame"; }
 };
 
 class AddInventoryNoHS : public ActionRecord {
 public:
-    virtual void readData(Common::SeekableReadStream &stream) override;
-    virtual void execute() override;
-    
-    uint _itemID;
-    
+	virtual void readData(Common::SeekableReadStream &stream) override;
+	virtual void execute() override;
+
+	uint _itemID;
+
 protected:
-    virtual Common::String getRecordTypeName() const override { return "AddInventoryNoHS"; }
+	virtual Common::String getRecordTypeName() const override { return "AddInventoryNoHS"; }
 };
 
 class RemoveInventoryNoHS : public ActionRecord {
 public:
-    virtual void readData(Common::SeekableReadStream &stream) override;
-    
+	virtual void readData(Common::SeekableReadStream &stream) override;
+
 protected:
-    virtual Common::String getRecordTypeName() const override { return "RemoveInventoryNoHS"; }
+	virtual Common::String getRecordTypeName() const override { return "RemoveInventoryNoHS"; }
 };
 
 class DifficultyLevel : public ActionRecord {
 public:
-    virtual void readData(Common::SeekableReadStream &stream) override;
-    virtual void execute() override;
+	virtual void readData(Common::SeekableReadStream &stream) override;
+	virtual void execute() override;
+
+	uint16 _difficulty = 0;
+	EventFlagDescription _flag;
 
-    uint16 _difficulty = 0;
-    EventFlagDescription _flag;
-    
 protected:
-    virtual Common::String getRecordTypeName() const override { return "DifficultyLevel"; }
+	virtual Common::String getRecordTypeName() const override { return "DifficultyLevel"; }
 };
 
 class ShowInventoryItem : public ActionRecord, public RenderObject {
 public:
-    virtual void readData(Common::SeekableReadStream &stream) override;
-    virtual void execute() override;
+	virtual void readData(Common::SeekableReadStream &stream) override;
+	virtual void execute() override;
+
+	ShowInventoryItem(RenderObject &redrawFrom) : RenderObject(redrawFrom) {}
+	virtual ~ShowInventoryItem() { _fullSurface.free(); }
 
-    ShowInventoryItem(RenderObject &redrawFrom) : RenderObject(redrawFrom) {}
-    virtual ~ShowInventoryItem() { _fullSurface.free(); }
+	virtual void init() override;
+	virtual void onPause(bool pause) override;
 
-    virtual void init() override;
-    virtual void onPause(bool pause) override;
- 
-    uint16 _objectID = 0;
-    Common::String _imageName;
-    Common::Array<BitmapDescription> _bitmaps;
+	uint16 _objectID = 0;
+	Common::String _imageName;
+	Common::Array<BitmapDescription> _bitmaps;
+
+	int16 _drawnFrameID = -1;
+	Graphics::ManagedSurface _fullSurface;
 
-    int16 _drawnFrameID = -1;
-    Graphics::ManagedSurface _fullSurface;
-    
 protected:
-    virtual Common::String getRecordTypeName() const override { return "ShowInventoryItem"; }
+	virtual Common::String getRecordTypeName() const override { return "ShowInventoryItem"; }
 
-    virtual uint16 getZOrder() const override { return 9; }
-    virtual bool isViewportRelative() const override { return true; }
+	virtual uint16 getZOrder() const override { return 9; }
+	virtual bool isViewportRelative() const override { return true; }
 };
 
 class PlayDigiSoundAndDie : public ActionRecord {
 public:
-    virtual void readData(Common::SeekableReadStream &stream) override;
-    virtual void execute() override;
-    // TODO subclass into Play and Stop (?)
+	virtual void readData(Common::SeekableReadStream &stream) override;
+	virtual void execute() override;
+	// TODO subclass into Play and Stop (?)
+
+	SoundDescription _sound;
+	SceneChangeDescription _sceneChange;
+	EventFlagDescription _flagOnTrigger;
 
-    SoundDescription _sound;
-    SceneChangeDescription _sceneChange;
-    EventFlagDescription _flagOnTrigger;
-    
 protected:
-    virtual Common::String getRecordTypeName() const override { return "PlayDigiSoundAndDie"; }
+	virtual Common::String getRecordTypeName() const override { return "PlayDigiSoundAndDie"; }
 };
 
 class PlaySoundPanFrameAnchorAndDie : public ActionRecord {
 public:
-    virtual void readData(Common::SeekableReadStream &stream) override;
-    
+	virtual void readData(Common::SeekableReadStream &stream) override;
+
 protected:
-    virtual Common::String getRecordTypeName() const override { return "PlaySoundPanFrameAnchorAndDie"; }
+	virtual Common::String getRecordTypeName() const override { return "PlaySoundPanFrameAnchorAndDie"; }
 };
 
 class PlaySoundMultiHS : public ActionRecord {
 public:
-    virtual void readData(Common::SeekableReadStream &stream) override;
-    virtual void execute() override;
+	virtual void readData(Common::SeekableReadStream &stream) override;
+	virtual void execute() override;
+
+	SoundDescription _sound; // 0x0
+	SceneChangeDescription _sceneChange; // 0x22
+	EventFlagDescription _flag; // 0x2A
+	Common::Array<HotspotDescription> _hotspots; // 0x31
 
-    SoundDescription _sound; // 0x0
-    SceneChangeDescription _sceneChange; // 0x22
-    EventFlagDescription _flag; // 0x2A
-    Common::Array<HotspotDescription> _hotspots; // 0x31
-    
 protected:
-    virtual Common::String getRecordTypeName() const override { return "PlaySoundMultiHS"; }
+	virtual Common::String getRecordTypeName() const override { return "PlaySoundMultiHS"; }
 };
 
 class HintSystem : public ActionRecord {
 public:
-    virtual void readData(Common::SeekableReadStream &stream) override;
-    virtual void execute() override;
+	virtual void readData(Common::SeekableReadStream &stream) override;
+	virtual void execute() override;
+
+	byte _characterID; // 0x00
+	SoundDescription _genericSound; // 0x01
 
-    byte _characterID; // 0x00
-    SoundDescription _genericSound; // 0x01
+	Common::String _text;
+	SceneChangeDescription _sceneChange;
+	uint16 _hintID;
+	int16 _hintWeight;
 
-    Common::String _text;
-    SceneChangeDescription _sceneChange;
-    uint16 _hintID;
-    int16 _hintWeight;
+	void selectHint();
+	void getHint(uint hint, uint difficulty);
 
-    void selectHint();
-    void getHint(uint hint, uint difficulty);
-    
 protected:
-    virtual Common::String getRecordTypeName() const override { return "HintSystem"; }
+	virtual Common::String getRecordTypeName() const override { return "HintSystem"; }
 };
 
 } // End of namespace Action
diff --git a/engines/nancy/action/responses.cpp b/engines/nancy/action/responses.cpp
index cd68be3faa..5b25d304b2 100644
--- a/engines/nancy/action/responses.cpp
+++ b/engines/nancy/action/responses.cpp
@@ -34,23 +34,23 @@ namespace Action {
 // directly copy the dialogue strings in here.
 
 struct ConditionalResponseDesc {
-    byte characterID; // 0: Daryl, 1: Connie, 2: Hal, 3: Hulk
-    uint fileOffset;
-    uint16 sceneID;
-    EventFlagDescription conditions[7];
+	byte characterID; // 0: Daryl, 1: Connie, 2: Hal, 3: Hulk
+	uint fileOffset;
+	uint16 sceneID;
+	EventFlagDescription conditions[7];
 };
 
 struct GoodbyeDesc {
-    byte characterID;
-    uint fileOffset;
-    uint16 sceneIDs[4];
+	byte characterID;
+	uint fileOffset;
+	uint16 sceneIDs[4];
 };
 
 struct HintDesc {
-    byte characterID; // 0: Ned, 1: Bess, 2: George
-    byte hintID;
-    EventFlagDescription flagConditions[4];
-    EventFlagDescription inventoryCondition[2];
+	byte characterID; // 0: Ned, 1: Bess, 2: George
+	byte hintID;
+	EventFlagDescription flagConditions[4];
+	EventFlagDescription inventoryCondition[2];
 };
 
 static const uint nancy1ResponseBaseFileOffset = 0xB1FE0; // TODO there could be more than one version of the exe
@@ -59,895 +59,895 @@ static const uint nancy1HintOffsets[] = { 0xABB88, 0xAD760, 0xAF338 }; // Ned, B
 #define EMPTY_DESC { -1, kFalse }
 
 static const GoodbyeDesc nancy1Goodbyes[] = {
-    // Daryl
-    {
-        0,
-        0x11B0,
-        { 0xC94, 0xC95, 0xC96, 0xC97}
-    },
-
-    // Connie
-    {
-        1,
-        0x11D8,
-        { 0xFC, 0x9D8, 0x9D9, 0x9DB }
-    },
-
-    // Hal
-    {
-        2,
-        0x11FC,
-        { 0x1C3, 0x1C4, 0x1C5, 0x1C6}
-    },
-
-    // Hulk
-    {
-        3,
-        0x1228,
-        { 0xCE2, 0xCE0, 0xCE2, 0xCE0 } // only two responses
-    }
+	// Daryl
+	{
+		0,
+		0x11B0,
+		{ 0xC94, 0xC95, 0xC96, 0xC97}
+	},
+
+	// Connie
+	{
+		1,
+		0x11D8,
+		{ 0xFC, 0x9D8, 0x9D9, 0x9DB }
+	},
+
+	// Hal
+	{
+		2,
+		0x11FC,
+		{ 0x1C3, 0x1C4, 0x1C5, 0x1C6}
+	},
+
+	// Hulk
+	{
+		3,
+		0x1228,
+		{ 0xCE2, 0xCE0, 0xCE2, 0xCE0 } // only two responses
+	}
 };
 
 static const ConditionalResponseDesc nancy1ConditionalResponses[] = {
-    // Daryl
-    {
-        0, 
-        0x840,
-        0x7C,
-        {
-            { 0x1D, kTrue },
-            { 0x39, kFalse },
-            EMPTY_DESC
-        }
-    },
-
-    {
-        0,
-        0x804,
-        0x7F,
-        {
-            { 0x13, kTrue },
-            { 0x37, kFalse },
-            EMPTY_DESC
-        }
-    },
-
-    {
-        0,
-        0x7BC,
-        0x81,
-        {
-            { 0xB, kTrue },
-            { 0x38, kFalse },
-            EMPTY_DESC
-        }
-    },
-
-    {
-        0,
-        0x750,
-        0x83,
-        {
-            { 0, kTrue },
-            { 1, kFalse },
-            { 0x6B, kFalse },
-            EMPTY_DESC
-        }
-    },
-
-    {
-        0,
-        0x6F4,
-        0x84,
-        {
-            { 0x64, kTrue },
-            { 0x1E, kFalse },
-            { 0x14, kFalse },
-            { 0xC, kFalse },
-            { 0x6C, kFalse },
-            EMPTY_DESC
-        }
-    },
-
-    {
-        0,
-        0x5EC,
-        0x86,
-        {
-            { 0x6D, kFalse },
-            { 0x6, kTrue },
-            { 0x8, kTrue },
-            { 0x5E, kTrue },
-            { 0x17, kTrue },
-            { 0x24, kTrue },
-            { 0x9, kTrue }
-        }
-    },
-
-    {
-        0,
-        0x554,
-        0x8B,
-        {
-            { 0x6E, kFalse },
-            { 0x24, kTrue },
-            { 0x9, kTrue },
-            { 0x5E, kFalse },
-            { 0x8, kFalse },
-            EMPTY_DESC
-        }
-    },
-
-    {
-        0,
-        0x4F0,
-        0x8D,
-        {
-            { 0x6F, kFalse },
-            { 0x5E, kTrue },
-            { 0x24, kTrue },
-            { 0x9, kTrue },
-            { 0x8, kFalse },
-            EMPTY_DESC
-        }
-    },
-
-    {
-        0,
-        0x458,
-        0x8F,
-        {
-            { 0x70, kFalse },
-            { 0x24, kTrue },
-            { 0x9, kTrue },
-            { 0x6, kTrue },
-            { 0x8, kTrue },
-            { 0x5E, kFalse },
-            EMPTY_DESC
-        }
-    },
-
-    {
-        0,
-        0x3BC,
-        0x90,
-        {
-            { 0x71, kFalse },
-            { 0x5E, kTrue },
-            { 0x24, kFalse },
-            { 0x8, kFalse },
-            EMPTY_DESC
-        }
-    },
-
-    {
-        0,
-        0x320,
-        0x91,
-        {
-            { 0x72, kFalse },
-            { 0x5E, kTrue },
-            { 0x8, kTrue },
-            { 0x6, kTrue },
-            { 0x24, kFalse },
-            EMPTY_DESC
-        }
-    },
-
-    {
-        0,
-        0x2AC,
-        0x92,
-        {
-            { 0x73, kFalse },
-            { 0x8, kTrue },
-            { 0x6, kTrue },
-            { 0x5E, kFalse },
-            { 0x24, kFalse },
-            EMPTY_DESC
-        }
-    },
-
-    {
-        0,
-        0x1F0,
-        0x96,
-        {
-            { 0x74, kFalse },
-            { 0x1D, kTrue },
-            { 0x13, kTrue },
-            { 0xB, kTrue },
-            { 0x5E, kFalse },
-            { 0x24, kFalse },
-            { 0x8, kFalse }
-        }
-    },
-
-    {
-        0,
-        0x190,
-        0x97,
-        {
-            { 0x27, kFalse },
-            { 0x5, kTrue },
-            EMPTY_DESC
-        }
-    },
-
-    {
-        0,
-        0xF0,
-        0x9C,
-        {
-            { 0x28, kTrue },
-            { 0x75, kFalse },
-            EMPTY_DESC
-        }
-    },
-
-    {
-        0,
-        0x94,
-        0x93,
-        {
-            { 0xC, kFalse },
-            { 0x6, kTrue },
-            { 0x76, kFalse },
-            EMPTY_DESC
-        }
-    },
-
-    {
-        0,
-        0x58,
-        0x94,
-        {
-            { 0x14, kFalse },
-            { 0x4, kTrue },
-            { 0x77, kFalse },
-            EMPTY_DESC
-        }
-    },
-
-    {
-        0,
-        0,
-        0x95,
-        {
-            { 0x1E, kFalse },
-            { 0x63, kTrue },
-            { 0x78, kFalse },
-            EMPTY_DESC
-        }
-    },
-
-    // Connie
-    {
-        1,
-        0xBE4,
-        0xE9,
-        {
-            { 0x1D, kTrue },
-            { 0x18, kFalse },
-            EMPTY_DESC
-        }
-    },
-
-    {
-        1,
-        0xB8C,
-        0xEA,
-        {
-            { 0x1F, kTrue },
-            { 0x19, kFalse },
-            EMPTY_DESC
-        }
-    },
-
-    {
-        1,
-        0xB54,
-        0xEB,
-        {
-            { 0xB, kTrue },
-            { 0x1A, kFalse },
-            EMPTY_DESC
-        }
-    },
-
-    {
-        1,
-        0xB14,
-        0xEC,
-        {
-            { 0x26, kTrue },
-            { 0x1C, kFalse },
-            EMPTY_DESC
-        }
-    },
-
-    {
-        1,
-        0xABC,
-        0xED,
-        {
-            { 0, kTrue },
-            { 1, kFalse },
-            { 0x79, kFalse },
-            EMPTY_DESC
-        }
-    },
-
-    {
-        1,
-        0xA00,
-        0xEE,
-        {
-            { 2, kTrue },
-            { 3, kTrue },
-            { 0x17, kFalse },
-            EMPTY_DESC
-        }
-    },
-
-    {
-        1,
-        0x6F4,
-        0xEF,
-        {
-            { 0x64, kTrue },
-            { 0x16, kFalse },
-            EMPTY_DESC
-        }
-    },
-
-    {
-        1,
-        0x968,
-        0xF0,
-        {
-            { 0x5, kTrue },
-            { 0x14, kFalse },
-            EMPTY_DESC
-        }
-    },
-
-    {
-        1,
-        0x8C8,
-        0xF5,
-        {
-            { 0x28, kTrue },
-            EMPTY_DESC
-        }
-    },
-
-    {
-        1,
-        0x884,
-        0xE7,
-        {
-            { 0xD, kTrue },
-            { 0x5E, kFalse },
-            EMPTY_DESC
-        }
-    },
-
-    // Hal
-    {
-        2,
-        0xED0,
-        0x1B3,
-        {
-            { 0x1D, kTrue },
-            { 0x11, kFalse },
-            EMPTY_DESC
-        }
-    },
-
-    {
-        2,
-        0x804,
-        0x1B5,
-        {
-            { 0x13, kTrue },
-            { 0xE, kFalse },
-            EMPTY_DESC
-        }
-    },
-
-    {
-        2,
-        0xE74,
-        0x1B6,
-        {
-            { 0x1B, kTrue },
-            { 0xF, kFalse },
-            EMPTY_DESC
-        }
-    },
-
-    {
-        2,
-        0xE2C,
-        0x1B7,
-        {
-            { 0x26, kTrue },
-            { 0x10, kFalse },
-            EMPTY_DESC
-        }
-    },
-
-    {
-        2,
-        0xDD4,
-        0x1B9,
-        {
-            { 0, kTrue },
-            { 1, kFalse },
-            { 0x68, kFalse },
-            EMPTY_DESC
-        }
-    },
-
-    {
-        2,
-        0xD48,
-        0x1BA,
-        {
-            { 0, kTrue },
-            { 1, kFalse },
-            { 0x20, kTrue },
-            { 0x69, kFalse },
-            EMPTY_DESC
-        }
-    },
-
-    {
-        2,
-        0x6F4,
-        0x1BB,
-        {
-            { 0x6A, kFalse },
-            { 0x64, kTrue },
-            { 0x5, kFalse },
-            EMPTY_DESC
-        }
-    },
-
-    {
-        2,
-        0xCC8,
-        0x1BC,
-        {
-            { 0x8, kTrue },
-            { 0x6, kTrue },
-            { 0xC, kFalse },
-            EMPTY_DESC
-        }
-    },
-
-    {
-        2,
-        0xC2C,
-        0x1BE,
-        {
-            { 0x28, kTrue },
-            EMPTY_DESC
-        }
-    },
-
-    // Hulk
-    {
-        3,
-        0x1164,
-        0x14D,
-        {
-            { 0x13, kTrue },
-            { 0x3A, kFalse },
-            EMPTY_DESC
-        }
-    },
-
-    {
-        3,
-        0xB54,
-        0x150,
-        {
-            { 0xB, kTrue },
-            { 0x25, kFalse },
-            EMPTY_DESC
-        }
-    },
-
-    {
-        3,
-        0x10D8,
-        0x153,
-        {
-            { 0x12, kTrue },
-            { 0x21, kFalse },
-            EMPTY_DESC
-        }
-    },
-
-    {
-
-        3,
-        0xE2C,
-        0x154,
-        {
-            { 0x26, kTrue },
-            { 0x22, kFalse },
-            EMPTY_DESC
-        }
-    },
-
-    {
-        3,
-        0x108C,
-        0x155,
-        {
-            { 0, kTrue },
-            { 1, kFalse },
-            { 0x66, kFalse },
-            EMPTY_DESC
-        }
-    },
-
-    {
-        3,
-        0x6F4,
-        0x156,
-        {
-            { 0x67, kFalse },
-            { 0x64, kTrue },
-            EMPTY_DESC
-        }
-    },
-
-    {
-        3,
-        0x1028,
-        0x157,
-        {
-            { 0x63, kTrue },
-            { 0x24, kFalse },
-            EMPTY_DESC
-        }
-    },
-
-    {
-        3,
-        0xFB0,
-        0x158,
-        {
-            { 0x5, kTrue },
-            { 0x1E, kFalse },
-            EMPTY_DESC
-        }
-    },
-
-    {
-        3,
-        0xF10,
-        0x159,
-        {
-            { 0x28, kTrue },
-            EMPTY_DESC
-        }
-    }
+	// Daryl
+	{
+		0,
+		0x840,
+		0x7C,
+		{
+			{ 0x1D, kTrue },
+			{ 0x39, kFalse },
+			EMPTY_DESC
+		}
+	},
+
+	{
+		0,
+		0x804,
+		0x7F,
+		{
+			{ 0x13, kTrue },
+			{ 0x37, kFalse },
+			EMPTY_DESC
+		}
+	},
+
+	{
+		0,
+		0x7BC,
+		0x81,
+		{
+			{ 0xB, kTrue },
+			{ 0x38, kFalse },
+			EMPTY_DESC
+		}
+	},
+
+	{
+		0,
+		0x750,
+		0x83,
+		{
+			{ 0, kTrue },
+			{ 1, kFalse },
+			{ 0x6B, kFalse },
+			EMPTY_DESC
+		}
+	},
+
+	{
+		0,
+		0x6F4,
+		0x84,
+		{
+			{ 0x64, kTrue },
+			{ 0x1E, kFalse },
+			{ 0x14, kFalse },
+			{ 0xC, kFalse },
+			{ 0x6C, kFalse },
+			EMPTY_DESC
+		}
+	},
+
+	{
+		0,
+		0x5EC,
+		0x86,
+		{
+			{ 0x6D, kFalse },
+			{ 0x6, kTrue },
+			{ 0x8, kTrue },
+			{ 0x5E, kTrue },
+			{ 0x17, kTrue },
+			{ 0x24, kTrue },
+			{ 0x9, kTrue }
+		}
+	},
+
+	{
+		0,
+		0x554,
+		0x8B,
+		{
+			{ 0x6E, kFalse },
+			{ 0x24, kTrue },
+			{ 0x9, kTrue },
+			{ 0x5E, kFalse },
+			{ 0x8, kFalse },
+			EMPTY_DESC
+		}
+	},
+
+	{
+		0,
+		0x4F0,
+		0x8D,
+		{
+			{ 0x6F, kFalse },
+			{ 0x5E, kTrue },
+			{ 0x24, kTrue },
+			{ 0x9, kTrue },
+			{ 0x8, kFalse },
+			EMPTY_DESC
+		}
+	},
+
+	{
+		0,
+		0x458,
+		0x8F,
+		{
+			{ 0x70, kFalse },
+			{ 0x24, kTrue },
+			{ 0x9, kTrue },
+			{ 0x6, kTrue },
+			{ 0x8, kTrue },
+			{ 0x5E, kFalse },
+			EMPTY_DESC
+		}
+	},
+
+	{
+		0,
+		0x3BC,
+		0x90,
+		{
+			{ 0x71, kFalse },
+			{ 0x5E, kTrue },
+			{ 0x24, kFalse },
+			{ 0x8, kFalse },
+			EMPTY_DESC
+		}
+	},
+
+	{
+		0,
+		0x320,
+		0x91,
+		{
+			{ 0x72, kFalse },
+			{ 0x5E, kTrue },
+			{ 0x8, kTrue },
+			{ 0x6, kTrue },
+			{ 0x24, kFalse },
+			EMPTY_DESC
+		}
+	},
+
+	{
+		0,
+		0x2AC,
+		0x92,
+		{
+			{ 0x73, kFalse },
+			{ 0x8, kTrue },
+			{ 0x6, kTrue },
+			{ 0x5E, kFalse },
+			{ 0x24, kFalse },
+			EMPTY_DESC
+		}
+	},
+
+	{
+		0,
+		0x1F0,
+		0x96,
+		{
+			{ 0x74, kFalse },
+			{ 0x1D, kTrue },
+			{ 0x13, kTrue },
+			{ 0xB, kTrue },
+			{ 0x5E, kFalse },
+			{ 0x24, kFalse },
+			{ 0x8, kFalse }
+		}
+	},
+
+	{
+		0,
+		0x190,
+		0x97,
+		{
+			{ 0x27, kFalse },
+			{ 0x5, kTrue },
+			EMPTY_DESC
+		}
+	},
+
+	{
+		0,
+		0xF0,
+		0x9C,
+		{
+			{ 0x28, kTrue },
+			{ 0x75, kFalse },
+			EMPTY_DESC
+		}
+	},
+
+	{
+		0,
+		0x94,
+		0x93,
+		{
+			{ 0xC, kFalse },
+			{ 0x6, kTrue },
+			{ 0x76, kFalse },
+			EMPTY_DESC
+		}
+	},
+
+	{
+		0,
+		0x58,
+		0x94,
+		{
+			{ 0x14, kFalse },
+			{ 0x4, kTrue },
+			{ 0x77, kFalse },
+			EMPTY_DESC
+		}
+	},
+
+	{
+		0,
+		0,
+		0x95,
+		{
+			{ 0x1E, kFalse },
+			{ 0x63, kTrue },
+			{ 0x78, kFalse },
+			EMPTY_DESC
+		}
+	},
+
+	// Connie
+	{
+		1,
+		0xBE4,
+		0xE9,
+		{
+			{ 0x1D, kTrue },
+			{ 0x18, kFalse },
+			EMPTY_DESC
+		}
+	},
+
+	{
+		1,
+		0xB8C,
+		0xEA,
+		{
+			{ 0x1F, kTrue },
+			{ 0x19, kFalse },
+			EMPTY_DESC
+		}
+	},
+
+	{
+		1,
+		0xB54,
+		0xEB,
+		{
+			{ 0xB, kTrue },
+			{ 0x1A, kFalse },
+			EMPTY_DESC
+		}
+	},
+
+	{
+		1,
+		0xB14,
+		0xEC,
+		{
+			{ 0x26, kTrue },
+			{ 0x1C, kFalse },
+			EMPTY_DESC
+		}
+	},
+
+	{
+		1,
+		0xABC,
+		0xED,
+		{
+			{ 0, kTrue },
+			{ 1, kFalse },
+			{ 0x79, kFalse },
+			EMPTY_DESC
+		}
+	},
+
+	{
+		1,
+		0xA00,
+		0xEE,
+		{
+			{ 2, kTrue },
+			{ 3, kTrue },
+			{ 0x17, kFalse },
+			EMPTY_DESC
+		}
+	},
+
+	{
+		1,
+		0x6F4,
+		0xEF,
+		{
+			{ 0x64, kTrue },
+			{ 0x16, kFalse },
+			EMPTY_DESC
+		}
+	},
+
+	{
+		1,
+		0x968,
+		0xF0,
+		{
+			{ 0x5, kTrue },
+			{ 0x14, kFalse },
+			EMPTY_DESC
+		}
+	},
+
+	{
+		1,
+		0x8C8,
+		0xF5,
+		{
+			{ 0x28, kTrue },
+			EMPTY_DESC
+		}
+	},
+
+	{
+		1,
+		0x884,
+		0xE7,
+		{
+			{ 0xD, kTrue },
+			{ 0x5E, kFalse },
+			EMPTY_DESC
+		}
+	},
+
+	// Hal
+	{
+		2,
+		0xED0,
+		0x1B3,
+		{
+			{ 0x1D, kTrue },
+			{ 0x11, kFalse },
+			EMPTY_DESC
+		}
+	},
+
+	{
+		2,
+		0x804,
+		0x1B5,
+		{
+			{ 0x13, kTrue },
+			{ 0xE, kFalse },
+			EMPTY_DESC
+		}
+	},
+
+	{
+		2,
+		0xE74,
+		0x1B6,
+		{
+			{ 0x1B, kTrue },
+			{ 0xF, kFalse },
+			EMPTY_DESC
+		}
+	},
+
+	{
+		2,
+		0xE2C,
+		0x1B7,
+		{
+			{ 0x26, kTrue },
+			{ 0x10, kFalse },
+			EMPTY_DESC
+		}
+	},
+
+	{
+		2,
+		0xDD4,
+		0x1B9,
+		{
+			{ 0, kTrue },
+			{ 1, kFalse },
+			{ 0x68, kFalse },
+			EMPTY_DESC
+		}
+	},
+
+	{
+		2,
+		0xD48,
+		0x1BA,
+		{
+			{ 0, kTrue },
+			{ 1, kFalse },
+			{ 0x20, kTrue },
+			{ 0x69, kFalse },
+			EMPTY_DESC
+		}
+	},
+
+	{
+		2,
+		0x6F4,
+		0x1BB,
+		{
+			{ 0x6A, kFalse },
+			{ 0x64, kTrue },
+			{ 0x5, kFalse },
+			EMPTY_DESC
+		}
+	},
+
+	{
+		2,
+		0xCC8,
+		0x1BC,
+		{
+			{ 0x8, kTrue },
+			{ 0x6, kTrue },
+			{ 0xC, kFalse },
+			EMPTY_DESC
+		}
+	},
+
+	{
+		2,
+		0xC2C,
+		0x1BE,
+		{
+			{ 0x28, kTrue },
+			EMPTY_DESC
+		}
+	},
+
+	// Hulk
+	{
+		3,
+		0x1164,
+		0x14D,
+		{
+			{ 0x13, kTrue },
+			{ 0x3A, kFalse },
+			EMPTY_DESC
+		}
+	},
+
+	{
+		3,
+		0xB54,
+		0x150,
+		{
+			{ 0xB, kTrue },
+			{ 0x25, kFalse },
+			EMPTY_DESC
+		}
+	},
+
+	{
+		3,
+		0x10D8,
+		0x153,
+		{
+			{ 0x12, kTrue },
+			{ 0x21, kFalse },
+			EMPTY_DESC
+		}
+	},
+
+	{
+
+		3,
+		0xE2C,
+		0x154,
+		{
+			{ 0x26, kTrue },
+			{ 0x22, kFalse },
+			EMPTY_DESC
+		}
+	},
+
+	{
+		3,
+		0x108C,
+		0x155,
+		{
+			{ 0, kTrue },
+			{ 1, kFalse },
+			{ 0x66, kFalse },
+			EMPTY_DESC
+		}
+	},
+
+	{
+		3,
+		0x6F4,
+		0x156,
+		{
+			{ 0x67, kFalse },
+			{ 0x64, kTrue },
+			EMPTY_DESC
+		}
+	},
+
+	{
+		3,
+		0x1028,
+		0x157,
+		{
+			{ 0x63, kTrue },
+			{ 0x24, kFalse },
+			EMPTY_DESC
+		}
+	},
+
+	{
+		3,
+		0xFB0,
+		0x158,
+		{
+			{ 0x5, kTrue },
+			{ 0x1E, kFalse },
+			EMPTY_DESC
+		}
+	},
+
+	{
+		3,
+		0xF10,
+		0x159,
+		{
+			{ 0x28, kTrue },
+			EMPTY_DESC
+		}
+	}
 };
 
 static const HintDesc nancy1Hints[] {
-    // Ned
-    {
-        0,
-        1,
-        {
-            { 0, kFalse },
-            EMPTY_DESC
-        },
-        {
-            EMPTY_DESC
-        }
-    },
-
-    {
-        0,
-        2,
-        {
-            { 0, kTrue },
-            { 1, kFalse },
-            EMPTY_DESC
-        },
-        {
-            EMPTY_DESC
-        }
-    },
-
-    {
-        0,
-        3,
-        {
-            { 1, kFalse },
-            EMPTY_DESC
-        },
-        {
-            { 3, kFalse },
-            EMPTY_DESC
-        }
-    },
-
-    {
-        0,
-        4,
-        {
-            { 0x55, kFalse },
-            EMPTY_DESC
-        },
-        {
-            { 3, kTrue },
-            EMPTY_DESC
-        }
-    },
-
-    {
-        0,
-        5,
-        {
-            { 0x55, kTrue },
-            { 0x56, kFalse },
-            EMPTY_DESC
-        },
-        {
-            EMPTY_DESC
-        }
-    },
-
-    {
-        0,
-        6,
-        {
-            { 0x57, kFalse },
-            { 0x56, kTrue },
-            EMPTY_DESC
-        },
-        {
-            EMPTY_DESC
-        }
-    },
-
-    {
-        0,
-        8,
-        {
-            { 0xA, kTrue },
-            { 0x3B, kTrue },
-            EMPTY_DESC
-        },
-        {
-            { 7, kFalse },
-            EMPTY_DESC
-        }
-    },
-
-    // Bess
-    {
-        1,
-        1,
-        {
-            { 0x57, kFalse },
-            EMPTY_DESC
-        },
-        {
-            EMPTY_DESC
-        }
-    },
-
-    {
-        1,
-        2,
-        {
-            { 0x57, kTrue },
-            { 0x3C, kFalse },
-            EMPTY_DESC
-        },
-        {
-            EMPTY_DESC
-        }
-    },
-
-    {
-        1,
-        3,
-        {
-            { 0x5A, kFalse },
-            { 0x3C, kTrue },
-            { 0x56, kFalse },
-            EMPTY_DESC
-        },
-        {
-            EMPTY_DESC
-        }
-    },
-
-    {
-        1,
-        4,
-        {
-            { 0x5A, kTrue },
-            { 0x56, kFalse },
-            EMPTY_DESC
-        },
-        {
-            EMPTY_DESC
-        }
-    },
-
-    {
-        1,
-        6,
-        {
-            { 0x5A, kFalse },
-            { 0x3C, kTrue },
-            { 0x56, kTrue },
-            EMPTY_DESC
-        },
-        {
-            EMPTY_DESC
-        }
-    },
-
-    {
-        1,
-        7,
-        {
-            { 0x59, kTrue },
-            { 0xA, kFalse },
-            EMPTY_DESC
-        },
-        {
-            { 0, kFalse },
-            EMPTY_DESC
-        }
-    },
-
-    {
-        1,
-        8,
-        {
-            { 0xA, kTrue },
-            { 0x3B, kTrue },
-            EMPTY_DESC
-        },
-        {
-            { 0, kTrue },
-            { 7, kFalse }
-        }
-    },
-
-    {
-        1,
-        9,
-        {
-            { 0x59, kFalse },
-            { 0xA, kTrue },
-            { 0x3B, kTrue },
-            EMPTY_DESC
-        },
-        {
-            { 7, kFalse },
-            EMPTY_DESC
-        }
-    },
-
-    // George
-    {
-        2,
-        0xA,
-        {
-            { 0x4A, kTrue },
-            EMPTY_DESC
-        },
-        EMPTY_DESC
-    },
-
-    {
-        2,
-        1,
-        {
-            { 0x5B, kFalse },
-            EMPTY_DESC
-        },
-        EMPTY_DESC
-    },
-
-    {
-        2,
-        2,
-        {
-            { 0x5B, kTrue },
-            EMPTY_DESC
-        },
-        {
-            { 9, kFalse },
-            EMPTY_DESC
-        }
-    },
-
-    {
-        2,
-        3,
-        {
-            { 0x5B, kTrue },
-            { 0x5C, kFalse },
-            { 0x5D, kFalse },
-            EMPTY_DESC
-        },
-        {
-            { 9, kTrue },
-            EMPTY_DESC
-        }
-    },
-
-    {
-        2,
-        4,
-        {
-            { 0x5B, kTrue },
-            { 0x5C, kTrue },
-            { 0x5D, kFalse },
-            EMPTY_DESC
-        },
-        {
-            { 9, kFalse },
-            EMPTY_DESC
-        }
-    },
-
-    {
-        2,
-        5,
-        {
-            { 0x5B, kTrue },
-            { 0x5C, kTrue },
-            { 0x5D, kTrue },
-            { 0x3B, kFalse }
-        },
-        {
-            { 9, kTrue },
-            EMPTY_DESC
-        }
-    },
-
-    {
-        2,
-        6,
-        {
-            { 0xA, kFalse },
-            { 0x3B, kTrue },
-            EMPTY_DESC
-        },
-        {
-            { 9, kTrue },
-            EMPTY_DESC
-        }
-    },
-
-    {
-        2,
-        7,
-        {
-            { 0x3B, kTrue },
-            { 0xA, kTrue },
-            EMPTY_DESC
-        },
-        {
-            { 7, kFalse },
-            EMPTY_DESC
-        }
-    }
+	// Ned
+	{
+		0,
+		1,
+		{
+			{ 0, kFalse },
+			EMPTY_DESC
+		},
+		{
+			EMPTY_DESC
+		}
+	},
+
+	{
+		0,
+		2,
+		{
+			{ 0, kTrue },
+			{ 1, kFalse },
+			EMPTY_DESC
+		},
+		{
+			EMPTY_DESC
+		}
+	},
+
+	{
+		0,
+		3,
+		{
+			{ 1, kFalse },
+			EMPTY_DESC
+		},
+		{
+			{ 3, kFalse },
+			EMPTY_DESC
+		}
+	},
+
+	{
+		0,
+		4,
+		{
+			{ 0x55, kFalse },
+			EMPTY_DESC
+		},
+		{
+			{ 3, kTrue },
+			EMPTY_DESC
+		}
+	},
+
+	{
+		0,
+		5,
+		{
+			{ 0x55, kTrue },
+			{ 0x56, kFalse },
+			EMPTY_DESC
+		},
+		{
+			EMPTY_DESC
+		}
+	},
+
+	{
+		0,
+		6,
+		{
+			{ 0x57, kFalse },
+			{ 0x56, kTrue },
+			EMPTY_DESC
+		},
+		{
+			EMPTY_DESC
+		}
+	},
+
+	{
+		0,
+		8,
+		{
+			{ 0xA, kTrue },
+			{ 0x3B, kTrue },
+			EMPTY_DESC
+		},
+		{
+			{ 7, kFalse },
+			EMPTY_DESC
+		}
+	},
+
+	// Bess
+	{
+		1,
+		1,
+		{
+			{ 0x57, kFalse },
+			EMPTY_DESC
+		},
+		{
+			EMPTY_DESC
+		}
+	},
+
+	{
+		1,
+		2,
+		{
+			{ 0x57, kTrue },
+			{ 0x3C, kFalse },
+			EMPTY_DESC
+		},
+		{
+			EMPTY_DESC
+		}
+	},
+
+	{
+		1,
+		3,
+		{
+			{ 0x5A, kFalse },
+			{ 0x3C, kTrue },
+			{ 0x56, kFalse },
+			EMPTY_DESC
+		},
+		{
+			EMPTY_DESC
+		}
+	},
+
+	{
+		1,
+		4,
+		{
+			{ 0x5A, kTrue },
+			{ 0x56, kFalse },
+			EMPTY_DESC
+		},
+		{
+			EMPTY_DESC
+		}
+	},
+
+	{
+		1,
+		6,
+		{
+			{ 0x5A, kFalse },
+			{ 0x3C, kTrue },
+			{ 0x56, kTrue },
+			EMPTY_DESC
+		},
+		{
+			EMPTY_DESC
+		}
+	},
+
+	{
+		1,
+		7,
+		{
+			{ 0x59, kTrue },
+			{ 0xA, kFalse },
+			EMPTY_DESC
+		},
+		{
+			{ 0, kFalse },
+			EMPTY_DESC
+		}
+	},
+
+	{
+		1,
+		8,
+		{
+			{ 0xA, kTrue },
+			{ 0x3B, kTrue },
+			EMPTY_DESC
+		},
+		{
+			{ 0, kTrue },
+			{ 7, kFalse }
+		}
+	},
+
+	{
+		1,
+		9,
+		{
+			{ 0x59, kFalse },
+			{ 0xA, kTrue },
+			{ 0x3B, kTrue },
+			EMPTY_DESC
+		},
+		{
+			{ 7, kFalse },
+			EMPTY_DESC
+		}
+	},
+
+	// George
+	{
+		2,
+		0xA,
+		{
+			{ 0x4A, kTrue },
+			EMPTY_DESC
+		},
+		EMPTY_DESC
+	},
+
+	{
+		2,
+		1,
+		{
+			{ 0x5B, kFalse },
+			EMPTY_DESC
+		},
+		EMPTY_DESC
+	},
+
+	{
+		2,
+		2,
+		{
+			{ 0x5B, kTrue },
+			EMPTY_DESC
+		},
+		{
+			{ 9, kFalse },
+			EMPTY_DESC
+		}
+	},
+
+	{
+		2,
+		3,
+		{
+			{ 0x5B, kTrue },
+			{ 0x5C, kFalse },
+			{ 0x5D, kFalse },
+			EMPTY_DESC
+		},
+		{
+			{ 9, kTrue },
+			EMPTY_DESC
+		}
+	},
+
+	{
+		2,
+		4,
+		{
+			{ 0x5B, kTrue },
+			{ 0x5C, kTrue },
+			{ 0x5D, kFalse },
+			EMPTY_DESC
+		},
+		{
+			{ 9, kFalse },
+			EMPTY_DESC
+		}
+	},
+
+	{
+		2,
+		5,
+		{
+			{ 0x5B, kTrue },
+			{ 0x5C, kTrue },
+			{ 0x5D, kTrue },
+			{ 0x3B, kFalse }
+		},
+		{
+			{ 9, kTrue },
+			EMPTY_DESC
+		}
+	},
+
+	{
+		2,
+		6,
+		{
+			{ 0xA, kFalse },
+			{ 0x3B, kTrue },
+			EMPTY_DESC
+		},
+		{
+			{ 9, kTrue },
+			EMPTY_DESC
+		}
+	},
+
+	{
+		2,
+		7,
+		{
+			{ 0x3B, kTrue },
+			{ 0xA, kTrue },
+			EMPTY_DESC
+		},
+		{
+			{ 7, kFalse },
+			EMPTY_DESC
+		}
+	}
 };
 
 }// End of namespace Action
diff --git a/engines/nancy/action/rotatinglockpuzzle.cpp b/engines/nancy/action/rotatinglockpuzzle.cpp
index d5568989d0..faf090f70b 100644
--- a/engines/nancy/action/rotatinglockpuzzle.cpp
+++ b/engines/nancy/action/rotatinglockpuzzle.cpp
@@ -35,189 +35,189 @@ namespace Nancy {
 namespace Action {
 
 void RotatingLockPuzzle::init() {
-    _drawSurface.create(_screenPosition.width(), _screenPosition.height(), GraphicsManager::getInputPixelFormat());
-    _drawSurface.clear(GraphicsManager::getTransColor());
-    
-    setTransparent(true);
+	_drawSurface.create(_screenPosition.width(), _screenPosition.height(), GraphicsManager::getInputPixelFormat());
+	_drawSurface.clear(GraphicsManager::getTransColor());
 
-    g_nancy->_resource->loadImage(_imageName, _image);
+	setTransparent(true);
+
+	g_nancy->_resource->loadImage(_imageName, _image);
 }
 
 void RotatingLockPuzzle::readData(Common::SeekableReadStream &stream) {
-    readFilename(stream, _imageName);
-
-    uint numDials = stream.readUint16LE();
-
-    for (uint i = 0; i < 10; ++i) {
-        _srcRects.push_back(Common::Rect());
-        readRect(stream, _srcRects.back());
-    }
-
-    for (uint i = 0; i < numDials; ++i) {
-        _destRects.push_back(Common::Rect());
-        readRect(stream, _destRects.back());
-
-        if (i == 0) {
-            _screenPosition = _destRects.back();
-        } else {
-            _screenPosition.extend(_destRects.back());
-        }
-    }
-
-    stream.skip((8 - numDials) * 16);
-
-    for (uint i = 0; i < numDials; ++i) {
-        _upHotspots.push_back(Common::Rect());
-        readRect(stream, _upHotspots.back());
-    }
-
-    stream.skip((8 - numDials) * 16);
-
-    for (uint i = 0; i < numDials; ++i) {
-        _downHotspots.push_back(Common::Rect());
-        readRect(stream, _downHotspots.back());
-    }
-
-    stream.skip((8 - numDials) * 16);
-
-    for (uint i = 0; i < numDials; ++i) {
-        _correctSequence.push_back(stream.readByte());
-    }
-
-    stream.skip(8 - numDials);
-
-    _clickSound.read(stream, SoundDescription::kNormal);
-    _solveExitScene.readData(stream);
-    stream.skip(2); // shouldStopRendering, useless
-    _flagOnSolve.label = stream.readSint16LE();
-    _flagOnSolve.flag = (NancyFlag)stream.readByte();
-    _solveSoundDelay = stream.readUint16LE();
-    _solveSound.read(stream, SoundDescription::kNormal);
-    _exitScene.readData(stream);
-    stream.skip(2); // shouldStopRendering, useless
-    _flagOnExit.label = stream.readSint16LE();
-    _flagOnExit.flag = (NancyFlag)stream.readByte();
-    readRect(stream, _exitHotspot);
+	readFilename(stream, _imageName);
+
+	uint numDials = stream.readUint16LE();
+
+	for (uint i = 0; i < 10; ++i) {
+		_srcRects.push_back(Common::Rect());
+		readRect(stream, _srcRects.back());
+	}
+
+	for (uint i = 0; i < numDials; ++i) {
+		_destRects.push_back(Common::Rect());
+		readRect(stream, _destRects.back());
+
+		if (i == 0) {
+			_screenPosition = _destRects.back();
+		} else {
+			_screenPosition.extend(_destRects.back());
+		}
+	}
+
+	stream.skip((8 - numDials) * 16);
+
+	for (uint i = 0; i < numDials; ++i) {
+		_upHotspots.push_back(Common::Rect());
+		readRect(stream, _upHotspots.back());
+	}
+
+	stream.skip((8 - numDials) * 16);
+
+	for (uint i = 0; i < numDials; ++i) {
+		_downHotspots.push_back(Common::Rect());
+		readRect(stream, _downHotspots.back());
+	}
+
+	stream.skip((8 - numDials) * 16);
+
+	for (uint i = 0; i < numDials; ++i) {
+		_correctSequence.push_back(stream.readByte());
+	}
+
+	stream.skip(8 - numDials);
+
+	_clickSound.read(stream, SoundDescription::kNormal);
+	_solveExitScene.readData(stream);
+	stream.skip(2); // shouldStopRendering, useless
+	_flagOnSolve.label = stream.readSint16LE();
+	_flagOnSolve.flag = (NancyFlag)stream.readByte();
+	_solveSoundDelay = stream.readUint16LE();
+	_solveSound.read(stream, SoundDescription::kNormal);
+	_exitScene.readData(stream);
+	stream.skip(2); // shouldStopRendering, useless
+	_flagOnExit.label = stream.readSint16LE();
+	_flagOnExit.flag = (NancyFlag)stream.readByte();
+	readRect(stream, _exitHotspot);
 }
 
 void RotatingLockPuzzle::execute() {
-    switch (_state) {
-    case kBegin:
-        init();
-        registerGraphics();
-
-        for (uint i = 0; i < _correctSequence.size(); ++i) {
-            _currentSequence.push_back(g_nancy->_randomSource->getRandomNumber(9));
-            drawDial(i);
-        }
-
-        g_nancy->_sound->loadSound(_clickSound);
-        g_nancy->_sound->loadSound(_solveSound);
-        _state = kRun;
-        // fall through
-    case kRun:
-        switch (_solveState) {
-        case kNotSolved:
-            for (uint i = 0; i < _correctSequence.size(); ++i) {
-                if (_currentSequence[i] != (int16)_correctSequence[i]) {
-                    return;
-                }
-            }
-
-            NancySceneState.setEventFlag(_flagOnSolve);
-            _solveSoundPlayTime = g_nancy->getTotalPlayTime() + _solveSoundDelay * 1000;
-            _solveState = kPlaySound;
-            // fall through
-        case kPlaySound:
-            if (g_nancy->getTotalPlayTime() <= _solveSoundPlayTime) {
-                break;
-            }
-
-            g_nancy->_sound->playSound(_solveSound);
-            _solveState = kWaitForSound;
-            break;
-        case kWaitForSound:
-            if (!g_nancy->_sound->isSoundPlaying(_solveSound)) {
-                _state = kActionTrigger;
-            }
-
-            break;
-        }
-        break;
-    case kActionTrigger:
-        g_nancy->_sound->stopSound(_clickSound);
-        g_nancy->_sound->stopSound(_solveSound);
-
-        if (_solveState == kNotSolved) {
-            NancySceneState.changeScene(_exitScene);
-            NancySceneState.setEventFlag(_flagOnExit);
-        } else {
-            NancySceneState.changeScene(_solveExitScene);
-        }
-
-        finishExecution();
-    }
+	switch (_state) {
+	case kBegin:
+		init();
+		registerGraphics();
+
+		for (uint i = 0; i < _correctSequence.size(); ++i) {
+			_currentSequence.push_back(g_nancy->_randomSource->getRandomNumber(9));
+			drawDial(i);
+		}
+
+		g_nancy->_sound->loadSound(_clickSound);
+		g_nancy->_sound->loadSound(_solveSound);
+		_state = kRun;
+		// fall through
+	case kRun:
+		switch (_solveState) {
+		case kNotSolved:
+			for (uint i = 0; i < _correctSequence.size(); ++i) {
+				if (_currentSequence[i] != (int16)_correctSequence[i]) {
+					return;
+				}
+			}
+
+			NancySceneState.setEventFlag(_flagOnSolve);
+			_solveSoundPlayTime = g_nancy->getTotalPlayTime() + _solveSoundDelay * 1000;
+			_solveState = kPlaySound;
+			// fall through
+		case kPlaySound:
+			if (g_nancy->getTotalPlayTime() <= _solveSoundPlayTime) {
+				break;
+			}
+
+			g_nancy->_sound->playSound(_solveSound);
+			_solveState = kWaitForSound;
+			break;
+		case kWaitForSound:
+			if (!g_nancy->_sound->isSoundPlaying(_solveSound)) {
+				_state = kActionTrigger;
+			}
+
+			break;
+		}
+		break;
+	case kActionTrigger:
+		g_nancy->_sound->stopSound(_clickSound);
+		g_nancy->_sound->stopSound(_solveSound);
+
+		if (_solveState == kNotSolved) {
+			NancySceneState.changeScene(_exitScene);
+			NancySceneState.setEventFlag(_flagOnExit);
+		} else {
+			NancySceneState.changeScene(_solveExitScene);
+		}
+
+		finishExecution();
+	}
 }
 
 void RotatingLockPuzzle::handleInput(NancyInput &input) {
-    if (_solveState != kNotSolved) {
-        return;
-    }
-
-    if (NancySceneState.getViewport().convertViewportToScreen(_exitHotspot).contains(input.mousePos)) {
-        g_nancy->_cursorManager->setCursorType(CursorManager::kExitArrow);
-
-        if (input.input & NancyInput::kLeftMouseButtonUp) {
-            _state = kActionTrigger;
-        }
-
-        return;
-    }
-
-    for (uint i = 0; i < _upHotspots.size(); ++i) {
-        if (NancySceneState.getViewport().convertViewportToScreen(_upHotspots[i]).contains(input.mousePos)) {
-            g_nancy->_cursorManager->setCursorType(CursorManager::kHotspot);
-
-            if (input.input & NancyInput::kLeftMouseButtonUp) {
-                g_nancy->_sound->playSound(_clickSound);
-                
-                _currentSequence[i] = ++_currentSequence[i] > 9 ? 0 : _currentSequence[i];
-                drawDial(i);
-            }
-
-            return;
-        }
-    }
-
-    for (uint i = 0; i < _downHotspots.size(); ++i) {
-        if (NancySceneState.getViewport().convertViewportToScreen(_downHotspots[i]).contains(input.mousePos)) {
-            g_nancy->_cursorManager->setCursorType(CursorManager::kHotspot);
-
-            if (input.input & NancyInput::kLeftMouseButtonUp) {
-                g_nancy->_sound->playSound(_clickSound);
-
-                int8 n = _currentSequence[i];
-                n = --n < 0 ? 9 : n;
-                _currentSequence[i] = n;
-                drawDial(i);
-            }
-            
-            return;
-        }
-    }
+	if (_solveState != kNotSolved) {
+		return;
+	}
+
+	if (NancySceneState.getViewport().convertViewportToScreen(_exitHotspot).contains(input.mousePos)) {
+		g_nancy->_cursorManager->setCursorType(CursorManager::kExitArrow);
+
+		if (input.input & NancyInput::kLeftMouseButtonUp) {
+			_state = kActionTrigger;
+		}
+
+		return;
+	}
+
+	for (uint i = 0; i < _upHotspots.size(); ++i) {
+		if (NancySceneState.getViewport().convertViewportToScreen(_upHotspots[i]).contains(input.mousePos)) {
+			g_nancy->_cursorManager->setCursorType(CursorManager::kHotspot);
+
+			if (input.input & NancyInput::kLeftMouseButtonUp) {
+				g_nancy->_sound->playSound(_clickSound);
+
+				_currentSequence[i] = ++_currentSequence[i] > 9 ? 0 : _currentSequence[i];
+				drawDial(i);
+			}
+
+			return;
+		}
+	}
+
+	for (uint i = 0; i < _downHotspots.size(); ++i) {
+		if (NancySceneState.getViewport().convertViewportToScreen(_downHotspots[i]).contains(input.mousePos)) {
+			g_nancy->_cursorManager->setCursorType(CursorManager::kHotspot);
+
+			if (input.input & NancyInput::kLeftMouseButtonUp) {
+				g_nancy->_sound->playSound(_clickSound);
+
+				int8 n = _currentSequence[i];
+				n = --n < 0 ? 9 : n;
+				_currentSequence[i] = n;
+				drawDial(i);
+			}
+
+			return;
+		}
+	}
 }
 
 void RotatingLockPuzzle::onPause(bool pause) {
-    if (pause) {
-        registerGraphics();
-    }
+	if (pause) {
+		registerGraphics();
+	}
 }
 
 void RotatingLockPuzzle::drawDial(uint id) {
-    Common::Point destPoint(_destRects[id].left - _screenPosition.left, _destRects[id].top - _screenPosition.top);
-    _drawSurface.blitFrom(_image, _srcRects[_currentSequence[id]], destPoint);
+	Common::Point destPoint(_destRects[id].left - _screenPosition.left, _destRects[id].top - _screenPosition.top);
+	_drawSurface.blitFrom(_image, _srcRects[_currentSequence[id]], destPoint);
 
-    _needsRedraw = true;
+	_needsRedraw = true;
 }
 
 } // End of namespace Action
diff --git a/engines/nancy/action/rotatinglockpuzzle.h b/engines/nancy/action/rotatinglockpuzzle.h
index a4312f8eae..cc943a8046 100644
--- a/engines/nancy/action/rotatinglockpuzzle.h
+++ b/engines/nancy/action/rotatinglockpuzzle.h
@@ -38,46 +38,46 @@ namespace Action {
 
 class RotatingLockPuzzle : public ActionRecord, public RenderObject {
 public:
-    enum SolveState { kNotSolved, kPlaySound, kWaitForSound };
-    RotatingLockPuzzle(RenderObject &redrawFrom) : RenderObject(redrawFrom) {}
-    virtual ~RotatingLockPuzzle() {}
-    
-    virtual void init() override;
-    
-    virtual void readData(Common::SeekableReadStream &stream) override;
-    virtual void execute() override;
-    virtual void handleInput(NancyInput &input) override;
-    virtual void onPause(bool pause) override;
-
-    Common::String _imageName; // 0x00
-    // 0xA numDials
-    Common::Array<Common::Rect> _srcRects; // 0xC, 10
-    Common::Array<Common::Rect> _destRects; // 0xAC, 8
-    Common::Array<Common::Rect> _upHotspots; // 0x12C, 8
-    Common::Array<Common::Rect> _downHotspots; // 0x1AC, 8
-    Common::Array<byte> _correctSequence; // 0x22C
-    Nancy::SoundDescription _clickSound; // 0x234, kNormal
-    SceneChangeDescription _solveExitScene; // 0x256
-    EventFlagDescription _flagOnSolve; // 0x260
-    uint16 _solveSoundDelay; // 0x263
-    Nancy::SoundDescription _solveSound; // 0x265
-    SceneChangeDescription _exitScene; // 0x287
-    EventFlagDescription _flagOnExit; // 0x291
-    Common::Rect _exitHotspot; // 0x294
-
-    SolveState _solveState = kNotSolved;
-    Graphics::ManagedSurface _image;
-    Common::Array<byte> _currentSequence;
-    Time _solveSoundPlayTime;
+	enum SolveState { kNotSolved, kPlaySound, kWaitForSound };
+	RotatingLockPuzzle(RenderObject &redrawFrom) : RenderObject(redrawFrom) {}
+	virtual ~RotatingLockPuzzle() {}
+
+	virtual void init() override;
+
+	virtual void readData(Common::SeekableReadStream &stream) override;
+	virtual void execute() override;
+	virtual void handleInput(NancyInput &input) override;
+	virtual void onPause(bool pause) override;
+
+	Common::String _imageName; // 0x00
+	// 0xA numDials
+	Common::Array<Common::Rect> _srcRects; // 0xC, 10
+	Common::Array<Common::Rect> _destRects; // 0xAC, 8
+	Common::Array<Common::Rect> _upHotspots; // 0x12C, 8
+	Common::Array<Common::Rect> _downHotspots; // 0x1AC, 8
+	Common::Array<byte> _correctSequence; // 0x22C
+	Nancy::SoundDescription _clickSound; // 0x234, kNormal
+	SceneChangeDescription _solveExitScene; // 0x256
+	EventFlagDescription _flagOnSolve; // 0x260
+	uint16 _solveSoundDelay; // 0x263
+	Nancy::SoundDescription _solveSound; // 0x265
+	SceneChangeDescription _exitScene; // 0x287
+	EventFlagDescription _flagOnExit; // 0x291
+	Common::Rect _exitHotspot; // 0x294
+
+	SolveState _solveState = kNotSolved;
+	Graphics::ManagedSurface _image;
+	Common::Array<byte> _currentSequence;
+	Time _solveSoundPlayTime;
 
 
 protected:
-    virtual Common::String getRecordTypeName() const override { return "RotatingLockPuzzle"; }
+	virtual Common::String getRecordTypeName() const override { return "RotatingLockPuzzle"; }
 
-    virtual uint16 getZOrder() const override { return 7; }
-    virtual bool isViewportRelative() const override { return true; }
+	virtual uint16 getZOrder() const override { return 7; }
+	virtual bool isViewportRelative() const override { return true; }
 
-    void drawDial(uint id);
+	void drawDial(uint id);
 };
 
 } // End of namespace Action
diff --git a/engines/nancy/action/secondarymovie.cpp b/engines/nancy/action/secondarymovie.cpp
index ed5e160005..223b57c2d3 100644
--- a/engines/nancy/action/secondarymovie.cpp
+++ b/engines/nancy/action/secondarymovie.cpp
@@ -31,59 +31,59 @@ namespace Nancy {
 namespace Action {
 
 PlaySecondaryMovie::~PlaySecondaryMovie() {
-    _decoder.close();
-    
-    if (_hideMouse == kTrue && _unknown == 5) {
-        g_nancy->setMouseEnabled(true);
-    }
+	_decoder.close();
+
+	if (_hideMouse == kTrue && _unknown == 5) {
+		g_nancy->setMouseEnabled(true);
+	}
 }
 
-void PlaySecondaryMovie::readData(Common::SeekableReadStream &stream) {  
-    readFilename(stream, _videoName);
+void PlaySecondaryMovie::readData(Common::SeekableReadStream &stream) {
+	readFilename(stream, _videoName);
 
-    stream.skip(0x12);
-    _unknown = stream.readUint16LE();
+	stream.skip(0x12);
+	_unknown = stream.readUint16LE();
 	_hideMouse = (NancyFlag)stream.readUint16LE();
-    _isReverse = (NancyFlag)stream.readUint16LE();
-    _firstFrame = (NancyFlag)stream.readUint16LE();
-    _lastFrame = (NancyFlag)stream.readUint16LE();
-
-    for (uint i = 0; i < 15; ++i) {
-        _frameFlags[i].frameID = stream.readSint16LE();
-        _frameFlags[i].flagDesc.label = stream.readSint16LE();
-        _frameFlags[i].flagDesc.flag = (NancyFlag)stream.readUint16LE();
-    }
-
-    _triggerFlags.readData(stream);
-    _sound.read(stream, SoundDescription::kNormal);
-    _sceneChange.readData(stream);
-
-    uint16 numVideoDescs = stream.readUint16LE();
-    for (uint i = 0; i < numVideoDescs; ++i) {
-        _videoDescs.push_back(SecondaryVideoDescription());
-        _videoDescs[i].readData(stream);
-    }
+	_isReverse = (NancyFlag)stream.readUint16LE();
+	_firstFrame = (NancyFlag)stream.readUint16LE();
+	_lastFrame = (NancyFlag)stream.readUint16LE();
+
+	for (uint i = 0; i < 15; ++i) {
+		_frameFlags[i].frameID = stream.readSint16LE();
+		_frameFlags[i].flagDesc.label = stream.readSint16LE();
+		_frameFlags[i].flagDesc.flag = (NancyFlag)stream.readUint16LE();
+	}
+
+	_triggerFlags.readData(stream);
+	_sound.read(stream, SoundDescription::kNormal);
+	_sceneChange.readData(stream);
+
+	uint16 numVideoDescs = stream.readUint16LE();
+	for (uint i = 0; i < numVideoDescs; ++i) {
+		_videoDescs.push_back(SecondaryVideoDescription());
+		_videoDescs[i].readData(stream);
+	}
 }
 
 void PlaySecondaryMovie::init() {
-    if (_decoder.isVideoLoaded()) {
-        _decoder.close();
-    }
+	if (_decoder.isVideoLoaded()) {
+		_decoder.close();
+	}
 
-    _decoder.loadFile(_videoName + ".avf");
-    _drawSurface.create(_decoder.getWidth(), _decoder.getHeight(), GraphicsManager::getInputPixelFormat());
-    _screenPosition = _drawSurface.getBounds();
+	_decoder.loadFile(_videoName + ".avf");
+	_drawSurface.create(_decoder.getWidth(), _decoder.getHeight(), GraphicsManager::getInputPixelFormat());
+	_screenPosition = _drawSurface.getBounds();
 
-    RenderObject::init();
+	RenderObject::init();
 }
 
 void PlaySecondaryMovie::updateGraphics() {
-    if (!_decoder.isVideoLoaded()) {
-        return;
-    }
+	if (!_decoder.isVideoLoaded()) {
+		return;
+	}
 
-    if (!_decoder.isPlaying() && _isVisible && !_isFinished) {
-        _decoder.start();
+	if (!_decoder.isPlaying() && _isVisible && !_isFinished) {
+		_decoder.start();
 
 		if (_isReverse == kTrue) {
 			_decoder.setRate(-_decoder.getRate());
@@ -91,99 +91,99 @@ void PlaySecondaryMovie::updateGraphics() {
 		} else {
 			_decoder.seekToFrame(_firstFrame);
 		}
-    }
-
-    if (_decoder.needsUpdate()) {
-        uint descID = 0;
-
-        for (uint i = 0; i < _videoDescs.size(); ++i) {
-            if (_videoDescs[i].frameID == _curViewportFrame) {
-                descID = i;
-            }
-        }
-
-        _drawSurface.blitFrom(*_decoder.decodeNextFrame(), _videoDescs[descID].srcRect, Common::Point());
-        _needsRedraw = true;
-        
-        for (auto f : _frameFlags) {
-            if (_decoder.getCurFrame() == f.frameID) {
-                NancySceneState.setEventFlag(f.flagDesc);
-            }
-        }
-    }
+	}
+
+	if (_decoder.needsUpdate()) {
+		uint descID = 0;
+
+		for (uint i = 0; i < _videoDescs.size(); ++i) {
+			if (_videoDescs[i].frameID == _curViewportFrame) {
+				descID = i;
+			}
+		}
+
+		_drawSurface.blitFrom(*_decoder.decodeNextFrame(), _videoDescs[descID].srcRect, Common::Point());
+		_needsRedraw = true;
+
+		for (auto f : _frameFlags) {
+			if (_decoder.getCurFrame() == f.frameID) {
+				NancySceneState.setEventFlag(f.flagDesc);
+			}
+		}
+	}
 
 	if ((_decoder.getCurFrame() == _lastFrame && _isReverse == kFalse) ||
-	    (_decoder.getCurFrame() == _firstFrame && _isReverse == kTrue)) {
+		(_decoder.getCurFrame() == _firstFrame && _isReverse == kTrue)) {
 		if (!g_nancy->_sound->isSoundPlaying(_sound)) {
 			g_nancy->_sound->stopSound(_sound);
 			_decoder.stop();
-            _isFinished = true;
+			_isFinished = true;
 			_state = kActionTrigger;
 		}
 	}
 
-    RenderObject::updateGraphics();
+	RenderObject::updateGraphics();
 }
 
 void PlaySecondaryMovie::onPause(bool pause) {
-    _decoder.pauseVideo(pause);
+	_decoder.pauseVideo(pause);
 
-    if (pause) {
-        registerGraphics();
-    }
+	if (pause) {
+		registerGraphics();
+	}
 }
 
 void PlaySecondaryMovie::execute() {
-    switch (_state) {
-    case kBegin:
-        init();
-        registerGraphics();
-        g_nancy->_sound->loadSound(_sound);
-        g_nancy->_sound->playSound(_sound);
-
-        if (_hideMouse == kTrue) {
-            g_nancy->setMouseEnabled(false);
-        }
-
-        _state = kRun;
-        // fall through
-    case kRun: {
-        int newFrame = NancySceneState.getSceneInfo().frameID;
-
-        if (newFrame != _curViewportFrame) {
-            _curViewportFrame = newFrame;
-            int activeFrame = -1;
-            for (uint i = 0; i < _videoDescs.size(); ++i) {
-                if (newFrame == _videoDescs[i].frameID) {
-                    activeFrame = i;
-                    break;
-                }
-            }
-
-            if (activeFrame != -1) {
-                _screenPosition = _videoDescs[activeFrame].destRect;
-                setVisible(true);
-            } else {
-                setVisible(false);
-            }
-        }
-
-        break;
-    }
-    case kActionTrigger:
-        _triggerFlags.execute();
-        if (_unknown == 5) {
-            NancySceneState.changeScene(_sceneChange);
-        } else {
-            // Not changing the scene so enable the mouse now
-            if (_hideMouse == kTrue) {
-                g_nancy->setMouseEnabled(true);
-            }
-        }
-
-        finishExecution();
-        break;
-    }
+	switch (_state) {
+	case kBegin:
+		init();
+		registerGraphics();
+		g_nancy->_sound->loadSound(_sound);
+		g_nancy->_sound->playSound(_sound);
+
+		if (_hideMouse == kTrue) {
+			g_nancy->setMouseEnabled(false);
+		}
+
+		_state = kRun;
+		// fall through
+	case kRun: {
+		int newFrame = NancySceneState.getSceneInfo().frameID;
+
+		if (newFrame != _curViewportFrame) {
+			_curViewportFrame = newFrame;
+			int activeFrame = -1;
+			for (uint i = 0; i < _videoDescs.size(); ++i) {
+				if (newFrame == _videoDescs[i].frameID) {
+					activeFrame = i;
+					break;
+				}
+			}
+
+			if (activeFrame != -1) {
+				_screenPosition = _videoDescs[activeFrame].destRect;
+				setVisible(true);
+			} else {
+				setVisible(false);
+			}
+		}
+
+		break;
+	}
+	case kActionTrigger:
+		_triggerFlags.execute();
+		if (_unknown == 5) {
+			NancySceneState.changeScene(_sceneChange);
+		} else {
+			// Not changing the scene so enable the mouse now
+			if (_hideMouse == kTrue) {
+				g_nancy->setMouseEnabled(true);
+			}
+		}
+
+		finishExecution();
+		break;
+	}
 }
 
 } // End of namespace Action
diff --git a/engines/nancy/action/secondarymovie.h b/engines/nancy/action/secondarymovie.h
index 8a3583f6c7..d5c00df5d2 100644
--- a/engines/nancy/action/secondarymovie.h
+++ b/engines/nancy/action/secondarymovie.h
@@ -37,50 +37,50 @@ namespace Action {
 
 class PlaySecondaryMovie : public ActionRecord, public RenderObject {
 public:
-    struct FlagAtFrame {
-        int16 frameID;
-        EventFlagDescription flagDesc;
-    };
+	struct FlagAtFrame {
+		int16 frameID;
+		EventFlagDescription flagDesc;
+	};
 
-    PlaySecondaryMovie(RenderObject &redrawFrom) :
-        RenderObject(redrawFrom),
-        _curViewportFrame(-1),
-        _isFinished(false) {}
-    virtual ~PlaySecondaryMovie();
+	PlaySecondaryMovie(RenderObject &redrawFrom) :
+		RenderObject(redrawFrom),
+		_curViewportFrame(-1),
+		_isFinished(false) {}
+	virtual ~PlaySecondaryMovie();
 
-    virtual void init() override;
-    virtual void updateGraphics() override;
-    virtual void onPause(bool pause) override;
+	virtual void init() override;
+	virtual void updateGraphics() override;
+	virtual void onPause(bool pause) override;
 
-    virtual void readData(Common::SeekableReadStream &stream) override;
-    virtual void execute() override;
+	virtual void readData(Common::SeekableReadStream &stream) override;
+	virtual void execute() override;
 
-    Common::String _videoName; // 0x00
+	Common::String _videoName; // 0x00
 
-    uint16 _unknown; // 0x1C
-    NancyFlag _hideMouse; // 0x1E
-    NancyFlag _isReverse; // 0x20
-    uint16 _firstFrame; // 0x22
-    uint16 _lastFrame; // 0x24
-    FlagAtFrame _frameFlags[15]; // 0x26
-    MultiEventFlagDescription _triggerFlags; // 0x80
+	uint16 _unknown; // 0x1C
+	NancyFlag _hideMouse; // 0x1E
+	NancyFlag _isReverse; // 0x20
+	uint16 _firstFrame; // 0x22
+	uint16 _lastFrame; // 0x24
+	FlagAtFrame _frameFlags[15]; // 0x26
+	MultiEventFlagDescription _triggerFlags; // 0x80
 
-    SoundDescription _sound; // 0xA8
+	SoundDescription _sound; // 0xA8
 
-    SceneChangeDescription _sceneChange; // 0xCA
-    Common::Array<SecondaryVideoDescription> _videoDescs; // 0xD4
+	SceneChangeDescription _sceneChange; // 0xCA
+	Common::Array<SecondaryVideoDescription> _videoDescs; // 0xD4
 
 protected:
-    virtual Common::String getRecordTypeName() const override { return "PlaySecondaryMovie"; }
+	virtual Common::String getRecordTypeName() const override { return "PlaySecondaryMovie"; }
 
-    virtual uint16 getZOrder() const override { return 8; }
-    virtual bool isViewportRelative() const override { return true; }
+	virtual uint16 getZOrder() const override { return 8; }
+	virtual bool isViewportRelative() const override { return true; }
 
-    AVFDecoder _decoder;
-    int _curViewportFrame;
-    bool _isFinished;
+	AVFDecoder _decoder;
+	int _curViewportFrame;
+	bool _isFinished;
 };
-    
+
 } // End of namespace Action
 } // End of namespace Nancy
 
diff --git a/engines/nancy/action/secondaryvideo.cpp b/engines/nancy/action/secondaryvideo.cpp
index cb7f3aa708..77abaa80ac 100644
--- a/engines/nancy/action/secondaryvideo.cpp
+++ b/engines/nancy/action/secondaryvideo.cpp
@@ -41,185 +41,185 @@ namespace Nancy {
 namespace Action {
 
 void PlaySecondaryVideo::init() {
-    if(_decoder.isVideoLoaded()) {
-        _decoder.close();
-    }
+	if (_decoder.isVideoLoaded()) {
+		_decoder.close();
+	}
 
-    _decoder.loadFile(_filename + ".avf");
-    // Every secondary video frame (in nancy1) plays exactly 12ms slower than what its metadata says.
-    // I'm still not sure how/why that happens so for now I'm using this hack to fix the timings
-    _decoder.addFrameTime(12);
-    _drawSurface.create(_decoder.getWidth(), _decoder.getHeight(), GraphicsManager::getInputPixelFormat());
+	_decoder.loadFile(_filename + ".avf");
+	// Every secondary video frame (in nancy1) plays exactly 12ms slower than what its metadata says.
+	// I'm still not sure how/why that happens so for now I'm using this hack to fix the timings
+	_decoder.addFrameTime(12);
+	_drawSurface.create(_decoder.getWidth(), _decoder.getHeight(), GraphicsManager::getInputPixelFormat());
 
-    if (_paletteFilename.size()) {
-        GraphicsManager::loadSurfacePalette(_drawSurface, _paletteFilename);
-    }
+	if (_paletteFilename.size()) {
+		GraphicsManager::loadSurfacePalette(_drawSurface, _paletteFilename);
+	}
 
-    setVisible(false);
-    setTransparent(true);
+	setVisible(false);
+	setTransparent(true);
 
-    RenderObject::init();
+	RenderObject::init();
 }
 
 void PlaySecondaryVideo::updateGraphics() {
-    if (!_decoder.isVideoLoaded()) {
-        return;
-    }
-
-    if (_isPlaying) {
-        if (!_decoder.isPlaying()) {
-            _decoder.start();
-        }
-        
-        switch (_hoverState) {
-        case kNoHover:
-            if (_isHovered) {
-                _decoder.seekToFrame(_onHoverFirstFrame);
-                
-                _hoverState = kHover;
-            } else {
-                if (_decoder.getCurFrame() == _loopLastFrame) {
-                    // loop back to beginning
-                    _decoder.seekToFrame(_loopFirstFrame);
-                }
-
-                break;
-            }
-            // fall through
-        case kHover:
-            if (!_isHovered) {
-                // Stopped hovering, reverse playback
-                _decoder.seekToFrame(_onHoverEndLastFrame);
-                _decoder.setRate(-_decoder.getRate());
-                if (!_decoder.isPlaying()) {
-                    _decoder.start();
-                }
-
-                _hoverState = kEndHover;
-            } else {
-                break;
-            }
-            // fall through
-        case kEndHover:
-            if (_decoder.getCurFrame() == _onHoverEndFirstFrame) {
-                // reversed playback has ended, go back to no hover _state
-                _decoder.seekToFrame(_loopFirstFrame);
-                _decoder.setRate(-_decoder.getRate());
-                _hoverState = kNoHover;
-            }
-
-            break;
-        }
-
-        if (_decoder.needsUpdate() && !_screenPosition.isEmpty()) {
-            for (uint i = 0; i < _videoDescs.size(); ++i) {
-                if ((uint16)_videoDescs[i].frameID == _currentViewportFrame) {
-                    // This ignores the srcRects for every frame
-                    GraphicsManager::copyToManaged(*_decoder.decodeNextFrame(), _drawSurface, _paletteFilename.size() > 0);
-                    break;
-                }
-            }
-            
-            _needsRedraw = true;
-        }
-    } else {
-        _decoder.seekToFrame(0);
-    }
-
-    RenderObject::updateGraphics();
+	if (!_decoder.isVideoLoaded()) {
+		return;
+	}
+
+	if (_isPlaying) {
+		if (!_decoder.isPlaying()) {
+			_decoder.start();
+		}
+
+		switch (_hoverState) {
+		case kNoHover:
+			if (_isHovered) {
+				_decoder.seekToFrame(_onHoverFirstFrame);
+
+				_hoverState = kHover;
+			} else {
+				if (_decoder.getCurFrame() == _loopLastFrame) {
+					// loop back to beginning
+					_decoder.seekToFrame(_loopFirstFrame);
+				}
+
+				break;
+			}
+			// fall through
+		case kHover:
+			if (!_isHovered) {
+				// Stopped hovering, reverse playback
+				_decoder.seekToFrame(_onHoverEndLastFrame);
+				_decoder.setRate(-_decoder.getRate());
+				if (!_decoder.isPlaying()) {
+					_decoder.start();
+				}
+
+				_hoverState = kEndHover;
+			} else {
+				break;
+			}
+			// fall through
+		case kEndHover:
+			if (_decoder.getCurFrame() == _onHoverEndFirstFrame) {
+				// reversed playback has ended, go back to no hover _state
+				_decoder.seekToFrame(_loopFirstFrame);
+				_decoder.setRate(-_decoder.getRate());
+				_hoverState = kNoHover;
+			}
+
+			break;
+		}
+
+		if (_decoder.needsUpdate() && !_screenPosition.isEmpty()) {
+			for (uint i = 0; i < _videoDescs.size(); ++i) {
+				if ((uint16)_videoDescs[i].frameID == _currentViewportFrame) {
+					// This ignores the srcRects for every frame
+					GraphicsManager::copyToManaged(*_decoder.decodeNextFrame(), _drawSurface, _paletteFilename.size() > 0);
+					break;
+				}
+			}
+
+			_needsRedraw = true;
+		}
+	} else {
+		_decoder.seekToFrame(0);
+	}
+
+	RenderObject::updateGraphics();
 }
 
 void PlaySecondaryVideo::onPause(bool pause) {
-    _decoder.pauseVideo(pause);
+	_decoder.pauseVideo(pause);
 
-    if (pause) {
-        registerGraphics();
-    }
+	if (pause) {
+		registerGraphics();
+	}
 }
 
 void PlaySecondaryVideo::handleInput(NancyInput &input) {
-    if (_hasHotspot && NancySceneState.getViewport().convertViewportToScreen(_hotspot).contains(input.mousePos)) {
-        _isHovered = true;
-    } else {
-        _isHovered = false;
-    }
+	if (_hasHotspot && NancySceneState.getViewport().convertViewportToScreen(_hotspot).contains(input.mousePos)) {
+		_isHovered = true;
+	} else {
+		_isHovered = false;
+	}
 }
 
 void PlaySecondaryVideo::readData(Common::SeekableReadStream &stream) {
-    readFilename(stream, _filename);
-    readFilename(stream, _paletteFilename);
-    stream.skip(10);
-    
-    if (_paletteFilename.size()) {
-        stream.skip(14); // unknown data
-    }
-
-    _loopFirstFrame = stream.readUint16LE();
-    _loopLastFrame = stream.readUint16LE();
-    _onHoverFirstFrame = stream.readUint16LE();
-    _onHoverLastFrame = stream.readUint16LE();
-    _onHoverEndFirstFrame = stream.readUint16LE();
-    _onHoverEndLastFrame = stream.readUint16LE();
-
-    _sceneChange.readData(stream);
-
-    if (_paletteFilename.size()) {
-        stream.skip(3);
-    } else {
-        stream.skip(1);
-    }
-
-    uint16 numVideoDescs = stream.readUint16LE();
-    for (uint i = 0; i < numVideoDescs; ++i) {
-        _videoDescs.push_back(SecondaryVideoDescription());
-        _videoDescs[i].readData(stream);
-    }
+	readFilename(stream, _filename);
+	readFilename(stream, _paletteFilename);
+	stream.skip(10);
+
+	if (_paletteFilename.size()) {
+		stream.skip(14); // unknown data
+	}
+
+	_loopFirstFrame = stream.readUint16LE();
+	_loopLastFrame = stream.readUint16LE();
+	_onHoverFirstFrame = stream.readUint16LE();
+	_onHoverLastFrame = stream.readUint16LE();
+	_onHoverEndFirstFrame = stream.readUint16LE();
+	_onHoverEndLastFrame = stream.readUint16LE();
+
+	_sceneChange.readData(stream);
+
+	if (_paletteFilename.size()) {
+		stream.skip(3);
+	} else {
+		stream.skip(1);
+	}
+
+	uint16 numVideoDescs = stream.readUint16LE();
+	for (uint i = 0; i < numVideoDescs; ++i) {
+		_videoDescs.push_back(SecondaryVideoDescription());
+		_videoDescs[i].readData(stream);
+	}
 }
 
 void PlaySecondaryVideo::execute() {
-    switch (_state) {
-    case kBegin:
-        init();
-        registerGraphics();
-        _state = kRun;
-        // fall through
-    case kRun: {
-        // Set correct position according to viewport frame
-        if (_currentViewportFrame != NancySceneState.getSceneInfo().frameID) {
-            _currentViewportFrame = NancySceneState.getSceneInfo().frameID;
-
-            int activeFrame = -1;
-
-            for (uint i = 0; i < _videoDescs.size(); ++i) {
-                if ((uint16)_videoDescs[i].frameID == _currentViewportFrame) {
-                    activeFrame = i;
-                }
-            }
-
-            if (activeFrame != -1) {
-                // Make the drawing destination rectangle valid
-                _screenPosition = _videoDescs[activeFrame].destRect;
-
-                // Activate the hotspot
-                _hotspot = _videoDescs[activeFrame].destRect;
-                _hasHotspot = true;
-                _isPlaying = true;
-                setVisible(true);
-            } else {
-                setVisible(false);
-                _hasHotspot = false;
-                _isPlaying = false;
-            }
-        }
-
-        break;
-    }
-    case kActionTrigger:
-        NancySceneState.pushScene();
-        NancySceneState.changeScene(_sceneChange);
-        finishExecution();
-        break;
-    }
+	switch (_state) {
+	case kBegin:
+		init();
+		registerGraphics();
+		_state = kRun;
+		// fall through
+	case kRun: {
+		// Set correct position according to viewport frame
+		if (_currentViewportFrame != NancySceneState.getSceneInfo().frameID) {
+			_currentViewportFrame = NancySceneState.getSceneInfo().frameID;
+
+			int activeFrame = -1;
+
+			for (uint i = 0; i < _videoDescs.size(); ++i) {
+				if ((uint16)_videoDescs[i].frameID == _currentViewportFrame) {
+					activeFrame = i;
+				}
+			}
+
+			if (activeFrame != -1) {
+				// Make the drawing destination rectangle valid
+				_screenPosition = _videoDescs[activeFrame].destRect;
+
+				// Activate the hotspot
+				_hotspot = _videoDescs[activeFrame].destRect;
+				_hasHotspot = true;
+				_isPlaying = true;
+				setVisible(true);
+			} else {
+				setVisible(false);
+				_hasHotspot = false;
+				_isPlaying = false;
+			}
+		}
+
+		break;
+	}
+	case kActionTrigger:
+		NancySceneState.pushScene();
+		NancySceneState.changeScene(_sceneChange);
+		finishExecution();
+		break;
+	}
 }
 
 } // End of namespace Action
diff --git a/engines/nancy/action/secondaryvideo.h b/engines/nancy/action/secondaryvideo.h
index 106bdc98eb..201317de16 100644
--- a/engines/nancy/action/secondaryvideo.h
+++ b/engines/nancy/action/secondaryvideo.h
@@ -39,44 +39,44 @@ namespace Action {
 // different animations depending on whether the NPC is hovered by the mouse
 class PlaySecondaryVideo : public ActionRecord, public RenderObject {
 public:
-    enum HoverState { kNoHover, kHover, kEndHover };
-
-    PlaySecondaryVideo(uint chan, RenderObject &redrawFrom) : RenderObject(redrawFrom), channel(chan) {}
-    virtual ~PlaySecondaryVideo() { _decoder.close(); }
-
-    virtual void init() override;
-    virtual void updateGraphics() override;
-    virtual void onPause(bool pause) override;
-    virtual void handleInput(NancyInput &input) override;
-
-    virtual void readData(Common::SeekableReadStream &stream) override;
-    virtual void execute() override;
-
-    Common::String _filename;
-    Common::String _paletteFilename;
-    uint16 _loopFirstFrame = 0; // 0x1E
-    uint16 _loopLastFrame = 0; // 0x20
-    uint16 _onHoverFirstFrame = 0; // 0x22
-    uint16 _onHoverLastFrame = 0; // 0x24
-    uint16 _onHoverEndFirstFrame = 0; // 0x26
-    uint16 _onHoverEndLastFrame = 0; // 0x28
-    SceneChangeDescription _sceneChange; // 0x2A
-    // unknown byte
-    Common::Array<SecondaryVideoDescription> _videoDescs; // 0x35
+	enum HoverState { kNoHover, kHover, kEndHover };
+
+	PlaySecondaryVideo(uint chan, RenderObject &redrawFrom) : RenderObject(redrawFrom), channel(chan) {}
+	virtual ~PlaySecondaryVideo() { _decoder.close(); }
+
+	virtual void init() override;
+	virtual void updateGraphics() override;
+	virtual void onPause(bool pause) override;
+	virtual void handleInput(NancyInput &input) override;
+
+	virtual void readData(Common::SeekableReadStream &stream) override;
+	virtual void execute() override;
+
+	Common::String _filename;
+	Common::String _paletteFilename;
+	uint16 _loopFirstFrame = 0; // 0x1E
+	uint16 _loopLastFrame = 0; // 0x20
+	uint16 _onHoverFirstFrame = 0; // 0x22
+	uint16 _onHoverLastFrame = 0; // 0x24
+	uint16 _onHoverEndFirstFrame = 0; // 0x26
+	uint16 _onHoverEndLastFrame = 0; // 0x28
+	SceneChangeDescription _sceneChange; // 0x2A
+	// unknown byte
+	Common::Array<SecondaryVideoDescription> _videoDescs; // 0x35
 
 protected:
-    virtual Common::String getRecordTypeName() const override { return Common::String::format("PlaySecondaryVideoChan%i", channel); }
+	virtual Common::String getRecordTypeName() const override { return Common::String::format("PlaySecondaryVideoChan%i", channel); }
 
-    virtual uint16 getZOrder() const override { return 8; }
-    virtual bool isViewportRelative() const override { return true; }
+	virtual uint16 getZOrder() const override { return 8; }
+	virtual bool isViewportRelative() const override { return true; }
 
-    HoverState _hoverState = kNoHover;
-    AVFDecoder _decoder;
-    int _currentViewportFrame = -1;
-    bool _isPlaying = false;
-    bool _isHovered = false;
+	HoverState _hoverState = kNoHover;
+	AVFDecoder _decoder;
+	int _currentViewportFrame = -1;
+	bool _isPlaying = false;
+	bool _isHovered = false;
 
-    uint channel;
+	uint channel;
 };
 
 } // End of namespace Action
diff --git a/engines/nancy/action/sliderpuzzle.cpp b/engines/nancy/action/sliderpuzzle.cpp
index a36417c720..2663436c5e 100644
--- a/engines/nancy/action/sliderpuzzle.cpp
+++ b/engines/nancy/action/sliderpuzzle.cpp
@@ -38,296 +38,296 @@ Common::Array<Common::Array<int16>> SliderPuzzle::_playerTileOrder = Common::Arr
 bool SliderPuzzle::_playerHasTriedPuzzle = false;
 
 void SliderPuzzle::init() {
-    _drawSurface.create(_screenPosition.width(), _screenPosition.height(), GraphicsManager::getInputPixelFormat());
-    _drawSurface.clear(GraphicsManager::getTransColor());
-    
-    setTransparent(true);
+	_drawSurface.create(_screenPosition.width(), _screenPosition.height(), GraphicsManager::getInputPixelFormat());
+	_drawSurface.clear(GraphicsManager::getTransColor());
 
-    g_nancy->_resource->loadImage(_imageName, _image);
+	setTransparent(true);
+
+	g_nancy->_resource->loadImage(_imageName, _image);
 }
 
 void SliderPuzzle::readData(Common::SeekableReadStream &stream) {
-    readFilename(stream, _imageName);
-
-    _width = stream.readUint16LE();
-    _height = stream.readUint16LE();
-
-    for (uint y = 0; y < _height; ++y) {
-        _srcRects.push_back(Common::Array<Common::Rect>());
-        for (uint x = 0; x < _width; ++x) {
-            _srcRects.back().push_back(Common::Rect());
-            readRect(stream, _srcRects.back().back());
-        }
-        stream.skip((6 - _width) * 16);
-    }
-
-    stream.skip((6 - _height) * 6 * 16);
-
-    for (uint y = 0; y < _height; ++y) {
-        _destRects.push_back(Common::Array<Common::Rect>());
-        for (uint x = 0; x < _width; ++x) {
-            _destRects.back().push_back(Common::Rect());
-            readRect(stream, _destRects.back().back());
-
-            if (x == 0 && y == 0) {
-                _screenPosition = _destRects.back().back();
-            } else {
-                _screenPosition.extend(_destRects.back().back());
-            }
-        }
-        stream.skip((6 - _width) * 16);
-    }
-
-    stream.skip((6 - _height) * 6 * 16);
-
-    for (uint y = 0; y < _height; ++y) {
-        _correctTileOrder.push_back(Common::Array<int16>());
-        for (uint x = 0; x < _width; ++x) {
-            _correctTileOrder.back().push_back(stream.readSint16LE());
-        }
-        stream.skip((6 - _width) * 2);
-    }
-
-    stream.skip((6 - _height) * 6 * 2);
-
-    _clickSound.read(stream, SoundDescription::kNormal);
-    _solveExitScene.readData(stream);
-    stream.skip(2);
-    _flagOnSolve.label = stream.readSint16LE();
-    _flagOnSolve.flag = (NancyFlag)stream.readByte();
-    _solveSound.read(stream, SoundDescription::kNormal);
-    _exitScene.readData(stream);
-    stream.skip(2);
-    _flagOnExit.label = stream.readSint16LE();
-    _flagOnExit.flag = (NancyFlag)stream.readByte();
-    readRect(stream, _exitHotspot);
+	readFilename(stream, _imageName);
+
+	_width = stream.readUint16LE();
+	_height = stream.readUint16LE();
+
+	for (uint y = 0; y < _height; ++y) {
+		_srcRects.push_back(Common::Array<Common::Rect>());
+		for (uint x = 0; x < _width; ++x) {
+			_srcRects.back().push_back(Common::Rect());
+			readRect(stream, _srcRects.back().back());
+		}
+		stream.skip((6 - _width) * 16);
+	}
+
+	stream.skip((6 - _height) * 6 * 16);
+
+	for (uint y = 0; y < _height; ++y) {
+		_destRects.push_back(Common::Array<Common::Rect>());
+		for (uint x = 0; x < _width; ++x) {
+			_destRects.back().push_back(Common::Rect());
+			readRect(stream, _destRects.back().back());
+
+			if (x == 0 && y == 0) {
+				_screenPosition = _destRects.back().back();
+			} else {
+				_screenPosition.extend(_destRects.back().back());
+			}
+		}
+		stream.skip((6 - _width) * 16);
+	}
+
+	stream.skip((6 - _height) * 6 * 16);
+
+	for (uint y = 0; y < _height; ++y) {
+		_correctTileOrder.push_back(Common::Array<int16>());
+		for (uint x = 0; x < _width; ++x) {
+			_correctTileOrder.back().push_back(stream.readSint16LE());
+		}
+		stream.skip((6 - _width) * 2);
+	}
+
+	stream.skip((6 - _height) * 6 * 2);
+
+	_clickSound.read(stream, SoundDescription::kNormal);
+	_solveExitScene.readData(stream);
+	stream.skip(2);
+	_flagOnSolve.label = stream.readSint16LE();
+	_flagOnSolve.flag = (NancyFlag)stream.readByte();
+	_solveSound.read(stream, SoundDescription::kNormal);
+	_exitScene.readData(stream);
+	stream.skip(2);
+	_flagOnExit.label = stream.readSint16LE();
+	_flagOnExit.flag = (NancyFlag)stream.readByte();
+	readRect(stream, _exitHotspot);
 }
 
 void SliderPuzzle::execute() {
-    switch (_state) {
-    case kBegin:
-        init();
-        registerGraphics();
-        if (!_playerHasTriedPuzzle) {
-            Common::SeekableReadStream *spuz = g_nancy->getBootChunkStream("SPUZ");
-            _playerTileOrder.clear();
-            spuz->seek(NancySceneState.getDifficulty() * 0x48);
-            for (uint y = 0; y < _height; ++y) {
-                _playerTileOrder.push_back(Common::Array<int16>());
-
-                for (uint x = 0; x < _width; ++x) {
-                    _playerTileOrder.back().push_back(spuz->readSint16LE());
-                }
-
-                spuz->skip((6 - _width) * 2);
-            }
-
-            _playerHasTriedPuzzle = true;
-        }
-
-        for (uint y = 0; y < _height; ++y) {
-            for (uint x = 0; x < _width; ++x) {
-                drawTile(_playerTileOrder[y][x], x, y);
-            }
-        }
-
-        g_nancy->_sound->loadSound(_clickSound);
-        _state = kRun;
-        // fall through
-    case kRun:
-        switch (_solveState) {
-        case kNotSolved:
-            for (uint y = 0; y < _height; ++y) {
-                for (uint x = 0; x < _width; ++x) {
-                    if (_playerTileOrder[y][x] != _correctTileOrder[y][x]) {
-                        return;
-                    }
-                }
-            }
-
-            g_nancy->_sound->loadSound(_solveSound);
-            g_nancy->_sound->playSound(_solveSound);
-            _solveState = kWaitForSound;
-            break;
-        case kWaitForSound:
-            if (!g_nancy->_sound->isSoundPlaying(_solveSound)) {
-                g_nancy->_sound->stopSound(_solveSound);
-                _state = kActionTrigger;
-            }
-
-            break;
-        }
-
-        break;
-    case kActionTrigger:
-        switch (_solveState) {
-        case kNotSolved:
-            NancySceneState.changeScene(_exitScene);
-            NancySceneState.setEventFlag(_flagOnExit);
-            break;
-        case kWaitForSound:
-            NancySceneState.changeScene(_solveExitScene);
-            NancySceneState.setEventFlag(_flagOnSolve);
-            _playerHasTriedPuzzle = false;
-            break;
-        }
-
-        g_nancy->_sound->stopSound(_clickSound);
-        finishExecution();
-    }
+	switch (_state) {
+	case kBegin:
+		init();
+		registerGraphics();
+		if (!_playerHasTriedPuzzle) {
+			Common::SeekableReadStream *spuz = g_nancy->getBootChunkStream("SPUZ");
+			_playerTileOrder.clear();
+			spuz->seek(NancySceneState.getDifficulty() * 0x48);
+			for (uint y = 0; y < _height; ++y) {
+				_playerTileOrder.push_back(Common::Array<int16>());
+
+				for (uint x = 0; x < _width; ++x) {
+					_playerTileOrder.back().push_back(spuz->readSint16LE());
+				}
+
+				spuz->skip((6 - _width) * 2);
+			}
+
+			_playerHasTriedPuzzle = true;
+		}
+
+		for (uint y = 0; y < _height; ++y) {
+			for (uint x = 0; x < _width; ++x) {
+				drawTile(_playerTileOrder[y][x], x, y);
+			}
+		}
+
+		g_nancy->_sound->loadSound(_clickSound);
+		_state = kRun;
+		// fall through
+	case kRun:
+		switch (_solveState) {
+		case kNotSolved:
+			for (uint y = 0; y < _height; ++y) {
+				for (uint x = 0; x < _width; ++x) {
+					if (_playerTileOrder[y][x] != _correctTileOrder[y][x]) {
+						return;
+					}
+				}
+			}
+
+			g_nancy->_sound->loadSound(_solveSound);
+			g_nancy->_sound->playSound(_solveSound);
+			_solveState = kWaitForSound;
+			break;
+		case kWaitForSound:
+			if (!g_nancy->_sound->isSoundPlaying(_solveSound)) {
+				g_nancy->_sound->stopSound(_solveSound);
+				_state = kActionTrigger;
+			}
+
+			break;
+		}
+
+		break;
+	case kActionTrigger:
+		switch (_solveState) {
+		case kNotSolved:
+			NancySceneState.changeScene(_exitScene);
+			NancySceneState.setEventFlag(_flagOnExit);
+			break;
+		case kWaitForSound:
+			NancySceneState.changeScene(_solveExitScene);
+			NancySceneState.setEventFlag(_flagOnSolve);
+			_playerHasTriedPuzzle = false;
+			break;
+		}
+
+		g_nancy->_sound->stopSound(_clickSound);
+		finishExecution();
+	}
 }
 
 void SliderPuzzle::handleInput(NancyInput &input) {
-    if (_solveState != kNotSolved) {
-        return;
-    }
-
-    if (NancySceneState.getViewport().convertViewportToScreen(_exitHotspot).contains(input.mousePos)) {
-        g_nancy->_cursorManager->setCursorType(CursorManager::kExitArrow);
-
-        if (input.input & NancyInput::kLeftMouseButtonUp) {
-            _state = kActionTrigger;
-        }
-        
-        return;
-    }
-
-    int currentTileX = -1;
-    int currentTileY = -1;
-    uint direction = 0;
-    for (uint y = 0; y < _height; ++y) {
-        bool shouldBreak = false;
-        for (uint x = 0; x < _width; ++x) {
-            if (x > 0 && _playerTileOrder[y][x - 1] < 0) {
-                if (NancySceneState.getViewport().convertViewportToScreen(_destRects[y][x]).contains(input.mousePos)) {
-                    currentTileX = x;
-                    currentTileY = y;
-                    direction = kLeft;
-                    shouldBreak = true;
-                    break;
-                }
-            } else if ((int)x < _width - 1 && _playerTileOrder[y][x + 1] < 0) {
-                if (NancySceneState.getViewport().convertViewportToScreen(_destRects[y][x]).contains(input.mousePos)) {
-                    currentTileX = x;
-                    currentTileY = y;
-                    direction = kRight;
-                    shouldBreak = true;
-                    break;
-                }
-            } else if (y > 0 && _playerTileOrder[y - 1][x] < 0) {
-                if (NancySceneState.getViewport().convertViewportToScreen(_destRects[y][x]).contains(input.mousePos)) {
-                    currentTileX = x;
-                    currentTileY = y;
-                    direction = kUp;
-                    shouldBreak = true;
-                    break;
-                }
-            } else if ((int)y < _height - 1 && _playerTileOrder[y + 1][x] < 0) {
-                if (NancySceneState.getViewport().convertViewportToScreen(_destRects[y][x]).contains(input.mousePos)) {
-                    currentTileX = x;
-                    currentTileY = y;
-                    direction = kDown;
-                    shouldBreak = true;
-                    break;
-                }
-            }
-        }
-
-        if (shouldBreak) {
-            break;
-        }
-    }
-
-    if (currentTileX != -1) {
-        g_nancy->_cursorManager->setCursorType(CursorManager::kHotspot);
-
-        if (input.input & NancyInput::kLeftMouseButtonUp) {
-            g_nancy->_sound->playSound(_clickSound);
-            switch (direction) {
-            case kUp: {
-                uint curTileID = _playerTileOrder[currentTileY][currentTileX];
-                drawTile(curTileID, currentTileX, currentTileY - 1);
-                undrawTile(currentTileX, currentTileY);
-                _playerTileOrder[currentTileY - 1][currentTileX] = curTileID;
-                _playerTileOrder[currentTileY][currentTileX] = -10;
-                break;
-            }
-            case kDown: {
-                uint curTileID = _playerTileOrder[currentTileY][currentTileX];
-                drawTile(curTileID, currentTileX, currentTileY + 1);
-                undrawTile(currentTileX, currentTileY);
-                _playerTileOrder[currentTileY + 1][currentTileX] = curTileID;
-                _playerTileOrder[currentTileY][currentTileX] = -10;
-                break;
-            }
-            case kLeft: {
-                uint curTileID = _playerTileOrder[currentTileY][currentTileX];
-                drawTile(curTileID, currentTileX - 1, currentTileY);
-                undrawTile(currentTileX, currentTileY);
-                _playerTileOrder[currentTileY][currentTileX - 1] = curTileID;
-                _playerTileOrder[currentTileY][currentTileX] = -10;
-                break;
-            }
-            case kRight: {
-                uint curTileID = _playerTileOrder[currentTileY][currentTileX];
-                drawTile(curTileID, currentTileX + 1, currentTileY);
-                undrawTile(currentTileX, currentTileY);
-                _playerTileOrder[currentTileY][currentTileX + 1] = curTileID;
-                _playerTileOrder[currentTileY][currentTileX] = -10;
-                break;
-            }
-            }
-        }
-    }
+	if (_solveState != kNotSolved) {
+		return;
+	}
+
+	if (NancySceneState.getViewport().convertViewportToScreen(_exitHotspot).contains(input.mousePos)) {
+		g_nancy->_cursorManager->setCursorType(CursorManager::kExitArrow);
+
+		if (input.input & NancyInput::kLeftMouseButtonUp) {
+			_state = kActionTrigger;
+		}
+
+		return;
+	}
+
+	int currentTileX = -1;
+	int currentTileY = -1;
+	uint direction = 0;
+	for (uint y = 0; y < _height; ++y) {
+		bool shouldBreak = false;
+		for (uint x = 0; x < _width; ++x) {
+			if (x > 0 && _playerTileOrder[y][x - 1] < 0) {
+				if (NancySceneState.getViewport().convertViewportToScreen(_destRects[y][x]).contains(input.mousePos)) {
+					currentTileX = x;
+					currentTileY = y;
+					direction = kLeft;
+					shouldBreak = true;
+					break;
+				}
+			} else if ((int)x < _width - 1 && _playerTileOrder[y][x + 1] < 0) {
+				if (NancySceneState.getViewport().convertViewportToScreen(_destRects[y][x]).contains(input.mousePos)) {
+					currentTileX = x;
+					currentTileY = y;
+					direction = kRight;
+					shouldBreak = true;
+					break;
+				}
+			} else if (y > 0 && _playerTileOrder[y - 1][x] < 0) {
+				if (NancySceneState.getViewport().convertViewportToScreen(_destRects[y][x]).contains(input.mousePos)) {
+					currentTileX = x;
+					currentTileY = y;
+					direction = kUp;
+					shouldBreak = true;
+					break;
+				}
+			} else if ((int)y < _height - 1 && _playerTileOrder[y + 1][x] < 0) {
+				if (NancySceneState.getViewport().convertViewportToScreen(_destRects[y][x]).contains(input.mousePos)) {
+					currentTileX = x;
+					currentTileY = y;
+					direction = kDown;
+					shouldBreak = true;
+					break;
+				}
+			}
+		}
+
+		if (shouldBreak) {
+			break;
+		}
+	}
+
+	if (currentTileX != -1) {
+		g_nancy->_cursorManager->setCursorType(CursorManager::kHotspot);
+
+		if (input.input & NancyInput::kLeftMouseButtonUp) {
+			g_nancy->_sound->playSound(_clickSound);
+			switch (direction) {
+			case kUp: {
+				uint curTileID = _playerTileOrder[currentTileY][currentTileX];
+				drawTile(curTileID, currentTileX, currentTileY - 1);
+				undrawTile(currentTileX, currentTileY);
+				_playerTileOrder[currentTileY - 1][currentTileX] = curTileID;
+				_playerTileOrder[currentTileY][currentTileX] = -10;
+				break;
+			}
+			case kDown: {
+				uint curTileID = _playerTileOrder[currentTileY][currentTileX];
+				drawTile(curTileID, currentTileX, currentTileY + 1);
+				undrawTile(currentTileX, currentTileY);
+				_playerTileOrder[currentTileY + 1][currentTileX] = curTileID;
+				_playerTileOrder[currentTileY][currentTileX] = -10;
+				break;
+			}
+			case kLeft: {
+				uint curTileID = _playerTileOrder[currentTileY][currentTileX];
+				drawTile(curTileID, currentTileX - 1, currentTileY);
+				undrawTile(currentTileX, currentTileY);
+				_playerTileOrder[currentTileY][currentTileX - 1] = curTileID;
+				_playerTileOrder[currentTileY][currentTileX] = -10;
+				break;
+			}
+			case kRight: {
+				uint curTileID = _playerTileOrder[currentTileY][currentTileX];
+				drawTile(curTileID, currentTileX + 1, currentTileY);
+				undrawTile(currentTileX, currentTileY);
+				_playerTileOrder[currentTileY][currentTileX + 1] = curTileID;
+				_playerTileOrder[currentTileY][currentTileX] = -10;
+				break;
+			}
+			}
+		}
+	}
 }
 
 void SliderPuzzle::onPause(bool pause) {
-    if (pause) {
-        registerGraphics();
-    }
+	if (pause) {
+		registerGraphics();
+	}
 }
 
 void SliderPuzzle::synchronize(Common::Serializer &ser) {
-    ser.syncAsByte(_playerHasTriedPuzzle);
+	ser.syncAsByte(_playerHasTriedPuzzle);
 
-    byte x, y;
+	byte x, y;
 
-    if (ser.isSaving()) {
-        y = _playerTileOrder.size();
-        if (y) {
-            x = _playerTileOrder.back().size();
-        }
-    }
+	if (ser.isSaving()) {
+		y = _playerTileOrder.size();
+		if (y) {
+			x = _playerTileOrder.back().size();
+		}
+	}
 
-    ser.syncAsByte(x);
-    ser.syncAsByte(y);
+	ser.syncAsByte(x);
+	ser.syncAsByte(y);
 
-    _playerTileOrder.resize(y);
+	_playerTileOrder.resize(y);
 
-    for (int i = 0; i < y; ++i) {
-        _playerTileOrder[i].resize(x);
-        ser.syncArray(_playerTileOrder[i].data(), x, Common::Serializer::Sint16LE);
-    }
+	for (int i = 0; i < y; ++i) {
+		_playerTileOrder[i].resize(x);
+		ser.syncArray(_playerTileOrder[i].data(), x, Common::Serializer::Sint16LE);
+	}
 }
 
 void SliderPuzzle::drawTile(int tileID, uint posX, uint posY) {
-    if (tileID < 0) {
-        undrawTile(posX, posY);
-        return;
-    }
+	if (tileID < 0) {
+		undrawTile(posX, posY);
+		return;
+	}
 
-    Common::Point destPoint(_destRects[posY][posX].left - _screenPosition.left, _destRects[posY][posX].top - _screenPosition.top);
-    _drawSurface.blitFrom(_image, _srcRects[tileID / _height][tileID % _width], destPoint);
+	Common::Point destPoint(_destRects[posY][posX].left - _screenPosition.left, _destRects[posY][posX].top - _screenPosition.top);
+	_drawSurface.blitFrom(_image, _srcRects[tileID / _height][tileID % _width], destPoint);
 
-    _needsRedraw = true;
+	_needsRedraw = true;
 }
 
 void SliderPuzzle::undrawTile(uint posX, uint posY) {
-    Common::Rect bounds = _destRects[posY][posX];
-    bounds.translate(-_screenPosition.left, -_screenPosition.top);
-    _drawSurface.fillRect(bounds, GraphicsManager::getTransColor());
+	Common::Rect bounds = _destRects[posY][posX];
+	bounds.translate(-_screenPosition.left, -_screenPosition.top);
+	_drawSurface.fillRect(bounds, GraphicsManager::getTransColor());
 
-    _needsRedraw = true;
+	_needsRedraw = true;
 }
 
 } // End of namespace Action
diff --git a/engines/nancy/action/sliderpuzzle.h b/engines/nancy/action/sliderpuzzle.h
index 1ffe0b0163..7d82d56046 100644
--- a/engines/nancy/action/sliderpuzzle.h
+++ b/engines/nancy/action/sliderpuzzle.h
@@ -43,47 +43,47 @@ namespace Action {
 
 class SliderPuzzle: public ActionRecord, public RenderObject {
 public:
-    enum SolveState { kNotSolved, kWaitForSound };
-    SliderPuzzle(RenderObject &redrawFrom) : RenderObject(redrawFrom) {}
-    virtual ~SliderPuzzle() {}
-    
-    virtual void init() override;
-    
-    virtual void readData(Common::SeekableReadStream &stream) override;
-    virtual void execute() override;
-    virtual void handleInput(NancyInput &input) override;
-    virtual void onPause(bool pause) override;
-
-    static void synchronize(Common::Serializer &ser);
-
-    Common::String _imageName; // 0x00
-    uint16 _width; // 0xA
-    uint16 _height; // 0xC
-    Common::Array<Common::Array<Common::Rect>> _srcRects; // 0x0E, size 0x240
-    Common::Array<Common::Array<Common::Rect>> _destRects; // 0x24E, size 0x240
-    Common::Array<Common::Array<int16>> _correctTileOrder; // 0x48E, size 0x48
-    SoundDescription _clickSound; // 0x4D6
-    SceneChangeDescription _solveExitScene; // 0x4F8
-    EventFlagDescription _flagOnSolve; // 0x502
-    SoundDescription _solveSound; // 0x505
-    SceneChangeDescription _exitScene; // 0x527
-    EventFlagDescription _flagOnExit; // 0x531
-    Common::Rect _exitHotspot; // 0x534
-
-    SolveState _solveState = kNotSolved;
-    Graphics::ManagedSurface _image;
-
-    static Common::Array<Common::Array<int16>> _playerTileOrder;
-    static bool _playerHasTriedPuzzle;
+	enum SolveState { kNotSolved, kWaitForSound };
+	SliderPuzzle(RenderObject &redrawFrom) : RenderObject(redrawFrom) {}
+	virtual ~SliderPuzzle() {}
+
+	virtual void init() override;
+
+	virtual void readData(Common::SeekableReadStream &stream) override;
+	virtual void execute() override;
+	virtual void handleInput(NancyInput &input) override;
+	virtual void onPause(bool pause) override;
+
+	static void synchronize(Common::Serializer &ser);
+
+	Common::String _imageName; // 0x00
+	uint16 _width; // 0xA
+	uint16 _height; // 0xC
+	Common::Array<Common::Array<Common::Rect>> _srcRects; // 0x0E, size 0x240
+	Common::Array<Common::Array<Common::Rect>> _destRects; // 0x24E, size 0x240
+	Common::Array<Common::Array<int16>> _correctTileOrder; // 0x48E, size 0x48
+	SoundDescription _clickSound; // 0x4D6
+	SceneChangeDescription _solveExitScene; // 0x4F8
+	EventFlagDescription _flagOnSolve; // 0x502
+	SoundDescription _solveSound; // 0x505
+	SceneChangeDescription _exitScene; // 0x527
+	EventFlagDescription _flagOnExit; // 0x531
+	Common::Rect _exitHotspot; // 0x534
+
+	SolveState _solveState = kNotSolved;
+	Graphics::ManagedSurface _image;
+
+	static Common::Array<Common::Array<int16>> _playerTileOrder;
+	static bool _playerHasTriedPuzzle;
 
 protected:
-    virtual Common::String getRecordTypeName() const override { return "SliderPuzzle"; }
+	virtual Common::String getRecordTypeName() const override { return "SliderPuzzle"; }
 
-    virtual uint16 getZOrder() const override { return 7; }
-    virtual bool isViewportRelative() const override { return true; }
+	virtual uint16 getZOrder() const override { return 7; }
+	virtual bool isViewportRelative() const override { return true; }
 
-    void drawTile(int tileID, uint posX, uint posY);
-    void undrawTile(uint posX, uint posY);
+	void drawTile(int tileID, uint posX, uint posY);
+	void undrawTile(uint posX, uint posY);
 };
 
 } // End of namespace Action
diff --git a/engines/nancy/action/staticbitmapanim.cpp b/engines/nancy/action/staticbitmapanim.cpp
index 512ff83e41..318723649a 100644
--- a/engines/nancy/action/staticbitmapanim.cpp
+++ b/engines/nancy/action/staticbitmapanim.cpp
@@ -35,151 +35,151 @@ namespace Nancy {
 namespace Action {
 
 void PlayStaticBitmapAnimation::init() {
-    g_nancy->_resource->loadImage(_imageName, _fullSurface);
+	g_nancy->_resource->loadImage(_imageName, _fullSurface);
 
-    setFrame(0);
+	setFrame(0);
 
-    RenderObject::init();
+	RenderObject::init();
 }
 
 void PlayStaticBitmapAnimation::readData(Common::SeekableReadStream &stream) {
-    readFilename(stream, _imageName);
-
-    stream.skip(0x2);
-    _isTransparent = (NancyFlag)(stream.readUint16LE());
-    _doNotChangeScene = (NancyFlag)(stream.readUint16LE());
-    _isReverse = (NancyFlag)(stream.readUint16LE());
-    _isLooping = (NancyFlag)(stream.readUint16LE());
-    _firstFrame = stream.readUint16LE();
-    _loopFirstFrame = stream.readUint16LE();
-    _loopLastFrame = stream.readUint16LE();
-    _frameTime = Common::Rational(1000, stream.readUint16LE()).toInt();
-    _zOrder = stream.readUint16LE();
-
-    if (_isInterruptible) {
-        _interruptCondition.label = stream.readSint16LE();
-        _interruptCondition.flag = (NancyFlag)stream.readUint16LE();
-    } else {
-        _interruptCondition.label = -1;
-        _interruptCondition.flag = kFalse;
-    }
-
-    _sceneChange.readData(stream);
-    _triggerFlags.readData(stream);
-    _sound.read(stream, SoundDescription::kNormal);
-    uint numViewportFrames = stream.readUint16LE();
-
-    for (uint i = _firstFrame; i <= _loopLastFrame; ++i) {
-        _srcRects.push_back(Common::Rect());
-        readRect(stream, _srcRects[i]);
-    }
-
-    for (uint i = 0; i < numViewportFrames; ++i) {
-        _bitmaps.push_back(BitmapDescription());
-        BitmapDescription &rects = _bitmaps.back();
-        rects.frameID = stream.readUint16LE();
-        readRect(stream, rects.src);
-        readRect(stream, rects.dest);
-    }
+	readFilename(stream, _imageName);
+
+	stream.skip(0x2);
+	_isTransparent = (NancyFlag)(stream.readUint16LE());
+	_doNotChangeScene = (NancyFlag)(stream.readUint16LE());
+	_isReverse = (NancyFlag)(stream.readUint16LE());
+	_isLooping = (NancyFlag)(stream.readUint16LE());
+	_firstFrame = stream.readUint16LE();
+	_loopFirstFrame = stream.readUint16LE();
+	_loopLastFrame = stream.readUint16LE();
+	_frameTime = Common::Rational(1000, stream.readUint16LE()).toInt();
+	_zOrder = stream.readUint16LE();
+
+	if (_isInterruptible) {
+		_interruptCondition.label = stream.readSint16LE();
+		_interruptCondition.flag = (NancyFlag)stream.readUint16LE();
+	} else {
+		_interruptCondition.label = -1;
+		_interruptCondition.flag = kFalse;
+	}
+
+	_sceneChange.readData(stream);
+	_triggerFlags.readData(stream);
+	_sound.read(stream, SoundDescription::kNormal);
+	uint numViewportFrames = stream.readUint16LE();
+
+	for (uint i = _firstFrame; i <= _loopLastFrame; ++i) {
+		_srcRects.push_back(Common::Rect());
+		readRect(stream, _srcRects[i]);
+	}
+
+	for (uint i = 0; i < numViewportFrames; ++i) {
+		_bitmaps.push_back(BitmapDescription());
+		BitmapDescription &rects = _bitmaps.back();
+		rects.frameID = stream.readUint16LE();
+		readRect(stream, rects.src);
+		readRect(stream, rects.dest);
+	}
 }
 
 void PlayStaticBitmapAnimation::execute() {
-    uint32 _currentFrameTime = g_nancy->getTotalPlayTime();
-    switch (_state) {
-    case kBegin:
-        init();
-        registerGraphics();
-        g_nancy->_sound->loadSound(_sound);
-        g_nancy->_sound->playSound(_sound);
-        _state = kRun;
-        // fall through
-    case kRun: {
-        // Check the timer to see if we need to draw the next animation frame
-        if (_nextFrameTime <= _currentFrameTime) {
-            // World's worst if statement
-            if (NancySceneState.getEventFlag(_interruptCondition) ||
-                (   (((_currentFrame == _loopLastFrame) && (_isReverse == kFalse) && (_isLooping == kFalse)) ||
-                    ((_currentFrame == _loopFirstFrame) && (_isReverse == kTrue) && (_isLooping == kFalse))) &&
-                        !g_nancy->_sound->isSoundPlaying(_sound))   ) {
-                
-                _state = kActionTrigger;
-
-                // Not sure if hiding when triggered is a hack or the intended behavior, but it's here to fix
-                // nancy1's safe lock light not turning off.
-                setVisible(false);
-    
-                if (!g_nancy->_sound->isSoundPlaying(_sound)) {
-                    g_nancy->_sound->stopSound(_sound);
-                }
-            } else {
-                // Check if we've moved the viewport
-                uint16 newFrame = NancySceneState.getSceneInfo().frameID;
-
-                if (_currentViewportFrame != newFrame) {
-                    _currentViewportFrame = newFrame;
-
-                    for (uint i = 0; i < _bitmaps.size(); ++i) {
-                        if (_currentViewportFrame == _bitmaps[i].frameID) {
-                            _screenPosition = _bitmaps[i].dest;
-                            break;
-                        }
-                    }
-                }
-                
-                _nextFrameTime = _currentFrameTime + _frameTime;
-                setFrame(_currentFrame);
-
-                if (_isReverse == kTrue) {
-                    --_currentFrame;
-                    _currentFrame = _currentFrame < _loopFirstFrame ? _loopLastFrame : _currentFrame;
-                    return;
-                } else {
-                    ++_currentFrame;
-                    _currentFrame = _currentFrame > _loopLastFrame ? _loopFirstFrame : _currentFrame;
-                    return;
-                }
-            }                
-        } else {
-            // Check if we've moved the viewport
-            uint16 newFrame = NancySceneState.getSceneInfo().frameID;
-
-            if (_currentViewportFrame != newFrame) {
-                _currentViewportFrame = newFrame;
-                
-                for (uint i = 0; i < _bitmaps.size(); ++i) {
-                    if (_currentViewportFrame == _bitmaps[i].frameID) {
-                        _screenPosition = _bitmaps[i].dest;
-                        break;
-                    }
-                }
-            }
-        }      
-        
-        break;
-    }
-    case kActionTrigger:
-        _triggerFlags.execute();
-        if (_doNotChangeScene == kFalse) {
-            NancySceneState.changeScene(_sceneChange);
-            finishExecution();
-        }
-        break;
-    }
+	uint32 _currentFrameTime = g_nancy->getTotalPlayTime();
+	switch (_state) {
+	case kBegin:
+		init();
+		registerGraphics();
+		g_nancy->_sound->loadSound(_sound);
+		g_nancy->_sound->playSound(_sound);
+		_state = kRun;
+		// fall through
+	case kRun: {
+		// Check the timer to see if we need to draw the next animation frame
+		if (_nextFrameTime <= _currentFrameTime) {
+			// World's worst if statement
+			if (NancySceneState.getEventFlag(_interruptCondition) ||
+				(   (((_currentFrame == _loopLastFrame) && (_isReverse == kFalse) && (_isLooping == kFalse)) ||
+					((_currentFrame == _loopFirstFrame) && (_isReverse == kTrue) && (_isLooping == kFalse))) &&
+						!g_nancy->_sound->isSoundPlaying(_sound))   ) {
+
+				_state = kActionTrigger;
+
+				// Not sure if hiding when triggered is a hack or the intended behavior, but it's here to fix
+				// nancy1's safe lock light not turning off.
+				setVisible(false);
+
+				if (!g_nancy->_sound->isSoundPlaying(_sound)) {
+					g_nancy->_sound->stopSound(_sound);
+				}
+			} else {
+				// Check if we've moved the viewport
+				uint16 newFrame = NancySceneState.getSceneInfo().frameID;
+
+				if (_currentViewportFrame != newFrame) {
+					_currentViewportFrame = newFrame;
+
+					for (uint i = 0; i < _bitmaps.size(); ++i) {
+						if (_currentViewportFrame == _bitmaps[i].frameID) {
+							_screenPosition = _bitmaps[i].dest;
+							break;
+						}
+					}
+				}
+
+				_nextFrameTime = _currentFrameTime + _frameTime;
+				setFrame(_currentFrame);
+
+				if (_isReverse == kTrue) {
+					--_currentFrame;
+					_currentFrame = _currentFrame < _loopFirstFrame ? _loopLastFrame : _currentFrame;
+					return;
+				} else {
+					++_currentFrame;
+					_currentFrame = _currentFrame > _loopLastFrame ? _loopFirstFrame : _currentFrame;
+					return;
+				}
+			}
+		} else {
+			// Check if we've moved the viewport
+			uint16 newFrame = NancySceneState.getSceneInfo().frameID;
+
+			if (_currentViewportFrame != newFrame) {
+				_currentViewportFrame = newFrame;
+
+				for (uint i = 0; i < _bitmaps.size(); ++i) {
+					if (_currentViewportFrame == _bitmaps[i].frameID) {
+						_screenPosition = _bitmaps[i].dest;
+						break;
+					}
+				}
+			}
+		}
+
+		break;
+	}
+	case kActionTrigger:
+		_triggerFlags.execute();
+		if (_doNotChangeScene == kFalse) {
+			NancySceneState.changeScene(_sceneChange);
+			finishExecution();
+		}
+		break;
+	}
 }
 
 void PlayStaticBitmapAnimation::onPause(bool pause) {
-    if (pause) {
-        registerGraphics();
-    }
+	if (pause) {
+		registerGraphics();
+	}
 }
 
 void PlayStaticBitmapAnimation::setFrame(uint frame) {
-    _currentFrame = frame;
-    _drawSurface.create(_fullSurface, _srcRects[frame]);
-    
-    setTransparent(_isTransparent == kTrue);
+	_currentFrame = frame;
+	_drawSurface.create(_fullSurface, _srcRects[frame]);
 
-    _needsRedraw = true;
+	setTransparent(_isTransparent == kTrue);
+
+	_needsRedraw = true;
 }
 
 } // End of namespace Action
diff --git a/engines/nancy/action/staticbitmapanim.h b/engines/nancy/action/staticbitmapanim.h
index 66b4eac776..dbfbad0d2b 100644
--- a/engines/nancy/action/staticbitmapanim.h
+++ b/engines/nancy/action/staticbitmapanim.h
@@ -40,52 +40,52 @@ namespace Action {
 // action record types, whose functionality is nearly identical
 class PlayStaticBitmapAnimation : public ActionRecord, public RenderObject {
 public:
-    PlayStaticBitmapAnimation(bool interruptible, RenderObject &redrawFrom) : RenderObject(redrawFrom), _isInterruptible(interruptible) {}
-    virtual ~PlayStaticBitmapAnimation() { _fullSurface.free(); }
-
-    virtual void init() override;
-
-    virtual void readData(Common::SeekableReadStream &stream) override;
-    virtual void execute() override;
-    virtual void onPause(bool pause) override;
-
-    Common::String _imageName;
-
-    NancyFlag _isTransparent; // 0xC
-    NancyFlag _doNotChangeScene; // 0xE
-    NancyFlag _isReverse; // 0x10
-    NancyFlag _isLooping; // 0x12
-    uint16 _firstFrame; // 0x14
-    uint16 _loopFirstFrame; // 0x16
-    uint16 _loopLastFrame; // 0x18
-    Time _frameTime;
-    uint16 _zOrder; // 0x1C
-    EventFlagDescription _interruptCondition; // 0x1E
-    SceneChangeDescription _sceneChange;
-    MultiEventFlagDescription _triggerFlags; // 0x2A
-
-    Nancy::SoundDescription _sound; // 0x52
-
-    // Describes a single frame in this animation
-    Common::Array<Common::Rect> _srcRects;
-    // Describes how the animation will be displayed on a single
-    // frame of the viewport
-    Common::Array<BitmapDescription> _bitmaps;
-
-    int16 _currentFrame = -1;
-    int16 _currentViewportFrame = -1;
-    Time _nextFrameTime;
-    bool _isInterruptible;
-    
+	PlayStaticBitmapAnimation(bool interruptible, RenderObject &redrawFrom) : RenderObject(redrawFrom), _isInterruptible(interruptible) {}
+	virtual ~PlayStaticBitmapAnimation() { _fullSurface.free(); }
+
+	virtual void init() override;
+
+	virtual void readData(Common::SeekableReadStream &stream) override;
+	virtual void execute() override;
+	virtual void onPause(bool pause) override;
+
+	Common::String _imageName;
+
+	NancyFlag _isTransparent; // 0xC
+	NancyFlag _doNotChangeScene; // 0xE
+	NancyFlag _isReverse; // 0x10
+	NancyFlag _isLooping; // 0x12
+	uint16 _firstFrame; // 0x14
+	uint16 _loopFirstFrame; // 0x16
+	uint16 _loopLastFrame; // 0x18
+	Time _frameTime;
+	uint16 _zOrder; // 0x1C
+	EventFlagDescription _interruptCondition; // 0x1E
+	SceneChangeDescription _sceneChange;
+	MultiEventFlagDescription _triggerFlags; // 0x2A
+
+	Nancy::SoundDescription _sound; // 0x52
+
+	// Describes a single frame in this animation
+	Common::Array<Common::Rect> _srcRects;
+	// Describes how the animation will be displayed on a single
+	// frame of the viewport
+	Common::Array<BitmapDescription> _bitmaps;
+
+	int16 _currentFrame = -1;
+	int16 _currentViewportFrame = -1;
+	Time _nextFrameTime;
+	bool _isInterruptible;
+
 protected:
-    virtual Common::String getRecordTypeName() const override { return _isInterruptible ? "PlayIntStaticBitmapAnimation" : "PlayStaticBitmapAnimation"; }
+	virtual Common::String getRecordTypeName() const override { return _isInterruptible ? "PlayIntStaticBitmapAnimation" : "PlayStaticBitmapAnimation"; }
+
+	virtual uint16 getZOrder() const override { return _zOrder; }
+	virtual bool isViewportRelative() const override { return true; }
 
-    virtual uint16 getZOrder() const override { return _zOrder; }
-    virtual bool isViewportRelative() const override { return true; }
+	void setFrame(uint frame);
 
-    void setFrame(uint frame);
-    
-    Graphics::ManagedSurface _fullSurface;
+	Graphics::ManagedSurface _fullSurface;
 };
 
 } // End of namespace Action
diff --git a/engines/nancy/action/telephone.cpp b/engines/nancy/action/telephone.cpp
index c3c4d88a1f..e587e77317 100644
--- a/engines/nancy/action/telephone.cpp
+++ b/engines/nancy/action/telephone.cpp
@@ -37,280 +37,280 @@ namespace Nancy {
 namespace Action {
 
 void Telephone::init() {
-    _drawSurface.create(_screenPosition.width(), _screenPosition.height(), GraphicsManager::getInputPixelFormat());
-    _drawSurface.clear(GraphicsManager::getTransColor());
-    
-    setTransparent(true);
+	_drawSurface.create(_screenPosition.width(), _screenPosition.height(), GraphicsManager::getInputPixelFormat());
+	_drawSurface.clear(GraphicsManager::getTransColor());
 
-    g_nancy->_resource->loadImage(_imageName, _image);
+	setTransparent(true);
 
-    NancySceneState.setShouldClearTextbox(false);
+	g_nancy->_resource->loadImage(_imageName, _image);
+
+	NancySceneState.setShouldClearTextbox(false);
 }
 
 void Telephone::readData(Common::SeekableReadStream &stream) {
-    readFilename(stream, _imageName);
-
-    for (uint i = 0; i < 12; ++i) {
-        _srcRects.push_back(Common::Rect());
-        readRect(stream, _srcRects.back());
-    }
-
-    for (uint i = 0; i < 12; ++i) {
-        _destRects.push_back(Common::Rect());
-        readRect(stream, _destRects.back());
-
-        if (i == 0) {
-            _screenPosition = _destRects.back();
-        } else {
-            _screenPosition.extend(_destRects.back());
-        }
-    }
-
-    _genericDialogueSound.read(stream, SoundDescription::kNormal);
-    _genericButtonSound.read(stream, SoundDescription::kNormal);
-    _ringSound.read(stream, SoundDescription::kNormal);
-    _dialToneSound.read(stream, SoundDescription::kNormal);
-    _dialAgainSound.read(stream, SoundDescription::kNormal);
-    _hangUpSound.read(stream, SoundDescription::kNormal);
-
-    for (uint i = 0; i < 12; ++i) {
-        Common::String buttonSoundName;
-        readFilename(stream, buttonSoundName);
-        _buttonSoundNames.push_back(buttonSoundName);
-    }
-
-    char textBuf[200];
-    stream.read(textBuf, 200);
-    textBuf[199] = '\0';
-    _addressBookString = textBuf;
-    stream.read(textBuf, 200);
-    textBuf[199] = '\0';
-    _dialAgainString = textBuf;
-    _reloadScene.readData(stream);
-    stream.skip(2);
-    _flagOnReload.label = stream.readSint16LE();
-    _flagOnReload.flag = (NancyFlag)stream.readUint16LE();
-    _exitScene.readData(stream);
-    stream.skip(2);
-    _flagOnExit.label = stream.readSint16LE();
-    _flagOnExit.flag = (NancyFlag)stream.readUint16LE();
-    readRect(stream, _exitHotspot);
-
-    uint numCalls = stream.readUint16LE();
-
-    for (uint i = 0; i < numCalls; ++i) {
-        _calls.push_back(PhoneCall());
-        PhoneCall &call = _calls.back();
-
-        for (uint j = 0; j < 11; ++j) {
-            call.phoneNumber.push_back(stream.readByte());
-        }
-
-        readFilename(stream, call.soundName);
-        stream.read(textBuf, 200);
-        textBuf[199] = '\0';
-        call.text = textBuf;
-        call._sceneChange.readData(stream);
-        stream.skip(2);
-        call.flag.label = stream.readSint16LE();
-        call.flag.flag = (NancyFlag)stream.readUint16LE();
-    }
+	readFilename(stream, _imageName);
+
+	for (uint i = 0; i < 12; ++i) {
+		_srcRects.push_back(Common::Rect());
+		readRect(stream, _srcRects.back());
+	}
+
+	for (uint i = 0; i < 12; ++i) {
+		_destRects.push_back(Common::Rect());
+		readRect(stream, _destRects.back());
+
+		if (i == 0) {
+			_screenPosition = _destRects.back();
+		} else {
+			_screenPosition.extend(_destRects.back());
+		}
+	}
+
+	_genericDialogueSound.read(stream, SoundDescription::kNormal);
+	_genericButtonSound.read(stream, SoundDescription::kNormal);
+	_ringSound.read(stream, SoundDescription::kNormal);
+	_dialToneSound.read(stream, SoundDescription::kNormal);
+	_dialAgainSound.read(stream, SoundDescription::kNormal);
+	_hangUpSound.read(stream, SoundDescription::kNormal);
+
+	for (uint i = 0; i < 12; ++i) {
+		Common::String buttonSoundName;
+		readFilename(stream, buttonSoundName);
+		_buttonSoundNames.push_back(buttonSoundName);
+	}
+
+	char textBuf[200];
+	stream.read(textBuf, 200);
+	textBuf[199] = '\0';
+	_addressBookString = textBuf;
+	stream.read(textBuf, 200);
+	textBuf[199] = '\0';
+	_dialAgainString = textBuf;
+	_reloadScene.readData(stream);
+	stream.skip(2);
+	_flagOnReload.label = stream.readSint16LE();
+	_flagOnReload.flag = (NancyFlag)stream.readUint16LE();
+	_exitScene.readData(stream);
+	stream.skip(2);
+	_flagOnExit.label = stream.readSint16LE();
+	_flagOnExit.flag = (NancyFlag)stream.readUint16LE();
+	readRect(stream, _exitHotspot);
+
+	uint numCalls = stream.readUint16LE();
+
+	for (uint i = 0; i < numCalls; ++i) {
+		_calls.push_back(PhoneCall());
+		PhoneCall &call = _calls.back();
+
+		for (uint j = 0; j < 11; ++j) {
+			call.phoneNumber.push_back(stream.readByte());
+		}
+
+		readFilename(stream, call.soundName);
+		stream.read(textBuf, 200);
+		textBuf[199] = '\0';
+		call.text = textBuf;
+		call._sceneChange.readData(stream);
+		stream.skip(2);
+		call.flag.label = stream.readSint16LE();
+		call.flag.flag = (NancyFlag)stream.readUint16LE();
+	}
 }
 
 void Telephone::execute() {
-    switch (_state) {
-    case kBegin:
-        init();
-        registerGraphics();
-        g_nancy->_sound->loadSound(_dialToneSound);
-        g_nancy->_sound->playSound(_dialToneSound);
-        NancySceneState.getTextbox().clear();
-        NancySceneState.getTextbox().addTextLine(_addressBookString);
-        _state = kRun;
-        // fall through
-    case kRun:
-        switch (_callState) {
-        case kWaiting:
-            // Long phone numbers start with 1
-            if (_calledNumber.size() >= 11 || (_calledNumber.size() >= 7 && (_calledNumber[0] != 1))) {
-                NancySceneState.getTextbox().clear();
-                NancySceneState.getTextbox().addTextLine("ringing...<n><e>"); // Hardcoded in the original engine
-                g_nancy->_sound->loadSound(_ringSound);
-                g_nancy->_sound->playSound(_ringSound);
-                _callState = kRinging;
-            }
-
-            break;
-        case kButtonPress:
-            if (!g_nancy->_sound->isSoundPlaying(_genericButtonSound)) {
-                g_nancy->_sound->stopSound(_genericButtonSound);
-                undrawButton(_selected);
-                _callState = kWaiting;
-            }
-
-            break;
-        case kRinging:
-            if (!g_nancy->_sound->isSoundPlaying(_ringSound)) {
-                g_nancy->_sound->stopSound(_ringSound);
-                uint numberLength = _calledNumber[0] == 1 ? 11 : 7;
-
-                for (uint i = 0; i < _calls.size(); ++i) {
-                    bool invalid = false;
-
-                    for (uint j = 0; j < numberLength; ++j) {
-                        if (_calledNumber[j] != _calls[i].phoneNumber[j]) {
-                            // Invalid number, move onto next
-                            invalid = true;
-                            break;
-                        }
-                    }
-
-                    if (invalid) {
-                        continue;
-                    }
-
-                    NancySceneState.getTextbox().clear();
-                    NancySceneState.getTextbox().addTextLine(_calls[i].text);
-
-                    _genericDialogueSound.name = _calls[i].soundName;
-                    g_nancy->_sound->loadSound(_genericDialogueSound);
-                    g_nancy->_sound->playSound(_genericDialogueSound);
-                    _selected = i;
-                    _callState = kCall;
-
-                    return;
-                }
-                
-                NancySceneState.getTextbox().clear();
-                NancySceneState.getTextbox().addTextLine(_dialAgainString);
-
-                g_nancy->_sound->loadSound(_dialAgainSound);
-                g_nancy->_sound->playSound(_dialAgainSound);
-                _callState = kBadNumber;
-                return;
-            }
-
-            break;
-        case kBadNumber:
-            if (!g_nancy->_sound->isSoundPlaying(_dialAgainSound)) {
-                g_nancy->_sound->stopSound(_dialAgainSound);
-
-                _state = kActionTrigger;
-            }
-
-            break;
-        case kCall:
-            if (!g_nancy->_sound->isSoundPlaying(_genericDialogueSound)) {
-                g_nancy->_sound->stopSound(_genericDialogueSound);
-
-                _state = kActionTrigger;
-            }
-
-            break;
-        case kHangUp:
-            if (!g_nancy->_sound->isSoundPlaying(_hangUpSound)) {
-                g_nancy->_sound->stopSound(_hangUpSound);
-
-                _state = kActionTrigger;
-            }
-
-            break;
-        }
-
-        break;
-    case kActionTrigger:
-        switch (_callState) {
-        case kBadNumber:
-            NancySceneState.changeScene(_reloadScene);
-            _calledNumber.clear();
-            NancySceneState.setEventFlag(_flagOnReload);
-            _state = kRun;
-            _callState = kWaiting;
-
-            break;
-        case kCall: {
-            PhoneCall &call = _calls[_selected];
-            NancySceneState.changeScene(call._sceneChange);
-            NancySceneState.setEventFlag(call.flag);
-
-            break;
-        }
-        case kHangUp:
-            NancySceneState.changeScene(_exitScene);
-            NancySceneState.setEventFlag(_flagOnExit);
-            
-            break;
-        default:
-            break;
-        }
-
-        finishExecution();
-        NancySceneState.setShouldClearTextbox(true);
-        NancySceneState.getTextbox().clear();
-    }
+	switch (_state) {
+	case kBegin:
+		init();
+		registerGraphics();
+		g_nancy->_sound->loadSound(_dialToneSound);
+		g_nancy->_sound->playSound(_dialToneSound);
+		NancySceneState.getTextbox().clear();
+		NancySceneState.getTextbox().addTextLine(_addressBookString);
+		_state = kRun;
+		// fall through
+	case kRun:
+		switch (_callState) {
+		case kWaiting:
+			// Long phone numbers start with 1
+			if (_calledNumber.size() >= 11 || (_calledNumber.size() >= 7 && (_calledNumber[0] != 1))) {
+				NancySceneState.getTextbox().clear();
+				NancySceneState.getTextbox().addTextLine("ringing...<n><e>"); // Hardcoded in the original engine
+				g_nancy->_sound->loadSound(_ringSound);
+				g_nancy->_sound->playSound(_ringSound);
+				_callState = kRinging;
+			}
+
+			break;
+		case kButtonPress:
+			if (!g_nancy->_sound->isSoundPlaying(_genericButtonSound)) {
+				g_nancy->_sound->stopSound(_genericButtonSound);
+				undrawButton(_selected);
+				_callState = kWaiting;
+			}
+
+			break;
+		case kRinging:
+			if (!g_nancy->_sound->isSoundPlaying(_ringSound)) {
+				g_nancy->_sound->stopSound(_ringSound);
+				uint numberLength = _calledNumber[0] == 1 ? 11 : 7;
+
+				for (uint i = 0; i < _calls.size(); ++i) {
+					bool invalid = false;
+
+					for (uint j = 0; j < numberLength; ++j) {
+						if (_calledNumber[j] != _calls[i].phoneNumber[j]) {
+							// Invalid number, move onto next
+							invalid = true;
+							break;
+						}
+					}
+
+					if (invalid) {
+						continue;
+					}
+
+					NancySceneState.getTextbox().clear();
+					NancySceneState.getTextbox().addTextLine(_calls[i].text);
+
+					_genericDialogueSound.name = _calls[i].soundName;
+					g_nancy->_sound->loadSound(_genericDialogueSound);
+					g_nancy->_sound->playSound(_genericDialogueSound);
+					_selected = i;
+					_callState = kCall;
+
+					return;
+				}
+
+				NancySceneState.getTextbox().clear();
+				NancySceneState.getTextbox().addTextLine(_dialAgainString);
+
+				g_nancy->_sound->loadSound(_dialAgainSound);
+				g_nancy->_sound->playSound(_dialAgainSound);
+				_callState = kBadNumber;
+				return;
+			}
+
+			break;
+		case kBadNumber:
+			if (!g_nancy->_sound->isSoundPlaying(_dialAgainSound)) {
+				g_nancy->_sound->stopSound(_dialAgainSound);
+
+				_state = kActionTrigger;
+			}
+
+			break;
+		case kCall:
+			if (!g_nancy->_sound->isSoundPlaying(_genericDialogueSound)) {
+				g_nancy->_sound->stopSound(_genericDialogueSound);
+
+				_state = kActionTrigger;
+			}
+
+			break;
+		case kHangUp:
+			if (!g_nancy->_sound->isSoundPlaying(_hangUpSound)) {
+				g_nancy->_sound->stopSound(_hangUpSound);
+
+				_state = kActionTrigger;
+			}
+
+			break;
+		}
+
+		break;
+	case kActionTrigger:
+		switch (_callState) {
+		case kBadNumber:
+			NancySceneState.changeScene(_reloadScene);
+			_calledNumber.clear();
+			NancySceneState.setEventFlag(_flagOnReload);
+			_state = kRun;
+			_callState = kWaiting;
+
+			break;
+		case kCall: {
+			PhoneCall &call = _calls[_selected];
+			NancySceneState.changeScene(call._sceneChange);
+			NancySceneState.setEventFlag(call.flag);
+
+			break;
+		}
+		case kHangUp:
+			NancySceneState.changeScene(_exitScene);
+			NancySceneState.setEventFlag(_flagOnExit);
+
+			break;
+		default:
+			break;
+		}
+
+		finishExecution();
+		NancySceneState.setShouldClearTextbox(true);
+		NancySceneState.getTextbox().clear();
+	}
 }
 
 void Telephone::handleInput(NancyInput &input) {
-    int buttonNr = -1;
-    // Cursor gets changed regardless of state
-    for (uint i = 0; i < 12; ++i) {
-        if (NancySceneState.getViewport().convertViewportToScreen(_destRects[i]).contains(input.mousePos)) {
-            g_nancy->_cursorManager->setCursorType(CursorManager::kHotspot);
-            buttonNr = i;
-            break;
-        }
-    }
-
-    if (_callState != kWaiting) {
-        return;
-    }
-
-    if (NancySceneState.getViewport().convertViewportToScreen(_exitHotspot).contains(input.mousePos)) {
-        g_nancy->_cursorManager->setCursorType(CursorManager::kExitArrow);
-
-        if (input.input & NancyInput::kLeftMouseButtonUp) {
-            g_nancy->_sound->loadSound(_hangUpSound);
-            g_nancy->_sound->playSound(_hangUpSound);
-
-            _callState = kHangUp;
-        }
-        
-        return;
-    }
-
-    if (buttonNr != -1) {
-        if (input.input & NancyInput::kLeftMouseButtonUp) {
-            if (g_nancy->_sound->isSoundPlaying(_dialToneSound)) {
-                g_nancy->_sound->stopSound(_dialToneSound);
-            }
-
-            _calledNumber.push_back(buttonNr);
-            _genericButtonSound.name = _buttonSoundNames[buttonNr];
-            g_nancy->_sound->loadSound(_genericButtonSound);
-            g_nancy->_sound->playSound(_genericButtonSound);
-
-            drawButton(buttonNr);
-
-            _selected = buttonNr;
-
-            _callState = kButtonPress;
-        }
-    }
+	int buttonNr = -1;
+	// Cursor gets changed regardless of state
+	for (uint i = 0; i < 12; ++i) {
+		if (NancySceneState.getViewport().convertViewportToScreen(_destRects[i]).contains(input.mousePos)) {
+			g_nancy->_cursorManager->setCursorType(CursorManager::kHotspot);
+			buttonNr = i;
+			break;
+		}
+	}
+
+	if (_callState != kWaiting) {
+		return;
+	}
+
+	if (NancySceneState.getViewport().convertViewportToScreen(_exitHotspot).contains(input.mousePos)) {
+		g_nancy->_cursorManager->setCursorType(CursorManager::kExitArrow);
+
+		if (input.input & NancyInput::kLeftMouseButtonUp) {
+			g_nancy->_sound->loadSound(_hangUpSound);
+			g_nancy->_sound->playSound(_hangUpSound);
+
+			_callState = kHangUp;
+		}
+
+		return;
+	}
+
+	if (buttonNr != -1) {
+		if (input.input & NancyInput::kLeftMouseButtonUp) {
+			if (g_nancy->_sound->isSoundPlaying(_dialToneSound)) {
+				g_nancy->_sound->stopSound(_dialToneSound);
+			}
+
+			_calledNumber.push_back(buttonNr);
+			_genericButtonSound.name = _buttonSoundNames[buttonNr];
+			g_nancy->_sound->loadSound(_genericButtonSound);
+			g_nancy->_sound->playSound(_genericButtonSound);
+
+			drawButton(buttonNr);
+
+			_selected = buttonNr;
+
+			_callState = kButtonPress;
+		}
+	}
 }
 
 void Telephone::drawButton(uint id) {
-    Common::Point destPoint(_destRects[id].left - _screenPosition.left, _destRects[id].top - _screenPosition.top);
-    _drawSurface.blitFrom(_image, _srcRects[id], destPoint);
+	Common::Point destPoint(_destRects[id].left - _screenPosition.left, _destRects[id].top - _screenPosition.top);
+	_drawSurface.blitFrom(_image, _srcRects[id], destPoint);
 
-    _needsRedraw = true;
+	_needsRedraw = true;
 }
 
 void Telephone::undrawButton(uint id) {
-    Common::Rect bounds = _destRects[id];
-    bounds.translate(-_screenPosition.left, -_screenPosition.top);
+	Common::Rect bounds = _destRects[id];
+	bounds.translate(-_screenPosition.left, -_screenPosition.top);
 
-    _drawSurface.fillRect(bounds, GraphicsManager::getTransColor());
-    _needsRedraw = true;
+	_drawSurface.fillRect(bounds, GraphicsManager::getTransColor());
+	_needsRedraw = true;
 }
 
 } // End of namespace Action
diff --git a/engines/nancy/action/telephone.h b/engines/nancy/action/telephone.h
index 612a507301..24ce767765 100644
--- a/engines/nancy/action/telephone.h
+++ b/engines/nancy/action/telephone.h
@@ -39,65 +39,65 @@ namespace Action {
 
 class Telephone : public ActionRecord, public RenderObject {
 public:
-    struct PhoneCall {
-        Common::Array<byte> phoneNumber; // 0x0, 11 bytes
-        Common::String soundName; // 0xB
-        Common::String text; // 0x15, 0xC8 bytes
-        SceneChangeDescription _sceneChange; // 0xDD
-        // shouldStopRendering
-        EventFlagDescription flag; // 0xE7
-    };
-
-    enum CallState { kWaiting, kButtonPress, kRinging, kBadNumber, kCall, kHangUp };
-
-    Telephone(RenderObject &redrawFrom) :
-        RenderObject(redrawFrom),
-        _callState(kWaiting),
-        _selected(0) {}
-    virtual ~Telephone() {}
-
-    virtual void init() override;
-
-    virtual void readData(Common::SeekableReadStream &stream) override;
-    virtual void execute() override;
-    virtual void handleInput(NancyInput &input) override;
-
-    Common::String _imageName; // 0x00
-    Common::Array<Common::Rect> _srcRects; // 0xA, 12
-    Common::Array<Common::Rect> _destRects; // 0xCA, 12
-    SoundDescription _genericDialogueSound; // 0x18A
-    SoundDescription _genericButtonSound; // 0x1AC
-    SoundDescription _ringSound; // 0x1CE
-    SoundDescription _dialToneSound; // 0x1F0
-    SoundDescription _dialAgainSound; // 0x212
-    SoundDescription _hangUpSound; // 0x234
-    Common::Array<Common::String> _buttonSoundNames; // 0x256, 12 * 0xA
-    Common::String _addressBookString; // 0x2CE, 0xC8 long
-    Common::String _dialAgainString; // 0x396
-    SceneChangeDescription _reloadScene; // 0x45E
-    EventFlagDescription _flagOnReload; // 0x468 ??
-    SceneChangeDescription _exitScene; // 0x46C
-    EventFlagDescription _flagOnExit; // 0x476
-    Common::Rect _exitHotspot; // 0x47A
-    // 0x48A numConvos
-    Common::Array<PhoneCall> _calls; // 0x48C
-
-    Common::Array<byte> _calledNumber;
-    Graphics::ManagedSurface _image;
-    CallState _callState;
-    uint _selected;
+	struct PhoneCall {
+		Common::Array<byte> phoneNumber; // 0x0, 11 bytes
+		Common::String soundName; // 0xB
+		Common::String text; // 0x15, 0xC8 bytes
+		SceneChangeDescription _sceneChange; // 0xDD
+		// shouldStopRendering
+		EventFlagDescription flag; // 0xE7
+	};
+
+	enum CallState { kWaiting, kButtonPress, kRinging, kBadNumber, kCall, kHangUp };
+
+	Telephone(RenderObject &redrawFrom) :
+		RenderObject(redrawFrom),
+		_callState(kWaiting),
+		_selected(0) {}
+	virtual ~Telephone() {}
+
+	virtual void init() override;
+
+	virtual void readData(Common::SeekableReadStream &stream) override;
+	virtual void execute() override;
+	virtual void handleInput(NancyInput &input) override;
+
+	Common::String _imageName; // 0x00
+	Common::Array<Common::Rect> _srcRects; // 0xA, 12
+	Common::Array<Common::Rect> _destRects; // 0xCA, 12
+	SoundDescription _genericDialogueSound; // 0x18A
+	SoundDescription _genericButtonSound; // 0x1AC
+	SoundDescription _ringSound; // 0x1CE
+	SoundDescription _dialToneSound; // 0x1F0
+	SoundDescription _dialAgainSound; // 0x212
+	SoundDescription _hangUpSound; // 0x234
+	Common::Array<Common::String> _buttonSoundNames; // 0x256, 12 * 0xA
+	Common::String _addressBookString; // 0x2CE, 0xC8 long
+	Common::String _dialAgainString; // 0x396
+	SceneChangeDescription _reloadScene; // 0x45E
+	EventFlagDescription _flagOnReload; // 0x468 ??
+	SceneChangeDescription _exitScene; // 0x46C
+	EventFlagDescription _flagOnExit; // 0x476
+	Common::Rect _exitHotspot; // 0x47A
+	// 0x48A numConvos
+	Common::Array<PhoneCall> _calls; // 0x48C
+
+	Common::Array<byte> _calledNumber;
+	Graphics::ManagedSurface _image;
+	CallState _callState;
+	uint _selected;
 
 protected:
-    virtual Common::String getRecordTypeName() const override { return "Telephone"; }
+	virtual Common::String getRecordTypeName() const override { return "Telephone"; }
 
-    virtual uint16 getZOrder() const override { return 7; }
-    virtual bool isViewportRelative() const override { return true; }
+	virtual uint16 getZOrder() const override { return 7; }
+	virtual bool isViewportRelative() const override { return true; }
 
-    void drawButton(uint id);
-    void undrawButton(uint id);
+	void drawButton(uint id);
+	void undrawButton(uint id);
 };
 
 } // End of namespace Action
 } // End of namespace Nancy
 
-#endif // NANCY_ACTION_TELEPHONE_H
\ No newline at end of file
+#endif // NANCY_ACTION_TELEPHONE_H
diff --git a/engines/nancy/cheat.cpp b/engines/nancy/cheat.cpp
index 25bfc5fe51..55a2de550d 100644
--- a/engines/nancy/cheat.cpp
+++ b/engines/nancy/cheat.cpp
@@ -37,307 +37,307 @@
 namespace Nancy {
 
 CheatDialog::CheatDialog() : GUI::Dialog(20, 20, 600, 440) {
-    _backgroundType = GUI::ThemeEngine::kDialogBackgroundSpecial;
-    Common::WinResources *res = Common::WinResources::createFromEXE("game.exe");
-    Common::Array<Common::WinResourceID> dialogIDs = res->getIDList(Common::kWinDialog);
-    State::SceneInfo scene = NancySceneState.getSceneInfo();
-    Time playerTime = NancySceneState._timers.playerTime;
-    Time timerTime = NancySceneState._timers.timerTime;
-    bool timerIsActive = NancySceneState._timers.timerIsActive;
-    if (!timerIsActive) {
-        timerTime = 0;
-    }
-    char buf[4];
-
-    GUI::TabWidget *_tabs = new GUI::TabWidget(this, 0, 0, 600, 370);
-    new GUI::ButtonWidget(this, 420, 410, 60, 20, _("Cancel"), Common::U32String(), GUI::kCloseCmd);
-    new GUI::ButtonWidget(this, 520, 410, 60, 20, _("Ok"), Common::U32String(), GUI::kOKCmd);
-
-    _tabs->addTab(_("General"), _("Cheat.General"));
-    
-    new GUI::StaticTextWidget(_tabs, 30, 20, 150, 20, _("Scene Data"), Graphics::kTextAlignLeft);
-    _restartScene = new GUI::CheckboxWidget(_tabs, 35, 50, 150, 20, _("Restart the Scene"), _(""));
-    _scene = new GUI::EditTextWidget(_tabs, 35, 75, 45, 20, _(Common::U32String::itoa(scene.sceneID, buf, 10)), _(""), kInputSceneNr, kInputSceneNr);
-    new GUI::StaticTextWidget(_tabs, 85, 75, 150, 20, _("Scene Number"), Graphics::kTextAlignLeft);
-    _frame = new GUI::EditTextWidget(_tabs, 35, 100, 45, 20, _(Common::U32String::itoa(scene.frameID, buf, 10)), _(""), kInputFrameNr, kInputFrameNr);
-    new GUI::StaticTextWidget(_tabs, 85, 100, 150, 20, _("Frame Number"), Graphics::kTextAlignLeft);
-    _offset = new GUI::EditTextWidget(_tabs, 35, 125, 45, 20, _(Common::U32String::itoa(scene.verticalOffset, buf, 10)), _(""), kInputScroll, kInputScroll);
-    new GUI::StaticTextWidget(_tabs, 85, 125, 150, 20, _("Background Top (Y)"), Graphics::kTextAlignLeft);
-
-    new GUI::StaticTextWidget(_tabs, 30, 160, 150, 20, _("Hints Remaining"), Graphics::kTextAlignLeft);
-    new GUI::StaticTextWidget(_tabs, 35, 185, 45, 20, _("Easy"), Graphics::kTextAlignLeft);
-    _hintsRemainingEasy = new GUI::EditTextWidget(_tabs, 35, 205, 45, 20, _(Common::U32String::itoa(NancySceneState._hintsRemaining[0], buf, 10)), _(""), kInputHintsEasy, kInputHintsEasy);
-    new GUI::StaticTextWidget(_tabs, 85, 185, 45, 20, _("Medium"), Graphics::kTextAlignLeft);
-    _hintsRemainingMedium = new GUI::EditTextWidget(_tabs, 85, 205, 45, 20, _(Common::U32String::itoa(NancySceneState._hintsRemaining[1], buf, 10)), _(""), kInputHintsMedium, kInputHintsMedium);
-    new GUI::StaticTextWidget(_tabs, 135, 185, 45, 20, _("Hard"), Graphics::kTextAlignLeft);
-    _hintsRemainingHard = new GUI::EditTextWidget(_tabs, 135, 205, 45, 20, _(Common::U32String::itoa(NancySceneState._hintsRemaining[2], buf, 10)), _(""), kInputHintsHard, kInputHintsHard);
-    
-    new GUI::StaticTextWidget(_tabs, 250, 20, 150, 20, _("Player Data"), Graphics::kTextAlignLeft);
-    new GUI::StaticTextWidget(_tabs, 255, 50, 150, 20, _("Player Time:"), Graphics::kTextAlignLeft);
-    _playerTimeDays = new GUI::EditTextWidget(_tabs, 255, 75, 35, 20, _(Common::U32String::itoa(playerTime.getDays(), buf, 10)), _(""), kInputPlayerTime, kInputPlayerTime);
-    new GUI::StaticTextWidget(_tabs, 295, 75, 40, 20, _("Days"), Graphics::kTextAlignLeft);
-    _playerTimeHours =new GUI::EditTextWidget(_tabs, 335, 75, 35, 20, _(Common::U32String::itoa(playerTime.getHours(), buf, 10)), _(""), kInputPlayerTime, kInputPlayerTime);
-    new GUI::StaticTextWidget(_tabs, 375, 75, 40, 20, _("Hours"), Graphics::kTextAlignLeft);
-    _playerTimeMinutes =new GUI::EditTextWidget(_tabs, 415, 75, 35, 20, _(Common::U32String::itoa(playerTime.getMinutes(), buf, 10)), _(""), kInputPlayerTime, kInputPlayerTime);
-    new GUI::StaticTextWidget(_tabs, 455, 75, 50, 20, _("Minutes"), Graphics::kTextAlignLeft);
-    _difficulty = new GUI::EditTextWidget(_tabs, 255, 105, 35, 20, _(Common::U32String::itoa(NancySceneState._difficulty, buf, 10)), _(""), kInputDifficulty, kInputDifficulty);
-    new GUI::StaticTextWidget(_tabs, 295, 105, 150, 20, _("Player Difficulty Level"), Graphics::kTextAlignLeft);
-
-    new GUI::StaticTextWidget(_tabs, 250, 140, 150, 20, _("Player Data"), Graphics::kTextAlignLeft);
-    _timerOn = new GUI::CheckboxWidget(_tabs, 255, 170, 150, 20, _("Timer On"), _(""));
-    _timerOn->setState(timerIsActive);
-    _timerHours = new GUI::EditTextWidget(_tabs, 255, 195, 35, 20, _(Common::U32String::itoa(timerTime.getTotalHours(), buf, 10)), _(""), kInputTimer, kInputTimer);
-    new GUI::StaticTextWidget(_tabs, 295, 195, 40, 20, _("Hours"), Graphics::kTextAlignLeft);
-    _timerMinutes = new GUI::EditTextWidget(_tabs, 335, 195, 35, 20, _(Common::U32String::itoa(timerTime.getMinutes(), buf, 10)), _(""), kInputTimer, kInputTimer);
-    new GUI::StaticTextWidget(_tabs, 375, 195, 50, 20, _("Minutes"), Graphics::kTextAlignLeft);
-    _timerSeconds = new GUI::EditTextWidget(_tabs, 425, 195, 35, 20, _(Common::U32String::itoa(timerTime.getSeconds(), buf, 10)), _(""), kInputTimer, kInputTimer);
-    new GUI::StaticTextWidget(_tabs, 465, 195, 50, 20, _("Seconds"), Graphics::kTextAlignLeft);
-
-    _tabs->addTab(_("Inventory"), _("Cheat.Inventory"));
-
-    for (uint i = 0; i < dialogIDs.size(); ++i) {
-        Common::SeekableReadStream *resStream = res->getResource(Common::kWinDialog, dialogIDs[i].getID());
-        
-        Common::String idString;
-        resStream->skip(0x16);
-        while (true) {
-            char add = resStream->readByte();
-            if (add != 0) {
-                idString += add;
-                resStream->skip(1);
-            } else {
-                resStream->skip(1);
-                break;
-            }
-        }
-
-        if (!idString.hasPrefix("Inventory")) {
-            continue;
-        }
-
-        idString.trim();
-        uint numItems = 0;
-        
-        while (resStream->pos() < resStream->size()) {
-            if (resStream->readUint16LE() == 0xFFFF && resStream->readSint16LE() == 0x80) {
-                // Found a resource, read its string id
-                Common::String itemLabel;
-
-                while (true) {
-                    char add = resStream->readByte();
-                    if (add != 0) {
-                        itemLabel += add;
-                        resStream->skip(1);
-                    } else {
-                        resStream->skip(1);
-                        break;
-                    }
-                }
-                GUI::CheckboxWidget *box = new GUI::CheckboxWidget(_tabs, 250 * (numItems / 10) + 20, (350 / 10) * (numItems % 10) + 15, 250, 250/10, _(itemLabel), Common::U32String());
-                box->setState(NancySceneState.hasItem(numItems) == kTrue);
-                _inventory.push_back(box);
-
-                ++numItems;
-            }
-        }
-
-        break;
-    }
-
-    _tabs->setActiveTab(0);
+	_backgroundType = GUI::ThemeEngine::kDialogBackgroundSpecial;
+	Common::WinResources *res = Common::WinResources::createFromEXE("game.exe");
+	Common::Array<Common::WinResourceID> dialogIDs = res->getIDList(Common::kWinDialog);
+	State::SceneInfo scene = NancySceneState.getSceneInfo();
+	Time playerTime = NancySceneState._timers.playerTime;
+	Time timerTime = NancySceneState._timers.timerTime;
+	bool timerIsActive = NancySceneState._timers.timerIsActive;
+	if (!timerIsActive) {
+		timerTime = 0;
+	}
+	char buf[4];
+
+	GUI::TabWidget *_tabs = new GUI::TabWidget(this, 0, 0, 600, 370);
+	new GUI::ButtonWidget(this, 420, 410, 60, 20, _("Cancel"), Common::U32String(), GUI::kCloseCmd);
+	new GUI::ButtonWidget(this, 520, 410, 60, 20, _("Ok"), Common::U32String(), GUI::kOKCmd);
+
+	_tabs->addTab(_("General"), _("Cheat.General"));
+
+	new GUI::StaticTextWidget(_tabs, 30, 20, 150, 20, _("Scene Data"), Graphics::kTextAlignLeft);
+	_restartScene = new GUI::CheckboxWidget(_tabs, 35, 50, 150, 20, _("Restart the Scene"), _(""));
+	_scene = new GUI::EditTextWidget(_tabs, 35, 75, 45, 20, _(Common::U32String::itoa(scene.sceneID, buf, 10)), _(""), kInputSceneNr, kInputSceneNr);
+	new GUI::StaticTextWidget(_tabs, 85, 75, 150, 20, _("Scene Number"), Graphics::kTextAlignLeft);
+	_frame = new GUI::EditTextWidget(_tabs, 35, 100, 45, 20, _(Common::U32String::itoa(scene.frameID, buf, 10)), _(""), kInputFrameNr, kInputFrameNr);
+	new GUI::StaticTextWidget(_tabs, 85, 100, 150, 20, _("Frame Number"), Graphics::kTextAlignLeft);
+	_offset = new GUI::EditTextWidget(_tabs, 35, 125, 45, 20, _(Common::U32String::itoa(scene.verticalOffset, buf, 10)), _(""), kInputScroll, kInputScroll);
+	new GUI::StaticTextWidget(_tabs, 85, 125, 150, 20, _("Background Top (Y)"), Graphics::kTextAlignLeft);
+
+	new GUI::StaticTextWidget(_tabs, 30, 160, 150, 20, _("Hints Remaining"), Graphics::kTextAlignLeft);
+	new GUI::StaticTextWidget(_tabs, 35, 185, 45, 20, _("Easy"), Graphics::kTextAlignLeft);
+	_hintsRemainingEasy = new GUI::EditTextWidget(_tabs, 35, 205, 45, 20, _(Common::U32String::itoa(NancySceneState._hintsRemaining[0], buf, 10)), _(""), kInputHintsEasy, kInputHintsEasy);
+	new GUI::StaticTextWidget(_tabs, 85, 185, 45, 20, _("Medium"), Graphics::kTextAlignLeft);
+	_hintsRemainingMedium = new GUI::EditTextWidget(_tabs, 85, 205, 45, 20, _(Common::U32String::itoa(NancySceneState._hintsRemaining[1], buf, 10)), _(""), kInputHintsMedium, kInputHintsMedium);
+	new GUI::StaticTextWidget(_tabs, 135, 185, 45, 20, _("Hard"), Graphics::kTextAlignLeft);
+	_hintsRemainingHard = new GUI::EditTextWidget(_tabs, 135, 205, 45, 20, _(Common::U32String::itoa(NancySceneState._hintsRemaining[2], buf, 10)), _(""), kInputHintsHard, kInputHintsHard);
+
+	new GUI::StaticTextWidget(_tabs, 250, 20, 150, 20, _("Player Data"), Graphics::kTextAlignLeft);
+	new GUI::StaticTextWidget(_tabs, 255, 50, 150, 20, _("Player Time:"), Graphics::kTextAlignLeft);
+	_playerTimeDays = new GUI::EditTextWidget(_tabs, 255, 75, 35, 20, _(Common::U32String::itoa(playerTime.getDays(), buf, 10)), _(""), kInputPlayerTime, kInputPlayerTime);
+	new GUI::StaticTextWidget(_tabs, 295, 75, 40, 20, _("Days"), Graphics::kTextAlignLeft);
+	_playerTimeHours =new GUI::EditTextWidget(_tabs, 335, 75, 35, 20, _(Common::U32String::itoa(playerTime.getHours(), buf, 10)), _(""), kInputPlayerTime, kInputPlayerTime);
+	new GUI::StaticTextWidget(_tabs, 375, 75, 40, 20, _("Hours"), Graphics::kTextAlignLeft);
+	_playerTimeMinutes =new GUI::EditTextWidget(_tabs, 415, 75, 35, 20, _(Common::U32String::itoa(playerTime.getMinutes(), buf, 10)), _(""), kInputPlayerTime, kInputPlayerTime);
+	new GUI::StaticTextWidget(_tabs, 455, 75, 50, 20, _("Minutes"), Graphics::kTextAlignLeft);
+	_difficulty = new GUI::EditTextWidget(_tabs, 255, 105, 35, 20, _(Common::U32String::itoa(NancySceneState._difficulty, buf, 10)), _(""), kInputDifficulty, kInputDifficulty);
+	new GUI::StaticTextWidget(_tabs, 295, 105, 150, 20, _("Player Difficulty Level"), Graphics::kTextAlignLeft);
+
+	new GUI::StaticTextWidget(_tabs, 250, 140, 150, 20, _("Player Data"), Graphics::kTextAlignLeft);
+	_timerOn = new GUI::CheckboxWidget(_tabs, 255, 170, 150, 20, _("Timer On"), _(""));
+	_timerOn->setState(timerIsActive);
+	_timerHours = new GUI::EditTextWidget(_tabs, 255, 195, 35, 20, _(Common::U32String::itoa(timerTime.getTotalHours(), buf, 10)), _(""), kInputTimer, kInputTimer);
+	new GUI::StaticTextWidget(_tabs, 295, 195, 40, 20, _("Hours"), Graphics::kTextAlignLeft);
+	_timerMinutes = new GUI::EditTextWidget(_tabs, 335, 195, 35, 20, _(Common::U32String::itoa(timerTime.getMinutes(), buf, 10)), _(""), kInputTimer, kInputTimer);
+	new GUI::StaticTextWidget(_tabs, 375, 195, 50, 20, _("Minutes"), Graphics::kTextAlignLeft);
+	_timerSeconds = new GUI::EditTextWidget(_tabs, 425, 195, 35, 20, _(Common::U32String::itoa(timerTime.getSeconds(), buf, 10)), _(""), kInputTimer, kInputTimer);
+	new GUI::StaticTextWidget(_tabs, 465, 195, 50, 20, _("Seconds"), Graphics::kTextAlignLeft);
+
+	_tabs->addTab(_("Inventory"), _("Cheat.Inventory"));
+
+	for (uint i = 0; i < dialogIDs.size(); ++i) {
+		Common::SeekableReadStream *resStream = res->getResource(Common::kWinDialog, dialogIDs[i].getID());
+
+		Common::String idString;
+		resStream->skip(0x16);
+		while (true) {
+			char add = resStream->readByte();
+			if (add != 0) {
+				idString += add;
+				resStream->skip(1);
+			} else {
+				resStream->skip(1);
+				break;
+			}
+		}
+
+		if (!idString.hasPrefix("Inventory")) {
+			continue;
+		}
+
+		idString.trim();
+		uint numItems = 0;
+
+		while (resStream->pos() < resStream->size()) {
+			if (resStream->readUint16LE() == 0xFFFF && resStream->readSint16LE() == 0x80) {
+				// Found a resource, read its string id
+				Common::String itemLabel;
+
+				while (true) {
+					char add = resStream->readByte();
+					if (add != 0) {
+						itemLabel += add;
+						resStream->skip(1);
+					} else {
+						resStream->skip(1);
+						break;
+					}
+				}
+				GUI::CheckboxWidget *box = new GUI::CheckboxWidget(_tabs, 250 * (numItems / 10) + 20, (350 / 10) * (numItems % 10) + 15, 250, 250/10, _(itemLabel), Common::U32String());
+				box->setState(NancySceneState.hasItem(numItems) == kTrue);
+				_inventory.push_back(box);
+
+				++numItems;
+			}
+		}
+
+		break;
+	}
+
+	_tabs->setActiveTab(0);
 }
 
 void CheatDialog::handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) {
-    switch (cmd) {
-    case GUI::kOKCmd: {
-        if (_restartScene->getState()) {
-            uint sceneID = atoi(Common::String(_scene->getEditString()).c_str());
-            IFF iff(Common::String::format("S%u", sceneID));
-            if (iff.load()) {
-                NancySceneState.changeScene(
-                    atoi(Common::String(_scene->getEditString()).c_str()),
-                    atoi(Common::String(_frame->getEditString()).c_str()),
-                    atoi(Common::String(_offset->getEditString()).c_str()),
-                    true);
-            } else {
-                new GUI::StaticTextWidget(this, 20, 410, 150, 20, _("Invalid Scene ID!"), Graphics::kTextAlignLeft);


Commit: 36883885f0180baae25b54a908aadb8e9348e34b
    https://github.com/scummvm/scummvm/commit/36883885f0180baae25b54a908aadb8e9348e34b
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Move ActionRecord::finishExecution() outside header file

Added an actionrecord.cpp file and moved the definition of ActionRecord::finishExecution() inside it.

Changed paths:
  A engines/nancy/action/actionrecord.cpp
    engines/nancy/action/actionrecord.h
    engines/nancy/module.mk


diff --git a/engines/nancy/action/actionrecord.cpp b/engines/nancy/action/actionrecord.cpp
new file mode 100644
index 0000000000..3e80110bbf
--- /dev/null
+++ b/engines/nancy/action/actionrecord.cpp
@@ -0,0 +1,51 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/nancy/action/actionmanager.h"
+
+namespace Nancy {
+namespace Action {
+
+void ActionRecord::finishExecution() {
+	switch (_execType) {
+	case kOneShot:
+		_isDone = true;
+		_state = kBegin;
+		break;
+	case kRepeating:
+		_isDone = false;
+		_isActive = false;
+		_state = kBegin;
+
+		for (uint i = 0; i < _dependencies.size(); ++i) {
+			_dependencies[i].satisfied = false;
+		}
+
+		break;
+	default:
+		_state = kBegin;
+		break;
+	}
+}
+
+} // End of namespace Action
+} // End of namespace Nancy
diff --git a/engines/nancy/action/actionrecord.h b/engines/nancy/action/actionrecord.h
index fbaec75919..458227e2af 100644
--- a/engines/nancy/action/actionrecord.h
+++ b/engines/nancy/action/actionrecord.h
@@ -101,27 +101,7 @@ public:
 	virtual void handleInput(NancyInput &input) {}
 
 protected:
-	void finishExecution() {
-		switch (_execType) {
-		case kOneShot:
-			_isDone = true;
-			_state = kBegin;
-			break;
-		case kRepeating:
-			_isDone = false;
-			_isActive = false;
-			_state = kBegin;
-
-			for (uint i = 0; i < _dependencies.size(); ++i) {
-				_dependencies[i].satisfied = false;
-			}
-
-			break;
-		default:
-			_state = kBegin;
-			break;
-		}
-	}
+	void finishExecution();
 
 	// Used for debugging
 	virtual Common::String getRecordTypeName() const =0;
diff --git a/engines/nancy/module.mk b/engines/nancy/module.mk
index 6a750e6dad..9ff66844c7 100644
--- a/engines/nancy/module.mk
+++ b/engines/nancy/module.mk
@@ -2,6 +2,7 @@ MODULE := engines/nancy
 
 MODULE_OBJS = \
   action/actionmanager.o \
+  action/actionrecord.o \
   action/arfactory.o \
   action/leverpuzzle.o \
   action/orderingpuzzle.o \


Commit: 8d774ba1e36a92b2ab8b0021681b80387104bb1f
    https://github.com/scummvm/scummvm/commit/8d774ba1e36a92b2ab8b0021681b80387104bb1f
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add POTFILES and mark translatable strings

Added a POTFILES that was previously missing. Marked the ringing string in telephone.cpp and the keymapper descriptions in input.cpp as translatable.

Changed paths:
  A engines/nancy/POTFILES
    engines/nancy/action/telephone.cpp
    engines/nancy/input.cpp


diff --git a/engines/nancy/POTFILES b/engines/nancy/POTFILES
new file mode 100644
index 0000000000..67ccfca338
--- /dev/null
+++ b/engines/nancy/POTFILES
@@ -0,0 +1,3 @@
+engines/nancy/action/telephone.cpp
+engines/nancy/cheat.cpp
+engines/nancy/input.cpp
diff --git a/engines/nancy/action/telephone.cpp b/engines/nancy/action/telephone.cpp
index e587e77317..95ec0781a6 100644
--- a/engines/nancy/action/telephone.cpp
+++ b/engines/nancy/action/telephone.cpp
@@ -33,6 +33,8 @@
 
 #include "engines/nancy/ui/textbox.h"
 
+#include "common/translation.h"
+
 namespace Nancy {
 namespace Action {
 
@@ -134,7 +136,7 @@ void Telephone::execute() {
 			// Long phone numbers start with 1
 			if (_calledNumber.size() >= 11 || (_calledNumber.size() >= 7 && (_calledNumber[0] != 1))) {
 				NancySceneState.getTextbox().clear();
-				NancySceneState.getTextbox().addTextLine("ringing...<n><e>"); // Hardcoded in the original engine
+				NancySceneState.getTextbox().addTextLine(_("ringing...<n><e>")); // Hardcoded in the original engine
 				g_nancy->_sound->loadSound(_ringSound);
 				g_nancy->_sound->playSound(_ringSound);
 				_callState = kRinging;
diff --git a/engines/nancy/input.cpp b/engines/nancy/input.cpp
index dbb1f6f9be..fa648f30dc 100644
--- a/engines/nancy/input.cpp
+++ b/engines/nancy/input.cpp
@@ -24,6 +24,8 @@
 
 #include "engines/nancy/nancy.h"
 
+#include "common/translation.h"
+
 #include "backends/keymapper/action.h"
 #include "backends/keymapper/keymap.h"
 #include "backends/keymapper/standard-actions.h"
@@ -165,45 +167,45 @@ void InputManager::initKeymaps(Common::KeymapArray &keymaps) {
 	Keymap *debugKeymap = new Keymap(Keymap::kKeymapTypeGame, "nancy-debug", "Nancy Drew - Debug/Cheat Shortcuts");
 	Action *act;
 
-	act = new Action(kStandardActionInteract, U32String("Left Click Interact"));
+	act = new Action(kStandardActionInteract, _("Left Click Interact"));
 	act->setLeftClickEvent();
 	act->setCustomEngineActionEvent(kNancyActionLeftClick);
 	act->addDefaultInputMapping("MOUSE_LEFT");
 	act->addDefaultInputMapping("JOY_A");
 	mainKeymap->addAction(act);
 
-	act = new Action("RCLK", U32String("Right Click Interact"));
+	act = new Action("RCLK", _("Right Click Interact"));
 	act->setRightClickEvent();
 	act->setCustomEngineActionEvent(kNancyActionRightClick);
 	act->addDefaultInputMapping("MOUSE_RIGHT");
 	act->addDefaultInputMapping("JOY_B");
 	mainKeymap->addAction(act);
 
-	act = new Action(kStandardActionMoveUp, U32String("Move up"));
+	act = new Action(kStandardActionMoveUp, _("Move up"));
 	act->setCustomEngineActionEvent(kNancyActionMoveUp);
 	act->addDefaultInputMapping("UP");
 	act->addDefaultInputMapping("JOY_UP");
 	mainKeymap->addAction(act);
 
-	act = new Action(kStandardActionMoveDown, U32String("Move down"));
+	act = new Action(kStandardActionMoveDown, _("Move down"));
 	act->setCustomEngineActionEvent(kNancyActionMoveDown);
 	act->addDefaultInputMapping("DOWN");
 	act->addDefaultInputMapping("JOY_DOWN");
 	mainKeymap->addAction(act);
 
-	act = new Action(kStandardActionMoveLeft, U32String("Move left"));
+	act = new Action(kStandardActionMoveLeft, _("Move left"));
 	act->setCustomEngineActionEvent(kNancyActionMoveLeft);
 	act->addDefaultInputMapping("LEFT");
 	act->addDefaultInputMapping("JOY_LEFT");
 	mainKeymap->addAction(act);
 
-	act = new Action(kStandardActionMoveRight, U32String("Move right"));
+	act = new Action(kStandardActionMoveRight, _("Move right"));
 	act->setCustomEngineActionEvent(kNancyActionMoveRight);
 	act->addDefaultInputMapping("RIGHT");
 	act->addDefaultInputMapping("JOY_RIGHT");
 	mainKeymap->addAction(act);
 
-	act = new Action("FASTM", U32String("Fast move modifier"));
+	act = new Action("FASTM", _("Fast move modifier"));
 	act->setCustomEngineActionEvent(kNancyActionMoveFast);
 	act->addDefaultInputMapping("LCTRL");
 	act->addDefaultInputMapping("JOY_LEFT_SHOULDER");
@@ -211,53 +213,53 @@ void InputManager::initKeymaps(Common::KeymapArray &keymaps) {
 
 	// Debug shortcuts
 
-	act = new Action("FASTC", U32String("Toggle fast conversation mode"));
+	act = new Action("FASTC", _("Toggle fast conversation mode"));
 	act->setCustomEngineActionEvent(kNancyActionFastConvoToggle);
 	act->addDefaultInputMapping("C+S+TAB+f");
 	debugKeymap->addAction(act);
 
-	act = new Action("ENDC", U32String("Toggle end conversation mode"));
+	act = new Action("ENDC", _("Toggle end conversation mode"));
 	act->setCustomEngineActionEvent(kNancyActionEndConvoToggle);
 	act->addDefaultInputMapping("C+S+TAB+e");
 	debugKeymap->addAction(act);
 
-	act = new Action("MMENU", U32String("Go to main menu"));
+	act = new Action("MMENU", _("Go to main menu"));
 	act->setCustomEngineActionEvent(kNancyActionRequestMainMenu);
 	act->addDefaultInputMapping("C+S+TAB+F2");
 	debugKeymap->addAction(act);
 
-	act = new Action("LDSV", U32String("Go to save/load menu"));
+	act = new Action("LDSV", _("Go to save/load menu"));
 	act->setCustomEngineActionEvent(kNancyActionRequestSaveLoad);
 	act->addDefaultInputMapping("C+S+TAB+F3");
 	debugKeymap->addAction(act);
 
-	act = new Action("RLDSV", U32String("Reload last save"));
+	act = new Action("RLDSV", _("Reload last save"));
 	act->setCustomEngineActionEvent(kNancyActionReloadSave);
 	act->addDefaultInputMapping("C+S+TAB+F4");
 	debugKeymap->addAction(act);
 
-	act = new Action("SETUP", U32String("Go to setup menu"));
+	act = new Action("SETUP", _("Go to setup menu"));
 	act->setCustomEngineActionEvent(kNancyActionRequestSetupMenu);
 	act->addDefaultInputMapping("C+S+TAB+F6");
 	debugKeymap->addAction(act);
 
-	act = new Action("CRED", U32String("Show credits"));
+	act = new Action("CRED", _("Show credits"));
 	act->setCustomEngineActionEvent(kNancyActionRequestCredits);
 	act->addDefaultInputMapping("C+S+TAB+F7");
 	debugKeymap->addAction(act);
 
-	act = new Action("MAP", U32String("Go to map screen"));
+	act = new Action("MAP", _("Go to map screen"));
 	act->setCustomEngineActionEvent(kNancyActionRequestMap);
 	act->addDefaultInputMapping("C+S+TAB+F8");
 	act->addDefaultInputMapping("C+S+TAB+m");
 	debugKeymap->addAction(act);
 
-	act = new Action("CHEAT", U32String("Open general cheat menu"));
+	act = new Action("CHEAT", _("Open general cheat menu"));
 	act->setCustomEngineActionEvent(kNancyActionRequestCheatMenu);
 	act->addDefaultInputMapping("C+S+TAB+c");
 	debugKeymap->addAction(act);
 
-	act = new Action("EVENT", U32String("Open event flags cheat menu"));
+	act = new Action("EVENT", _("Open event flags cheat menu"));
 	act->setCustomEngineActionEvent(kNancyActionRequestEventMenu);
 	act->addDefaultInputMapping("C+S+TAB+v");
 	debugKeymap->addAction(act);


Commit: af28b38f4beeb772a37e66a222ad6bd37dbddb2a
    https://github.com/scummvm/scummvm/commit/af28b38f4beeb772a37e66a222ad6bd37dbddb2a
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Properly read early video format frame IDs

Chunk format 1 video (found in The Vampire Diaries) contains frame IDs as well as frame names. Those were previously skipped over, but are now getting read correctly.

Changed paths:
    engines/nancy/video.cpp


diff --git a/engines/nancy/video.cpp b/engines/nancy/video.cpp
index 528786fbf8..f4625415fa 100644
--- a/engines/nancy/video.cpp
+++ b/engines/nancy/video.cpp
@@ -118,9 +118,11 @@ AVFDecoder::AVFVideoTrack::AVFVideoTrack(Common::SeekableReadStream *stream, uin
 		ChunkInfo info;
 
 		if (formatHi == 1) {
-			char buf[13];
-			stream->read(buf, 13);
+			char buf[9];
+			stream->read(buf, 9);
+			buf[8] = '\0';
 			info.name = buf;
+			info.index = stream->readUint32LE();
 
 			stream->skip(4); // unknown
 


Commit: 646dfeb035107a064444150cab11cbebf5392711
    https://github.com/scummvm/scummvm/commit/646dfeb035107a064444150cab11cbebf5392711
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Use macros in detection table

The detection table now uses the AD_ENTRY1s macro where it is applicable.

Changed paths:
    engines/nancy/detection.cpp


diff --git a/engines/nancy/detection.cpp b/engines/nancy/detection.cpp
index 033fded9ce..66e5ad3f88 100644
--- a/engines/nancy/detection.cpp
+++ b/engines/nancy/detection.cpp
@@ -54,10 +54,7 @@ static const Nancy::NancyGameDescription gameDescriptions[] = {
 	{ // MD5 by fracturehill
 		{
 			"vampirediaries", 0,
-			{
-				{"vampire.exe", 0, "c6207f4bb7418b8a067ad75ed9f57bdf", 114688},
-				AD_LISTEND
-			},
+			AD_ENTRY1s("vampire.exe", "c6207f4bb7418b8a067ad75ed9f57bdf", 114688),
 			Common::EN_ANY,
 			Common::kPlatformWindows,
 			Nancy::NGF_8BITCOLOR,
@@ -68,10 +65,7 @@ static const Nancy::NancyGameDescription gameDescriptions[] = {
 	{ // MD5 by waltervn
 		{
 			"nancy1", 0,
-			{
-				{"ciftree.dat", 0, "9f89e0b53717515ae0eb82d14ffe0e88", 4317962},
-				AD_LISTEND
-			},
+			AD_ENTRY1s("ciftree.dat", "9f89e0b53717515ae0eb82d14ffe0e88", 4317962),
 			Common::EN_ANY,
 			Common::kPlatformWindows,
 			ADGF_NO_FLAGS,
@@ -82,10 +76,7 @@ static const Nancy::NancyGameDescription gameDescriptions[] = {
 	{ // MD5 by waltervn
 		{
 			"nancy2", 0,
-			{
-				{"ciftree.dat", 0, "fa4293d728a1b31407961cd82e86a015", 7784516},
-				AD_LISTEND
-			},
+			AD_ENTRY1s("ciftree.dat", "fa4293d728a1b31407961cd82e86a015", 7784516),
 			Common::EN_ANY,
 			Common::kPlatformWindows,
 			ADGF_NO_FLAGS,
@@ -96,10 +87,7 @@ static const Nancy::NancyGameDescription gameDescriptions[] = {
 	{ // MD5 by waltervn
 		{
 			"nancy3", 0,
-			{
-				{"ciftree.dat", 0, "ee5f8832226567c3610556497c451b09", 16256355},
-				AD_LISTEND
-			},
+			AD_ENTRY1s("ciftree.dat", "ee5f8832226567c3610556497c451b09", 16256355),
 			Common::EN_ANY,
 			Common::kPlatformWindows,
 			ADGF_NO_FLAGS,
@@ -126,10 +114,7 @@ static const Nancy::NancyGameDescription gameDescriptions[] = {
 	{ // MD5 by waltervn
 		{
 			"nancy4", 0,
-			{
-				{"ciftree.dat", 0, "e9d45f7db453b0d8f37d202fc979537c", 8742289},
-				AD_LISTEND
-			},
+			AD_ENTRY1s("ciftree.dat", "e9d45f7db453b0d8f37d202fc979537c", 8742289),
 			Common::EN_ANY,
 			Common::kPlatformWindows,
 			ADGF_NO_FLAGS,
@@ -156,10 +141,7 @@ static const Nancy::NancyGameDescription gameDescriptions[] = {
 	{ // MD5 by waltervn
 		{
 			"nancy5", 0,
-			{
-				{"ciftree.dat", 0, "21fa81f322595c3100d8d58d100852d5", 8187692},
-				AD_LISTEND
-			},
+			AD_ENTRY1s("ciftree.dat", "21fa81f322595c3100d8d58d100852d5", 8187692),
 			Common::EN_ANY,
 			Common::kPlatformWindows,
 			ADGF_NO_FLAGS,
@@ -202,10 +184,7 @@ static const Nancy::NancyGameDescription gameDescriptions[] = {
 	{ // MD5 by Strangerke
 		{
 			"nancy6", 0,
-			{
-				{"ciftree.dat", 0, "a97b848651fdcf38f5cad7092d98e4a1", 28888006},
-				AD_LISTEND
-			},
+			AD_ENTRY1s("ciftree.dat", "a97b848651fdcf38f5cad7092d98e4a1", 28888006),
 			Common::EN_ANY,
 			Common::kPlatformWindows,
 			ADGF_NO_FLAGS,


Commit: bcd011eb79e56cc74d750931e2ef5be5771a145f
    https://github.com/scummvm/scummvm/commit/bcd011eb79e56cc74d750931e2ef5be5771a145f
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Reduce calls to getBasePtr() when resizing surface

Replaced most calls to getBasePtr() inside GraphicsManager::copyToManaged() with simpler pointer arithmetic to make it less costly when copying double size images.

Changed paths:
    engines/nancy/graphics.cpp


diff --git a/engines/nancy/graphics.cpp b/engines/nancy/graphics.cpp
index ecd6cfde30..988ca36183 100644
--- a/engines/nancy/graphics.cpp
+++ b/engines/nancy/graphics.cpp
@@ -171,30 +171,36 @@ void GraphicsManager::copyToManaged(const Graphics::Surface &src, Graphics::Mana
 					const byte *srcP = (const byte *)src.getBasePtr(x, y);
 					uint dstX = x * 2;
 					uint dstY = verticalFlip ? (src.h - y - 1) * 2 : src.h - y - 1;
-					*((byte *)dst.getBasePtr(dstX, dstY)) = *srcP;
-					*((byte *)dst.getBasePtr(dstX + 1, dstY)) = *srcP;
-					*((byte *)dst.getBasePtr(dstX, dstY + 1)) = *srcP;
-					*((byte *)dst.getBasePtr(dstX + 1, dstY + 1)) = *srcP;
+					byte *dstP = ((byte *)dst.getBasePtr(dstX, dstY));
+					*dstP = *srcP;
+					*(dstP + 1) = *srcP;
+					dstP += dst.w;
+					*dstP = *srcP;
+					*(dstP + 1) = *srcP;
 					break;
 				}
 				case 2: {
 					const uint16 *srcP = (const uint16 *)src.getBasePtr(x, y);
 					uint dstX = x * 2;
 					uint dstY = verticalFlip ? (src.h - y - 1) * 2 : src.h - y - 1;
-					*((uint16 *)dst.getBasePtr(dstX, dstY)) = *srcP;
-					*((uint16 *)dst.getBasePtr(dstX + 1, dstY)) = *srcP;
-					*((uint16 *)dst.getBasePtr(dstX, dstY + 1)) = *srcP;
-					*((uint16 *)dst.getBasePtr(dstX + 1, dstY + 1)) = *srcP;
+					uint16 *dstP = ((uint16 *)dst.getBasePtr(dstX, dstY));
+					*dstP = *srcP;
+					*(dstP + 1) = *srcP;
+					dstP += dst.w;
+					*dstP = *srcP;
+					*(dstP + 1) = *srcP;
 					break;
 				}
 				case 4: {
 					const uint32 *srcP = (const uint32 *)src.getBasePtr(x, y);
 					uint dstX = x * 2;
 					uint dstY = verticalFlip ? (src.h - y - 1) * 2 : src.h - y - 1;
-					*((uint32 *)dst.getBasePtr(dstX, dstY)) = *srcP;
-					*((uint32 *)dst.getBasePtr(dstX + 1, dstY)) = *srcP;
-					*((uint32 *)dst.getBasePtr(dstX, dstY + 1)) = *srcP;
-					*((uint32 *)dst.getBasePtr(dstX + 1, dstY + 1)) = *srcP;
+					uint32 *dstP = ((uint32 *)dst.getBasePtr(dstX, dstY));
+					*dstP = *srcP;
+					*(dstP + 1) = *srcP;
+					dstP += dst.w;
+					*dstP = *srcP;
+					*(dstP + 1) = *srcP;
 					break;
 				}
 				default:


Commit: a2efd6e86725884961494e70d8e8f9de2d064a51
    https://github.com/scummvm/scummvm/commit/a2efd6e86725884961494e70d8e8f9de2d064a51
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Formatting fixes

Added a missing newline at the end of telephone.cpp, removed an unnecessary newline in nancy.cpp, and corrected a couple of end of namespace comments.

Changed paths:
    engines/nancy/action/recordtypes.cpp
    engines/nancy/action/responses.cpp
    engines/nancy/action/telephone.cpp
    engines/nancy/nancy.cpp


diff --git a/engines/nancy/action/recordtypes.cpp b/engines/nancy/action/recordtypes.cpp
index 97e122617b..208301da5b 100644
--- a/engines/nancy/action/recordtypes.cpp
+++ b/engines/nancy/action/recordtypes.cpp
@@ -609,5 +609,5 @@ void HintSystem::getHint(uint hint, uint difficulty) {
 	_sceneChange.readData(file);
 }
 
-}
+} // End of namespace Action
 } // End of namespace Nancy
diff --git a/engines/nancy/action/responses.cpp b/engines/nancy/action/responses.cpp
index 5b25d304b2..c93de16a00 100644
--- a/engines/nancy/action/responses.cpp
+++ b/engines/nancy/action/responses.cpp
@@ -950,5 +950,5 @@ static const HintDesc nancy1Hints[] {
 	}
 };
 
-}// End of namespace Action
-} // End of namespace Nancy::Action
+} // End of namespace Action
+} // End of namespace Nancy
diff --git a/engines/nancy/action/telephone.cpp b/engines/nancy/action/telephone.cpp
index 95ec0781a6..c478936b2d 100644
--- a/engines/nancy/action/telephone.cpp
+++ b/engines/nancy/action/telephone.cpp
@@ -316,4 +316,4 @@ void Telephone::undrawButton(uint id) {
 }
 
 } // End of namespace Action
-} // End of namespace Nancy
\ No newline at end of file
+} // End of namespace Nancy
diff --git a/engines/nancy/nancy.cpp b/engines/nancy/nancy.cpp
index eb9aed954a..54ff708200 100644
--- a/engines/nancy/nancy.cpp
+++ b/engines/nancy/nancy.cpp
@@ -233,8 +233,7 @@ void NancyEngine::setMouseEnabled(bool enabled) {
 	_cursorManager->showCursor(enabled); _input->setMouseInputEnabled(enabled);
 }
 
-void NancyEngine::callCheatMenu(bool eventFlags)
-{
+void NancyEngine::callCheatMenu(bool eventFlags) {
 	setState(kCheat), _cheatTypeIsEventFlag = eventFlags;
 }
 


Commit: 86e656f709b4255baef47d6f13523122ef689f15
    https://github.com/scummvm/scummvm/commit/86e656f709b4255baef47d6f13523122ef689f15
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Remove static members of SliderPuzzle

Moved the static members of SliderPuzzle that were used to save the puzzle's state to a struct inside Scene. Removed the SliderPuzzle::synchronize() function and incorporated its logic into Scene::synchronize(). Also fixed a possible use of uninitialized variables inside the synchronization code.

Changed paths:
    engines/nancy/action/sliderpuzzle.cpp
    engines/nancy/action/sliderpuzzle.h
    engines/nancy/nancy.cpp
    engines/nancy/state/scene.cpp
    engines/nancy/state/scene.h


diff --git a/engines/nancy/action/sliderpuzzle.cpp b/engines/nancy/action/sliderpuzzle.cpp
index 2663436c5e..82f0f57d29 100644
--- a/engines/nancy/action/sliderpuzzle.cpp
+++ b/engines/nancy/action/sliderpuzzle.cpp
@@ -34,9 +34,6 @@
 namespace Nancy {
 namespace Action {
 
-Common::Array<Common::Array<int16>> SliderPuzzle::_playerTileOrder = Common::Array<Common::Array<int16>>();
-bool SliderPuzzle::_playerHasTriedPuzzle = false;
-
 void SliderPuzzle::init() {
 	_drawSurface.create(_screenPosition.width(), _screenPosition.height(), GraphicsManager::getInputPixelFormat());
 	_drawSurface.clear(GraphicsManager::getTransColor());
@@ -108,26 +105,26 @@ void SliderPuzzle::execute() {
 	case kBegin:
 		init();
 		registerGraphics();
-		if (!_playerHasTriedPuzzle) {
+		if (!NancySceneState._sliderPuzzleState.playerHasTriedPuzzle) {
 			Common::SeekableReadStream *spuz = g_nancy->getBootChunkStream("SPUZ");
-			_playerTileOrder.clear();
+			NancySceneState._sliderPuzzleState.playerTileOrder.clear();
 			spuz->seek(NancySceneState.getDifficulty() * 0x48);
 			for (uint y = 0; y < _height; ++y) {
-				_playerTileOrder.push_back(Common::Array<int16>());
+				NancySceneState._sliderPuzzleState.playerTileOrder.push_back(Common::Array<int16>());
 
 				for (uint x = 0; x < _width; ++x) {
-					_playerTileOrder.back().push_back(spuz->readSint16LE());
+					NancySceneState._sliderPuzzleState.playerTileOrder.back().push_back(spuz->readSint16LE());
 				}
 
 				spuz->skip((6 - _width) * 2);
 			}
 
-			_playerHasTriedPuzzle = true;
+			NancySceneState._sliderPuzzleState.playerHasTriedPuzzle = true;
 		}
 
 		for (uint y = 0; y < _height; ++y) {
 			for (uint x = 0; x < _width; ++x) {
-				drawTile(_playerTileOrder[y][x], x, y);
+				drawTile(NancySceneState._sliderPuzzleState.playerTileOrder[y][x], x, y);
 			}
 		}
 
@@ -139,7 +136,7 @@ void SliderPuzzle::execute() {
 		case kNotSolved:
 			for (uint y = 0; y < _height; ++y) {
 				for (uint x = 0; x < _width; ++x) {
-					if (_playerTileOrder[y][x] != _correctTileOrder[y][x]) {
+					if (NancySceneState._sliderPuzzleState.playerTileOrder[y][x] != _correctTileOrder[y][x]) {
 						return;
 					}
 				}
@@ -168,7 +165,7 @@ void SliderPuzzle::execute() {
 		case kWaitForSound:
 			NancySceneState.changeScene(_solveExitScene);
 			NancySceneState.setEventFlag(_flagOnSolve);
-			_playerHasTriedPuzzle = false;
+			NancySceneState._sliderPuzzleState.playerHasTriedPuzzle = false;
 			break;
 		}
 
@@ -198,7 +195,7 @@ void SliderPuzzle::handleInput(NancyInput &input) {
 	for (uint y = 0; y < _height; ++y) {
 		bool shouldBreak = false;
 		for (uint x = 0; x < _width; ++x) {
-			if (x > 0 && _playerTileOrder[y][x - 1] < 0) {
+			if (x > 0 && NancySceneState._sliderPuzzleState.playerTileOrder[y][x - 1] < 0) {
 				if (NancySceneState.getViewport().convertViewportToScreen(_destRects[y][x]).contains(input.mousePos)) {
 					currentTileX = x;
 					currentTileY = y;
@@ -206,7 +203,7 @@ void SliderPuzzle::handleInput(NancyInput &input) {
 					shouldBreak = true;
 					break;
 				}
-			} else if ((int)x < _width - 1 && _playerTileOrder[y][x + 1] < 0) {
+			} else if ((int)x < _width - 1 && NancySceneState._sliderPuzzleState.playerTileOrder[y][x + 1] < 0) {
 				if (NancySceneState.getViewport().convertViewportToScreen(_destRects[y][x]).contains(input.mousePos)) {
 					currentTileX = x;
 					currentTileY = y;
@@ -214,7 +211,7 @@ void SliderPuzzle::handleInput(NancyInput &input) {
 					shouldBreak = true;
 					break;
 				}
-			} else if (y > 0 && _playerTileOrder[y - 1][x] < 0) {
+			} else if (y > 0 && NancySceneState._sliderPuzzleState.playerTileOrder[y - 1][x] < 0) {
 				if (NancySceneState.getViewport().convertViewportToScreen(_destRects[y][x]).contains(input.mousePos)) {
 					currentTileX = x;
 					currentTileY = y;
@@ -222,7 +219,7 @@ void SliderPuzzle::handleInput(NancyInput &input) {
 					shouldBreak = true;
 					break;
 				}
-			} else if ((int)y < _height - 1 && _playerTileOrder[y + 1][x] < 0) {
+			} else if ((int)y < _height - 1 && NancySceneState._sliderPuzzleState.playerTileOrder[y + 1][x] < 0) {
 				if (NancySceneState.getViewport().convertViewportToScreen(_destRects[y][x]).contains(input.mousePos)) {
 					currentTileX = x;
 					currentTileY = y;
@@ -245,35 +242,35 @@ void SliderPuzzle::handleInput(NancyInput &input) {
 			g_nancy->_sound->playSound(_clickSound);
 			switch (direction) {
 			case kUp: {
-				uint curTileID = _playerTileOrder[currentTileY][currentTileX];
+				uint curTileID = NancySceneState._sliderPuzzleState.playerTileOrder[currentTileY][currentTileX];
 				drawTile(curTileID, currentTileX, currentTileY - 1);
 				undrawTile(currentTileX, currentTileY);
-				_playerTileOrder[currentTileY - 1][currentTileX] = curTileID;
-				_playerTileOrder[currentTileY][currentTileX] = -10;
+				NancySceneState._sliderPuzzleState.playerTileOrder[currentTileY - 1][currentTileX] = curTileID;
+				NancySceneState._sliderPuzzleState.playerTileOrder[currentTileY][currentTileX] = -10;
 				break;
 			}
 			case kDown: {
-				uint curTileID = _playerTileOrder[currentTileY][currentTileX];
+				uint curTileID = NancySceneState._sliderPuzzleState.playerTileOrder[currentTileY][currentTileX];
 				drawTile(curTileID, currentTileX, currentTileY + 1);
 				undrawTile(currentTileX, currentTileY);
-				_playerTileOrder[currentTileY + 1][currentTileX] = curTileID;
-				_playerTileOrder[currentTileY][currentTileX] = -10;
+				NancySceneState._sliderPuzzleState.playerTileOrder[currentTileY + 1][currentTileX] = curTileID;
+				NancySceneState._sliderPuzzleState.playerTileOrder[currentTileY][currentTileX] = -10;
 				break;
 			}
 			case kLeft: {
-				uint curTileID = _playerTileOrder[currentTileY][currentTileX];
+				uint curTileID = NancySceneState._sliderPuzzleState.playerTileOrder[currentTileY][currentTileX];
 				drawTile(curTileID, currentTileX - 1, currentTileY);
 				undrawTile(currentTileX, currentTileY);
-				_playerTileOrder[currentTileY][currentTileX - 1] = curTileID;
-				_playerTileOrder[currentTileY][currentTileX] = -10;
+				NancySceneState._sliderPuzzleState.playerTileOrder[currentTileY][currentTileX - 1] = curTileID;
+				NancySceneState._sliderPuzzleState.playerTileOrder[currentTileY][currentTileX] = -10;
 				break;
 			}
 			case kRight: {
-				uint curTileID = _playerTileOrder[currentTileY][currentTileX];
+				uint curTileID = NancySceneState._sliderPuzzleState.playerTileOrder[currentTileY][currentTileX];
 				drawTile(curTileID, currentTileX + 1, currentTileY);
 				undrawTile(currentTileX, currentTileY);
-				_playerTileOrder[currentTileY][currentTileX + 1] = curTileID;
-				_playerTileOrder[currentTileY][currentTileX] = -10;
+				NancySceneState._sliderPuzzleState.playerTileOrder[currentTileY][currentTileX + 1] = curTileID;
+				NancySceneState._sliderPuzzleState.playerTileOrder[currentTileY][currentTileX] = -10;
 				break;
 			}
 			}
@@ -287,29 +284,6 @@ void SliderPuzzle::onPause(bool pause) {
 	}
 }
 
-void SliderPuzzle::synchronize(Common::Serializer &ser) {
-	ser.syncAsByte(_playerHasTriedPuzzle);
-
-	byte x, y;
-
-	if (ser.isSaving()) {
-		y = _playerTileOrder.size();
-		if (y) {
-			x = _playerTileOrder.back().size();
-		}
-	}
-
-	ser.syncAsByte(x);
-	ser.syncAsByte(y);
-
-	_playerTileOrder.resize(y);
-
-	for (int i = 0; i < y; ++i) {
-		_playerTileOrder[i].resize(x);
-		ser.syncArray(_playerTileOrder[i].data(), x, Common::Serializer::Sint16LE);
-	}
-}
-
 void SliderPuzzle::drawTile(int tileID, uint posX, uint posY) {
 	if (tileID < 0) {
 		undrawTile(posX, posY);
diff --git a/engines/nancy/action/sliderpuzzle.h b/engines/nancy/action/sliderpuzzle.h
index 7d82d56046..b01338e2bf 100644
--- a/engines/nancy/action/sliderpuzzle.h
+++ b/engines/nancy/action/sliderpuzzle.h
@@ -54,8 +54,6 @@ public:
 	virtual void handleInput(NancyInput &input) override;
 	virtual void onPause(bool pause) override;
 
-	static void synchronize(Common::Serializer &ser);
-
 	Common::String _imageName; // 0x00
 	uint16 _width; // 0xA
 	uint16 _height; // 0xC
@@ -73,9 +71,6 @@ public:
 	SolveState _solveState = kNotSolved;
 	Graphics::ManagedSurface _image;
 
-	static Common::Array<Common::Array<int16>> _playerTileOrder;
-	static bool _playerHasTriedPuzzle;
-
 protected:
 	virtual Common::String getRecordTypeName() const override { return "SliderPuzzle"; }
 
diff --git a/engines/nancy/nancy.cpp b/engines/nancy/nancy.cpp
index 54ff708200..56cc71d82e 100644
--- a/engines/nancy/nancy.cpp
+++ b/engines/nancy/nancy.cpp
@@ -482,9 +482,6 @@ Common::Error NancyEngine::synchronize(Common::Serializer &ser) {
 	NancySceneState.synchronize(ser);
 	NancySceneState._actionManager.synchronize(ser);
 
-	// Sync any action record-related data
-	Action::SliderPuzzle::synchronize(ser);
-
 	return Common::kNoError;
 }
 
diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index edd13c15fb..de50f88ff3 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -339,6 +339,30 @@ void Scene::synchronize(Common::Serializer &ser) {
 	ser.syncAsUint16LE(_difficulty);
 	ser.syncArray<uint16>(_hintsRemaining.data(), _hintsRemaining.size(), Common::Serializer::Uint16LE);
 	ser.syncAsSint16LE(_lastHint);
+
+	// Synchronize SliderPuzzle static data
+	ser.syncAsByte(_sliderPuzzleState.playerHasTriedPuzzle);
+
+	byte x = 0, y = 0;
+
+	if (ser.isSaving()) {
+		y = _sliderPuzzleState.playerTileOrder.size();
+		if (y) {
+			x = _sliderPuzzleState.playerTileOrder.back().size();
+		} else {
+			x = 0;
+		}
+	}
+
+	ser.syncAsByte(x);
+	ser.syncAsByte(y);
+
+	_sliderPuzzleState.playerTileOrder.resize(y);
+
+	for (int i = 0; i < y; ++i) {
+		_sliderPuzzleState.playerTileOrder[i].resize(x);
+		ser.syncArray(_sliderPuzzleState.playerTileOrder[i].data(), x, Common::Serializer::Sint16LE);
+	}
 }
 
 void Scene::init() {
@@ -380,7 +404,7 @@ void Scene::init() {
 		_lastHint = -1;
 	}
 
-	Action::SliderPuzzle::_playerHasTriedPuzzle = false;
+	_sliderPuzzleState.playerHasTriedPuzzle = false;
 
 	initStaticData();
 
diff --git a/engines/nancy/state/scene.h b/engines/nancy/state/scene.h
index d741c4d8ce..5f18ef7c2c 100644
--- a/engines/nancy/state/scene.h
+++ b/engines/nancy/state/scene.h
@@ -56,6 +56,10 @@ namespace Nancy {
 
 class NancyEngine;
 
+namespace Action {
+class SliderPuzzle;
+}
+
 namespace State {
 
 struct SceneInfo {
@@ -68,6 +72,7 @@ struct SceneInfo {
 class Scene : public State, public Common::Singleton<Scene> {
 	friend class Nancy::Action::ActionRecord;
 	friend class Nancy::Action::ActionManager;
+	friend class Nancy::Action::SliderPuzzle;
 	friend class Nancy::NancyConsole;
 	friend class Nancy::NancyEngine;
 	friend class Nancy::CheatDialog;
@@ -224,6 +229,11 @@ private:
 		int16 primaryVideoResponsePicked = -1;
 	};
 
+	struct SliderPuzzleState {
+		Common::Array<Common::Array<int16>> playerTileOrder;
+		bool playerHasTriedPuzzle;
+	};
+
 	// UI
 	UI::FullScreenImage _frame;
 	UI::Viewport _viewport;
@@ -236,6 +246,7 @@ private:
 	SceneState _sceneState;
 	PlayFlags _flags;
 	Timers _timers;
+	SliderPuzzleState _sliderPuzzleState;
 	uint16 _difficulty;
 	Common::Array<uint16> _hintsRemaining;
 	int16 _lastHint;


Commit: 0c5b6a024423e1809f90fccfaa3f2df3f05f008f
    https://github.com/scummvm/scummvm/commit/0c5b6a024423e1809f90fccfaa3f2df3f05f008f
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Formatting fixes

Fixed a couple of else statements that were not following the formatting conventions and a double semicolon in recordtypes.cpp.

Changed paths:
    engines/nancy/action/recordtypes.cpp
    engines/nancy/metaengine.cpp
    engines/nancy/nancy.cpp


diff --git a/engines/nancy/action/recordtypes.cpp b/engines/nancy/action/recordtypes.cpp
index 208301da5b..e0ef70a261 100644
--- a/engines/nancy/action/recordtypes.cpp
+++ b/engines/nancy/action/recordtypes.cpp
@@ -199,7 +199,7 @@ void TextBoxWrite::readData(Common::SeekableReadStream &stream) {
 	stream.skip(size);
 
 	if (size > 10000) {
-		error("Action Record atTextboxWrite has too many text box chars: %d", size);;
+		error("Action Record atTextboxWrite has too many text box chars: %d", size);
 	}
 }
 
diff --git a/engines/nancy/metaengine.cpp b/engines/nancy/metaengine.cpp
index 19bcfa0a9a..5c8d1a9501 100644
--- a/engines/nancy/metaengine.cpp
+++ b/engines/nancy/metaengine.cpp
@@ -69,10 +69,12 @@ Common::Error NancyMetaEngine::createInstance(OSystem *syst, Engine **engine, co
 	if (gd) {
 		*engine = Nancy::NancyEngine::create(((const Nancy::NancyGameDescription *)gd)->gameType, syst, (const Nancy::NancyGameDescription *)gd);
 	}
+	
 	if (gd) {
 		return Common::kNoError;
+	} else {
+		return Common::Error();
 	}
-	else return Common::Error();
 }
 
 int NancyMetaEngine::getMaximumSaveSlot() const { return 8; }
diff --git a/engines/nancy/nancy.cpp b/engines/nancy/nancy.cpp
index 56cc71d82e..b958efabe1 100644
--- a/engines/nancy/nancy.cpp
+++ b/engines/nancy/nancy.cpp
@@ -376,8 +376,9 @@ bool NancyEngine::addBootChunk(const Common::String &name, Common::SeekableReadS
 Common::SeekableReadStream *NancyEngine::getBootChunkStream(const Common::String &name) {
 	if (_bootChunks.contains(name)) {
 		return _bootChunks[name];
+	} else {
+		return nullptr;
 	}
-	else return nullptr;
 }
 
 void NancyEngine::clearBootChunks() {


Commit: 99da486454c9be74f83da1f23b34abedd5952f47
    https://github.com/scummvm/scummvm/commit/99da486454c9be74f83da1f23b34abedd5952f47
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add dependencies to configure.engine

Added the 16bit and highres dependencies which were previously missing in configure.engine.

Changed paths:
    engines/nancy/configure.engine


diff --git a/engines/nancy/configure.engine b/engines/nancy/configure.engine
index 94cb58dde2..12922994e7 100644
--- a/engines/nancy/configure.engine
+++ b/engines/nancy/configure.engine
@@ -1,3 +1,3 @@
 # This file is included from the main "configure" script
 # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps]
-add_engine nancy "Nancy Drew" no "" "" "cxx11"
+add_engine nancy "Nancy Drew" no "" "" "16bit highres cxx11"


Commit: bfc6586f7841775545b347df02bca7ae7a296b57
    https://github.com/scummvm/scummvm/commit/bfc6586f7841775545b347df02bca7ae7a296b57
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add reserve() calls before initializing arrays

Added calls to reserve() before initializing most arrays in the engine to reduce unnecessary allocations when pushing back elements one by one.

Changed paths:
    engines/nancy/action/actionmanager.cpp
    engines/nancy/action/leverpuzzle.cpp
    engines/nancy/action/orderingpuzzle.cpp
    engines/nancy/action/primaryvideo.cpp
    engines/nancy/action/recordtypes.cpp
    engines/nancy/action/rotatinglockpuzzle.cpp
    engines/nancy/action/secondarymovie.cpp
    engines/nancy/action/secondaryvideo.cpp
    engines/nancy/action/sliderpuzzle.cpp
    engines/nancy/action/staticbitmapanim.cpp
    engines/nancy/action/telephone.cpp
    engines/nancy/cursor.cpp
    engines/nancy/font.cpp
    engines/nancy/iff.cpp
    engines/nancy/resource.cpp
    engines/nancy/state/map.cpp
    engines/nancy/state/scene.cpp
    engines/nancy/video.cpp


diff --git a/engines/nancy/action/actionmanager.cpp b/engines/nancy/action/actionmanager.cpp
index eb284aa86f..a4f19c6ccd 100644
--- a/engines/nancy/action/actionmanager.cpp
+++ b/engines/nancy/action/actionmanager.cpp
@@ -127,6 +127,7 @@ bool ActionManager::addNewActionRecord(Common::SeekableReadStream &inputData) {
 
 		// Initialize the dependencies data
 		inputData.seek(localChunkSize);
+		newRecord->_dependencies.reserve(numDependencies);
 		for (uint16 i = 0; i < numDependencies; ++i) {
 			newRecord->_dependencies.push_back(DependencyRecord());
 			DependencyRecord &dep = newRecord->_dependencies.back();
diff --git a/engines/nancy/action/leverpuzzle.cpp b/engines/nancy/action/leverpuzzle.cpp
index bb9b4562cf..7a7d5794ce 100644
--- a/engines/nancy/action/leverpuzzle.cpp
+++ b/engines/nancy/action/leverpuzzle.cpp
@@ -44,14 +44,17 @@ void LeverPuzzle::init() {
 void LeverPuzzle::readData(Common::SeekableReadStream &stream) {
 	readFilename(stream, _imageName);
 
+	_srcRects.reserve(3);	
 	for (uint leverID = 0; leverID < 3; ++leverID) {
 		_srcRects.push_back(Common::Array<Common::Rect>());
+		_srcRects.back().reserve(3);
 		for (uint i = 0; i < 4; ++i) {
 			_srcRects.back().push_back(Common::Rect());
 			readRect(stream, _srcRects.back().back());
 		}
 	}
 
+	_destRects.reserve(3);
 	for (uint leverID = 0; leverID < 3; ++leverID) {
 		_destRects.push_back(Common::Rect());
 		readRect(stream, _destRects.back());
@@ -63,11 +66,14 @@ void LeverPuzzle::readData(Common::SeekableReadStream &stream) {
 		}
 	}
 
+	_playerSequence.reserve(3);
+	_leverDirection.reserve(3);
 	for (uint leverID = 0; leverID < 3; ++leverID) {
 		_playerSequence.push_back(stream.readByte());
 		_leverDirection.push_back(true);
 	}
 
+	_correctSequence.reserve(3);
 	for (uint leverID = 0; leverID < 3; ++leverID) {
 		_correctSequence.push_back(stream.readByte());
 	}
diff --git a/engines/nancy/action/orderingpuzzle.cpp b/engines/nancy/action/orderingpuzzle.cpp
index 3480ae6e5b..e9be9770b2 100644
--- a/engines/nancy/action/orderingpuzzle.cpp
+++ b/engines/nancy/action/orderingpuzzle.cpp
@@ -56,6 +56,7 @@ void OrderingPuzzle::readData(Common::SeekableReadStream &stream) {
 	readFilename(stream, _imageName);
 	uint16 numElements = stream.readUint16LE();
 
+	_srcRects.reserve(numElements);
 	for (uint i = 0; i < numElements; ++i) {
 		_srcRects.push_back(Common::Rect());
 		readRect(stream, _srcRects.back());
@@ -63,6 +64,8 @@ void OrderingPuzzle::readData(Common::SeekableReadStream &stream) {
 
 	stream.skip(16 * (15 - numElements));
 
+	_destRects.reserve(numElements);
+	_drawnElements.reserve(numElements);
 	for (uint i = 0; i < numElements; ++i) {
 		_destRects.push_back(Common::Rect());
 		readRect(stream, _destRects.back());
@@ -80,6 +83,7 @@ void OrderingPuzzle::readData(Common::SeekableReadStream &stream) {
 
 	_sequenceLength = stream.readUint16LE();
 
+	_correctSequence.reserve(15);
 	for (uint i = 0; i < 15; ++i) {
 		_correctSequence.push_back(stream.readByte());
 	}
diff --git a/engines/nancy/action/primaryvideo.cpp b/engines/nancy/action/primaryvideo.cpp
index 2fc01cc406..e11d4a945a 100644
--- a/engines/nancy/action/primaryvideo.cpp
+++ b/engines/nancy/action/primaryvideo.cpp
@@ -78,6 +78,7 @@ void PlayPrimaryVideoChan0::ConditionFlag::set() const {
 void PlayPrimaryVideoChan0::ConditionFlags::read(Common::SeekableReadStream &stream) {
 	uint16 numFlags = stream.readUint16LE();
 
+	conditionFlags.reserve(numFlags);
 	for (uint i = 0; i < numFlags; ++i) {
 		conditionFlags.push_back(ConditionFlag());
 		conditionFlags.back().read(stream);
@@ -180,21 +181,20 @@ void PlayPrimaryVideoChan0::readData(Common::SeekableReadStream &stream) {
 	uint16 numResponses = stream.readUint16LE();
 	rawText = new char[400];
 
-	if (numResponses > 0) {
-		for (uint i = 0; i < numResponses; ++i) {
-			_responses.push_back(ResponseStruct());
-			ResponseStruct &response = _responses[i];
-			response.conditionFlags.read(stream);
-			stream.read(rawText, 400);
-			UI::Textbox::assembleTextLine(rawText, response.text, 400);
-			readFilename(stream, response.soundName);
-			stream.skip(1);
-			response._sceneChange.readData(stream);
-			response.flagDesc.label = stream.readSint16LE();
-			response.flagDesc.flag = (NancyFlag)stream.readByte();
-
-			stream.skip(0x32);
-		}
+	_responses.reserve(numResponses);
+	for (uint i = 0; i < numResponses; ++i) {
+		_responses.push_back(ResponseStruct());
+		ResponseStruct &response = _responses[i];
+		response.conditionFlags.read(stream);
+		stream.read(rawText, 400);
+		UI::Textbox::assembleTextLine(rawText, response.text, 400);
+		readFilename(stream, response.soundName);
+		stream.skip(1);
+		response._sceneChange.readData(stream);
+		response.flagDesc.label = stream.readSint16LE();
+		response.flagDesc.flag = (NancyFlag)stream.readByte();
+
+		stream.skip(0x32);
 	}
 
 	delete[] rawText;
@@ -205,15 +205,14 @@ void PlayPrimaryVideoChan0::readData(Common::SeekableReadStream &stream) {
 	}
 
 	uint16 numFlagsStructs = stream.readUint16LE();
-	if (numFlagsStructs > 0) {
-		for (uint16 i = 0; i < numFlagsStructs; ++i) {
-			_flagsStructs.push_back(FlagsStruct());
-			FlagsStruct &flagsStruct = _flagsStructs.back();
-			flagsStruct.conditions.read(stream);
-			flagsStruct.flagToSet.type = (ConditionFlag::ConditionType)stream.readByte();
-			flagsStruct.flagToSet.flag.label = stream.readSint16LE();
-			flagsStruct.flagToSet.flag.flag = (NancyFlag)stream.readByte();
-		}
+	_flagsStructs.reserve(numFlagsStructs);
+	for (uint16 i = 0; i < numFlagsStructs; ++i) {
+		_flagsStructs.push_back(FlagsStruct());
+		FlagsStruct &flagsStruct = _flagsStructs.back();
+		flagsStruct.conditions.read(stream);
+		flagsStruct.flagToSet.type = (ConditionFlag::ConditionType)stream.readByte();
+		flagsStruct.flagToSet.flag.label = stream.readSint16LE();
+		flagsStruct.flagToSet.flag.flag = (NancyFlag)stream.readByte();
 	}
 }
 
diff --git a/engines/nancy/action/recordtypes.cpp b/engines/nancy/action/recordtypes.cpp
index e0ef70a261..c63fbc4293 100644
--- a/engines/nancy/action/recordtypes.cpp
+++ b/engines/nancy/action/recordtypes.cpp
@@ -53,6 +53,7 @@ void HotMultiframeSceneChange::readData(Common::SeekableReadStream &stream) {
 	SceneChange::readData(stream);
 	uint16 numHotspots = stream.readUint16LE();
 
+	_hotspots.reserve(numHotspots);
 	for (uint i = 0; i < numHotspots; ++i) {
 		_hotspots.push_back(HotspotDescription());
 		HotspotDescription &newDesc = _hotspots[i];
@@ -152,6 +153,7 @@ void MapCallHot1Fr::execute() {
 
 void MapCallHotMultiframe::readData(Common::SeekableReadStream &stream) {
 	uint16 numDescs = stream.readUint16LE();
+	_hotspots.reserve(numDescs);
 	for (uint i = 0; i < numDescs; ++i) {
 		_hotspots.push_back(HotspotDescription());
 		_hotspots[i].readData(stream);
@@ -254,6 +256,7 @@ void EventFlagsMultiHS::readData(Common::SeekableReadStream &stream) {
 	EventFlags::readData(stream);
 	uint16 numHotspots = stream.readUint16LE();
 
+	_hotspots.reserve(numHotspots);
 	for (uint16 i = 0; i < numHotspots; ++i) {
 		_hotspots.push_back(HotspotDescription());
 		HotspotDescription &newDesc = _hotspots[i];
@@ -360,6 +363,7 @@ void ShowInventoryItem::readData(Common::SeekableReadStream &stream) {
 
 	uint16 numFrames = stream.readUint16LE();
 
+	_bitmaps.reserve(numFrames);
 	for (uint i = 0; i < numFrames; ++i) {
 		_bitmaps.push_back(BitmapDescription());
 		_bitmaps[i].readData(stream);
@@ -462,6 +466,7 @@ void PlaySoundMultiHS::readData(Common::SeekableReadStream &stream) {
 	stream.skip(2);
 	uint16 numHotspots = stream.readUint16LE();
 
+	_hotspots.reserve(numHotspots);
 	for (uint i = 0; i < numHotspots; ++i) {
 		_hotspots.push_back(HotspotDescription());
 		_hotspots.back().frameID = stream.readUint16LE();
diff --git a/engines/nancy/action/rotatinglockpuzzle.cpp b/engines/nancy/action/rotatinglockpuzzle.cpp
index faf090f70b..4e9f1d05bf 100644
--- a/engines/nancy/action/rotatinglockpuzzle.cpp
+++ b/engines/nancy/action/rotatinglockpuzzle.cpp
@@ -48,11 +48,13 @@ void RotatingLockPuzzle::readData(Common::SeekableReadStream &stream) {
 
 	uint numDials = stream.readUint16LE();
 
+	_srcRects.reserve(10);
 	for (uint i = 0; i < 10; ++i) {
 		_srcRects.push_back(Common::Rect());
 		readRect(stream, _srcRects.back());
 	}
 
+	_destRects.reserve(numDials);
 	for (uint i = 0; i < numDials; ++i) {
 		_destRects.push_back(Common::Rect());
 		readRect(stream, _destRects.back());
@@ -66,11 +68,13 @@ void RotatingLockPuzzle::readData(Common::SeekableReadStream &stream) {
 
 	stream.skip((8 - numDials) * 16);
 
+	_upHotspots.reserve(numDials);
 	for (uint i = 0; i < numDials; ++i) {
 		_upHotspots.push_back(Common::Rect());
 		readRect(stream, _upHotspots.back());
 	}
 
+	_downHotspots.reserve(numDials);
 	stream.skip((8 - numDials) * 16);
 
 	for (uint i = 0; i < numDials; ++i) {
@@ -80,6 +84,7 @@ void RotatingLockPuzzle::readData(Common::SeekableReadStream &stream) {
 
 	stream.skip((8 - numDials) * 16);
 
+	_correctSequence.reserve(numDials);
 	for (uint i = 0; i < numDials; ++i) {
 		_correctSequence.push_back(stream.readByte());
 	}
diff --git a/engines/nancy/action/secondarymovie.cpp b/engines/nancy/action/secondarymovie.cpp
index 223b57c2d3..ecfc65c977 100644
--- a/engines/nancy/action/secondarymovie.cpp
+++ b/engines/nancy/action/secondarymovie.cpp
@@ -59,6 +59,7 @@ void PlaySecondaryMovie::readData(Common::SeekableReadStream &stream) {
 	_sceneChange.readData(stream);
 
 	uint16 numVideoDescs = stream.readUint16LE();
+	_videoDescs.reserve(numVideoDescs);
 	for (uint i = 0; i < numVideoDescs; ++i) {
 		_videoDescs.push_back(SecondaryVideoDescription());
 		_videoDescs[i].readData(stream);
diff --git a/engines/nancy/action/secondaryvideo.cpp b/engines/nancy/action/secondaryvideo.cpp
index 77abaa80ac..318bd10ffc 100644
--- a/engines/nancy/action/secondaryvideo.cpp
+++ b/engines/nancy/action/secondaryvideo.cpp
@@ -170,6 +170,7 @@ void PlaySecondaryVideo::readData(Common::SeekableReadStream &stream) {
 	}
 
 	uint16 numVideoDescs = stream.readUint16LE();
+	_videoDescs.reserve(numVideoDescs);
 	for (uint i = 0; i < numVideoDescs; ++i) {
 		_videoDescs.push_back(SecondaryVideoDescription());
 		_videoDescs[i].readData(stream);
diff --git a/engines/nancy/action/sliderpuzzle.cpp b/engines/nancy/action/sliderpuzzle.cpp
index 82f0f57d29..d808a56e7b 100644
--- a/engines/nancy/action/sliderpuzzle.cpp
+++ b/engines/nancy/action/sliderpuzzle.cpp
@@ -49,19 +49,26 @@ void SliderPuzzle::readData(Common::SeekableReadStream &stream) {
 	_width = stream.readUint16LE();
 	_height = stream.readUint16LE();
 
+	_srcRects.reserve(_height);
 	for (uint y = 0; y < _height; ++y) {
 		_srcRects.push_back(Common::Array<Common::Rect>());
+		_srcRects.back().reserve(_width);
+		
 		for (uint x = 0; x < _width; ++x) {
 			_srcRects.back().push_back(Common::Rect());
 			readRect(stream, _srcRects.back().back());
 		}
+		
 		stream.skip((6 - _width) * 16);
 	}
 
 	stream.skip((6 - _height) * 6 * 16);
 
+	_destRects.reserve(_height);
 	for (uint y = 0; y < _height; ++y) {
 		_destRects.push_back(Common::Array<Common::Rect>());
+		_destRects.back().reserve(_width);
+
 		for (uint x = 0; x < _width; ++x) {
 			_destRects.back().push_back(Common::Rect());
 			readRect(stream, _destRects.back().back());
@@ -77,11 +84,15 @@ void SliderPuzzle::readData(Common::SeekableReadStream &stream) {
 
 	stream.skip((6 - _height) * 6 * 16);
 
+	_correctTileOrder.reserve(_height);
 	for (uint y = 0; y < _height; ++y) {
 		_correctTileOrder.push_back(Common::Array<int16>());
+		_correctTileOrder.back().reserve(_width);
+
 		for (uint x = 0; x < _width; ++x) {
 			_correctTileOrder.back().push_back(stream.readSint16LE());
 		}
+
 		stream.skip((6 - _width) * 2);
 	}
 
diff --git a/engines/nancy/action/staticbitmapanim.cpp b/engines/nancy/action/staticbitmapanim.cpp
index 318723649a..9abf7a698f 100644
--- a/engines/nancy/action/staticbitmapanim.cpp
+++ b/engines/nancy/action/staticbitmapanim.cpp
@@ -69,11 +69,13 @@ void PlayStaticBitmapAnimation::readData(Common::SeekableReadStream &stream) {
 	_sound.read(stream, SoundDescription::kNormal);
 	uint numViewportFrames = stream.readUint16LE();
 
+	_srcRects.reserve(_loopLastFrame - _firstFrame);
 	for (uint i = _firstFrame; i <= _loopLastFrame; ++i) {
 		_srcRects.push_back(Common::Rect());
 		readRect(stream, _srcRects[i]);
 	}
 
+	_bitmaps.reserve(numViewportFrames);
 	for (uint i = 0; i < numViewportFrames; ++i) {
 		_bitmaps.push_back(BitmapDescription());
 		BitmapDescription &rects = _bitmaps.back();
diff --git a/engines/nancy/action/telephone.cpp b/engines/nancy/action/telephone.cpp
index c478936b2d..50786f1f0d 100644
--- a/engines/nancy/action/telephone.cpp
+++ b/engines/nancy/action/telephone.cpp
@@ -52,11 +52,13 @@ void Telephone::init() {
 void Telephone::readData(Common::SeekableReadStream &stream) {
 	readFilename(stream, _imageName);
 
+	_srcRects.reserve(12);
 	for (uint i = 0; i < 12; ++i) {
 		_srcRects.push_back(Common::Rect());
 		readRect(stream, _srcRects.back());
 	}
 
+	_destRects.reserve(12);
 	for (uint i = 0; i < 12; ++i) {
 		_destRects.push_back(Common::Rect());
 		readRect(stream, _destRects.back());
@@ -75,6 +77,7 @@ void Telephone::readData(Common::SeekableReadStream &stream) {
 	_dialAgainSound.read(stream, SoundDescription::kNormal);
 	_hangUpSound.read(stream, SoundDescription::kNormal);
 
+	_buttonSoundNames.reserve(12);
 	for (uint i = 0; i < 12; ++i) {
 		Common::String buttonSoundName;
 		readFilename(stream, buttonSoundName);
@@ -100,10 +103,12 @@ void Telephone::readData(Common::SeekableReadStream &stream) {
 
 	uint numCalls = stream.readUint16LE();
 
+	_calls.reserve(numCalls);
 	for (uint i = 0; i < numCalls; ++i) {
 		_calls.push_back(PhoneCall());
 		PhoneCall &call = _calls.back();
 
+		call.phoneNumber.reserve(11);
 		for (uint j = 0; j < 11; ++j) {
 			call.phoneNumber.push_back(stream.readByte());
 		}
diff --git a/engines/nancy/cursor.cpp b/engines/nancy/cursor.cpp
index 901cc1ff9b..6094418585 100644
--- a/engines/nancy/cursor.cpp
+++ b/engines/nancy/cursor.cpp
@@ -38,6 +38,7 @@ void CursorManager::init() {
 	Common::String inventoryCursorsImageName = chunk->readString();
 
 	chunk = g_nancy->getBootChunkStream("CURS");
+	_cursors.reserve(56);
 	for (uint i = 0; i < 56; ++i) {
 		_cursors.push_back(Cursor());
 		chunk->seek(i * 16, SEEK_SET);
diff --git a/engines/nancy/font.cpp b/engines/nancy/font.cpp
index 9df4024361..dbe484a5d6 100644
--- a/engines/nancy/font.cpp
+++ b/engines/nancy/font.cpp
@@ -70,6 +70,7 @@ void Font::read(Common::SeekableReadStream &stream) {
 	_semicolonOffset = stream.readUint16LE();
 	_slashOffset = stream.readUint16LE();
 
+	_symbolRects.reserve(78);
 	for (uint i = 0; i < 78; ++i) {
 		_symbolRects.push_back(Common::Rect());
 		Common::Rect &cur = _symbolRects[i];
diff --git a/engines/nancy/iff.cpp b/engines/nancy/iff.cpp
index 099ca07d05..372a613f09 100644
--- a/engines/nancy/iff.cpp
+++ b/engines/nancy/iff.cpp
@@ -146,6 +146,7 @@ uint32 IFF::stringToId(const Common::String &s) {
 }
 
 void IFF::list(Common::Array<Common::String> &nameList) {
+	nameList.reserve(_chunks.size());
 	for (uint i = 0; i < _chunks.size(); ++i) {
 		nameList.push_back(idToString(_chunks[i].id));
 	}
diff --git a/engines/nancy/resource.cpp b/engines/nancy/resource.cpp
index 87fd5fa645..4988d96f57 100644
--- a/engines/nancy/resource.cpp
+++ b/engines/nancy/resource.cpp
@@ -233,6 +233,7 @@ bool CifTree::initialize() {
 	if (f.eos())
 		error("Error reading CifTree '%s'", _name.c_str());
 
+	_cifInfo.reserve(infoBlockCount);
 	for (int i = 0; i < infoBlockCount; i++) {
 		CifInfoChain chain;
 		readCifInfo(f, chain);
diff --git a/engines/nancy/state/map.cpp b/engines/nancy/state/map.cpp
index 53c5787804..f62866c4aa 100644
--- a/engines/nancy/state/map.cpp
+++ b/engines/nancy/state/map.cpp
@@ -84,6 +84,7 @@ void Map::init() {
 
 	_locations.clear();
 
+	_locations.reserve(4);
 	for (uint i = 0; i < 4; ++i) {
 		chunk->seek(0x162 + i * 16, SEEK_SET);
 		_locations.push_back(Location());
@@ -96,6 +97,7 @@ void Map::init() {
 			loc.isActive = true;
 		}
 
+		loc.scenes.reserve(2);
 		for (uint j = 0; j < 2; ++j) {
 			loc.scenes.push_back(Location::SceneChange());
 			Location::SceneChange &sc = loc.scenes[j];
diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp
index de50f88ff3..3f8c6d3ab9 100644
--- a/engines/nancy/state/scene.cpp
+++ b/engines/nancy/state/scene.cpp
@@ -397,6 +397,7 @@ void Scene::init() {
 
 		_hintsRemaining.clear();
 
+		_hintsRemaining.reserve(3);
 		for (uint i = 0; i < 3; ++i) {
 			_hintsRemaining.push_back(chunk->readByte());
 		}
@@ -575,6 +576,7 @@ void Scene::initStaticData() {
 
 	// Hardcoded by original engine
 	_mapAccessSceneIDs.clear();
+	_mapAccessSceneIDs.reserve(8);
 	_mapAccessSceneIDs.push_back(9);
 	_mapAccessSceneIDs.push_back(10);
 	_mapAccessSceneIDs.push_back(11);
diff --git a/engines/nancy/video.cpp b/engines/nancy/video.cpp
index f4625415fa..32ca875f98 100644
--- a/engines/nancy/video.cpp
+++ b/engines/nancy/video.cpp
@@ -114,6 +114,7 @@ AVFDecoder::AVFVideoTrack::AVFVideoTrack(Common::SeekableReadStream *stream, uin
 	_surface->create(_width, _height, _pixelFormat);
 	_frameSize = _width * _height * _pixelFormat.bytesPerPixel;
 
+	_chunkInfo.reserve(_frameCount);
 	for (uint i = 0; i < _frameCount; i++) {
 		ChunkInfo info;
 


Commit: 56c005ef2cb385b56088b11d37b95dab43d58005
    https://github.com/scummvm/scummvm/commit/56c005ef2cb385b56088b11d37b95dab43d58005
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Mark functions and iterators as const

Added the const keyword to several auto & iterators inside for loops, as well as to a number of class member functions.

Changed paths:
    engines/nancy/action/passwordpuzzle.cpp
    engines/nancy/action/primaryvideo.cpp
    engines/nancy/action/recordtypes.cpp
    engines/nancy/graphics.cpp
    engines/nancy/graphics.h
    engines/nancy/iff.cpp
    engines/nancy/iff.h
    engines/nancy/nancy.cpp
    engines/nancy/nancy.h
    engines/nancy/resource.cpp
    engines/nancy/resource.h
    engines/nancy/sound.cpp
    engines/nancy/sound.h
    engines/nancy/ui/inventorybox.h
    engines/nancy/ui/textbox.cpp


diff --git a/engines/nancy/action/passwordpuzzle.cpp b/engines/nancy/action/passwordpuzzle.cpp
index 101df86ae5..0f7bb6f3a4 100644
--- a/engines/nancy/action/passwordpuzzle.cpp
+++ b/engines/nancy/action/passwordpuzzle.cpp
@@ -220,7 +220,7 @@ void PasswordPuzzle::onPause(bool pause) {
 
 void PasswordPuzzle::drawText() {
 	_drawSurface.clear(GraphicsManager::getTransColor());
-	Graphics::Font *font = g_nancy->_graphicsManager->getFont(_fontID);
+	const Graphics::Font *font = g_nancy->_graphicsManager->getFont(_fontID);
 
 	Common::Rect bounds = _nameBounds;
 	bounds = NancySceneState.getViewport().convertViewportToScreen(bounds);
diff --git a/engines/nancy/action/primaryvideo.cpp b/engines/nancy/action/primaryvideo.cpp
index e11d4a945a..db44d304f5 100644
--- a/engines/nancy/action/primaryvideo.cpp
+++ b/engines/nancy/action/primaryvideo.cpp
@@ -317,10 +317,10 @@ void PlayPrimaryVideoChan0::execute() {
 }
 
 void PlayPrimaryVideoChan0::addConditionalResponses() {
-	for (auto &res : nancy1ConditionalResponses) {
+	for (const auto &res : nancy1ConditionalResponses) {
 		if (res.characterID == _conditionalResponseCharacterID) {
 			bool isSatisfied = true;
-			for (auto & cond : res.conditions) {
+			for (const auto & cond : res.conditions) {
 				if (cond.label == -1) {
 					break;
 				}
@@ -354,7 +354,7 @@ void PlayPrimaryVideoChan0::addConditionalResponses() {
 }
 
 void PlayPrimaryVideoChan0::addGoodbye() {
-	for (auto &res : nancy1Goodbyes) {
+	for (const auto &res : nancy1Goodbyes) {
 		if (res.characterID == _goodbyeResponseCharacterID) {
 			Common::File file;
 			char snd[9];
diff --git a/engines/nancy/action/recordtypes.cpp b/engines/nancy/action/recordtypes.cpp
index c63fbc4293..0aa369bcbb 100644
--- a/engines/nancy/action/recordtypes.cpp
+++ b/engines/nancy/action/recordtypes.cpp
@@ -545,14 +545,14 @@ void HintSystem::execute() {
 }
 
 void HintSystem::selectHint() {
-	for (auto &hint : nancy1Hints) {
+	for (const auto &hint : nancy1Hints) {
 		if (hint.characterID != _characterID) {
 			continue;
 		}
 
 		bool satisfied = true;
 
-		for (auto &flag : hint.flagConditions) {
+		for (const auto &flag : hint.flagConditions) {
 			if (flag.label == -1) {
 				break;
 			}
@@ -563,7 +563,7 @@ void HintSystem::selectHint() {
 			}
 		}
 
-		for (auto &inv : hint.inventoryCondition) {
+		for (const auto &inv : hint.inventoryCondition) {
 			if (inv.label == -1) {
 				break;
 			}
diff --git a/engines/nancy/graphics.cpp b/engines/nancy/graphics.cpp
index 988ca36183..1d384336ba 100644
--- a/engines/nancy/graphics.cpp
+++ b/engines/nancy/graphics.cpp
@@ -94,7 +94,7 @@ void GraphicsManager::draw() {
 }
 
 void GraphicsManager::addObject(RenderObject *object) {
-	for (auto &r : _objects) {
+	for (const auto &r : _objects) {
 		if (r == object) {
 			return;
 		}
diff --git a/engines/nancy/graphics.h b/engines/nancy/graphics.h
index 8dcb756ef1..31ffae87fb 100644
--- a/engines/nancy/graphics.h
+++ b/engines/nancy/graphics.h
@@ -48,7 +48,7 @@ public:
 
 	void redrawAll();
 
-	Font *getFont(uint id) { return id < _fonts.size() ? &_fonts[id] : nullptr; }
+	const Font *getFont(uint id) const { return id < _fonts.size() ? &_fonts[id] : nullptr; }
 
 	static void loadSurfacePalette(Graphics::ManagedSurface &inSurf, const Common::String paletteFilename);
 	static void copyToManaged(const Graphics::Surface &src, Graphics::ManagedSurface &dst, bool verticalFlip = false, bool doubleSize = false);
diff --git a/engines/nancy/iff.cpp b/engines/nancy/iff.cpp
index 372a613f09..669867e76e 100644
--- a/engines/nancy/iff.cpp
+++ b/engines/nancy/iff.cpp
@@ -145,7 +145,7 @@ uint32 IFF::stringToId(const Common::String &s) {
 	return id;
 }
 
-void IFF::list(Common::Array<Common::String> &nameList) {
+void IFF::list(Common::Array<Common::String> &nameList) const {
 	nameList.reserve(_chunks.size());
 	for (uint i = 0; i < _chunks.size(); ++i) {
 		nameList.push_back(idToString(_chunks[i].id));
diff --git a/engines/nancy/iff.h b/engines/nancy/iff.h
index ca9093b4b9..e297b5b217 100644
--- a/engines/nancy/iff.h
+++ b/engines/nancy/iff.h
@@ -48,7 +48,7 @@ public:
 	Common::SeekableReadStream *getChunkStream(const Common::String &id, uint index = 0) const;
 
 	// Debugger functions
-	void list(Common::Array<Common::String> &nameList);
+	void list(Common::Array<Common::String> &nameList) const;
 
 private:
 	static Common::String idToString(uint32 id);
diff --git a/engines/nancy/nancy.cpp b/engines/nancy/nancy.cpp
index b958efabe1..01419c930f 100644
--- a/engines/nancy/nancy.cpp
+++ b/engines/nancy/nancy.cpp
@@ -349,7 +349,7 @@ void NancyEngine::bootGameEngine() {
 	_cursorManager->init();
 }
 
-State::State *NancyEngine::getStateObject(GameState state) {
+State::State *NancyEngine::getStateObject(GameState state) const {
 	switch (state) {
 	case kLogo:
 		return &State::Logo::instance();
@@ -373,7 +373,7 @@ bool NancyEngine::addBootChunk(const Common::String &name, Common::SeekableReadS
 	return true;
 }
 
-Common::SeekableReadStream *NancyEngine::getBootChunkStream(const Common::String &name) {
+Common::SeekableReadStream *NancyEngine::getBootChunkStream(const Common::String &name) const {
 	if (_bootChunks.contains(name)) {
 		return _bootChunks[name];
 	} else {
diff --git a/engines/nancy/nancy.h b/engines/nancy/nancy.h
index 8ab8fb7f70..31ed891bed 100644
--- a/engines/nancy/nancy.h
+++ b/engines/nancy/nancy.h
@@ -129,7 +129,7 @@ public:
 	void setPreviousState();
 
 	// Chunks found in BOOT get extracted and cached at startup, this function lets other classes access them
-	Common::SeekableReadStream *getBootChunkStream(const Common::String &name);
+	Common::SeekableReadStream *getBootChunkStream(const Common::String &name) const;
 
 	void setMouseEnabled(bool enabled);
 
@@ -164,7 +164,7 @@ private:
 
 	void bootGameEngine();
 
-	State::State *getStateObject(GameState state);
+	State::State *getStateObject(GameState state) const;
 
 	bool addBootChunk(const Common::String &name, Common::SeekableReadStream *stream);
 	void clearBootChunks();
diff --git a/engines/nancy/resource.cpp b/engines/nancy/resource.cpp
index 4988d96f57..2a8eb34b6b 100644
--- a/engines/nancy/resource.cpp
+++ b/engines/nancy/resource.cpp
@@ -305,7 +305,7 @@ byte *CifTree::getCifData(const Common::String &name, ResourceManager::CifInfo &
 	return buf;
 }
 
-byte *ResourceManager::getCifData(const Common::String &treeName, const Common::String &name, CifInfo &info, uint *size) {
+byte *ResourceManager::getCifData(const Common::String &treeName, const Common::String &name, CifInfo &info, uint *size) const {
 	const CifFile *cifFile = CifFile::load(name);
 	byte *buf;
 
@@ -635,8 +635,8 @@ void ResourceManager::initialize() {
 	loadCifTree("ciftree", "dat");
 }
 
-bool ResourceManager::getCifInfo(const Common::String &name, CifInfo &info) {
-	for (auto &tree : _cifTrees) {
+bool ResourceManager::getCifInfo(const Common::String &name, CifInfo &info) const {
+	for (const auto &tree : _cifTrees) {
 		if (getCifInfo(tree->getName(), name, info)) {
 			return true;
 		}
@@ -645,7 +645,7 @@ bool ResourceManager::getCifInfo(const Common::String &name, CifInfo &info) {
 	return false;
 }
 
-bool ResourceManager::getCifInfo(const Common::String &treeName, const Common::String &name, CifInfo &info) {
+bool ResourceManager::getCifInfo(const Common::String &treeName, const Common::String &name, CifInfo &info) const {
 	const CifFile *cifFile = CifFile::load(name);
 
 	if (cifFile) {
@@ -662,7 +662,7 @@ bool ResourceManager::getCifInfo(const Common::String &treeName, const Common::S
 	return cifTree->getCifInfo(name, info);
 }
 
-byte *ResourceManager::getCifData(const Common::String &name, CifInfo &info, uint *size) {
+byte *ResourceManager::getCifData(const Common::String &name, CifInfo &info, uint *size) const {
 	// Try to open name.cif
 	const CifFile *cifFile = CifFile::load(name);
 	byte *buf = nullptr;
@@ -835,7 +835,7 @@ bool ResourceManager::loadImage(const Common::String &name, Graphics::ManagedSur
 	}
 }
 
-void ResourceManager::list(const Common::String &treeName, Common::Array<Common::String> &nameList, uint type) {
+void ResourceManager::list(const Common::String &treeName, Common::Array<Common::String> &nameList, uint type) const {
 	const CifTree *cifTree = findCifTree(treeName);
 
 	if (!cifTree)
@@ -844,7 +844,7 @@ void ResourceManager::list(const Common::String &treeName, Common::Array<Common:
 	cifTree->list(nameList, type);
 }
 
-Common::String ResourceManager::getCifDescription(const Common::String &treeName, const Common::String &name) {
+Common::String ResourceManager::getCifDescription(const Common::String &treeName, const Common::String &name) const {
 	CifInfo info;
 	if (!getCifInfo(treeName, name, info))
 		return Common::String::format("Couldn't find '%s' in CifTree '%s'\n", name.c_str(), treeName.c_str());
diff --git a/engines/nancy/resource.h b/engines/nancy/resource.h
index e6eb51e4f5..ec015192be 100644
--- a/engines/nancy/resource.h
+++ b/engines/nancy/resource.h
@@ -71,16 +71,16 @@ public:
 	byte *loadData(const Common::String &name, uint &size);
 
 	// Debugger functions
-	void list(const Common::String &treeName, Common::Array<Common::String> &nameList, uint type);
+	void list(const Common::String &treeName, Common::Array<Common::String> &nameList, uint type) const;
 	byte *loadCif(const Common::String &treeName, const Common::String &name, uint &size);
 	bool exportCif(const Common::String &treeName, const Common::String &name);
-	Common::String getCifDescription(const Common::String &treeName, const Common::String &name);
+	Common::String getCifDescription(const Common::String &treeName, const Common::String &name) const;
 
 private:
-	byte *getCifData(const Common::String &name, CifInfo &info, uint *size = nullptr);
-	byte *getCifData(const Common::String &treeName, const Common::String &name, CifInfo &info, uint *size = nullptr);
-	bool getCifInfo(const Common::String &name, CifInfo &info);
-	bool getCifInfo(const Common::String &treeName, const Common::String &name, CifInfo &info);
+	byte *getCifData(const Common::String &name, CifInfo &info, uint *size = nullptr) const;
+	byte *getCifData(const Common::String &treeName, const Common::String &name, CifInfo &info, uint *size = nullptr) const;
+	bool getCifInfo(const Common::String &name, CifInfo &info) const;
+	bool getCifInfo(const Common::String &treeName, const Common::String &name, CifInfo &info) const;
 	const CifTree *findCifTree(const Common::String &name) const;
 
 	Common::Array<const CifTree *> _cifTrees;
diff --git a/engines/nancy/sound.cpp b/engines/nancy/sound.cpp
index 8ca3568918..9e26788704 100644
--- a/engines/nancy/sound.cpp
+++ b/engines/nancy/sound.cpp
@@ -244,14 +244,14 @@ void SoundManager::pauseSound(const SoundDescription &description, bool pause) {
 	}
 }
 
-bool SoundManager::isSoundPlaying(uint16 channelID) {
+bool SoundManager::isSoundPlaying(uint16 channelID) const {
 	if (channelID > 32)
 		return false;
 
 	return _mixer->isSoundHandleActive(_channels[channelID].handle);
 }
 
-bool SoundManager::isSoundPlaying(const SoundDescription &description) {
+bool SoundManager::isSoundPlaying(const SoundDescription &description) const {
 	if (description.name == "NO SOUND") {
 		return false;
 	} else {
diff --git a/engines/nancy/sound.h b/engines/nancy/sound.h
index 0565c0769b..cd402e1e58 100644
--- a/engines/nancy/sound.h
+++ b/engines/nancy/sound.h
@@ -56,8 +56,8 @@ public:
 	void pauseSound(uint16 channelID, bool pause);
 	void pauseSound(const SoundDescription &description, bool pause);
 
-	bool isSoundPlaying(uint16 channelID);
-	bool isSoundPlaying(const SoundDescription &description);
+	bool isSoundPlaying(uint16 channelID) const;
+	bool isSoundPlaying(const SoundDescription &description) const;
 
 	void stopSound(uint16 channelID);
 	void stopSound(const SoundDescription &description);
diff --git a/engines/nancy/ui/inventorybox.h b/engines/nancy/ui/inventorybox.h
index 20e477e3cc..ebb6d4dae2 100644
--- a/engines/nancy/ui/inventorybox.h
+++ b/engines/nancy/ui/inventorybox.h
@@ -72,7 +72,7 @@ public:
 	void addItem(int16 itemID);
 	void removeItem(int16 itemID);
 
-	ItemDescription getItemDescription(uint id) { return _itemDescriptions[id]; }
+	ItemDescription getItemDescription(uint id) const { return _itemDescriptions[id]; }
 
 protected:
 	virtual uint16 getZOrder() const override { return 6; }
diff --git a/engines/nancy/ui/textbox.cpp b/engines/nancy/ui/textbox.cpp
index a40dfdebf8..9db17ef1d4 100644
--- a/engines/nancy/ui/textbox.cpp
+++ b/engines/nancy/ui/textbox.cpp
@@ -126,7 +126,7 @@ void Textbox::drawTextbox() {
 
 	_numLines = 0;
 
-	Font *font = g_nancy->_graphicsManager->getFont(_fontID);
+	const Font *font = g_nancy->_graphicsManager->getFont(_fontID);
 
 	uint maxWidth = _fullSurface.w - _borderWidth * 2;
 	uint lineDist = _lineHeight + _lineHeight / 4;


Commit: 0d8d31ffd02a8695c1ef986e30d636c6e2a71be9
    https://github.com/scummvm/scummvm/commit/0d8d31ffd02a8695c1ef986e30d636c6e2a71be9
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Formatting fixes

Added spaces after the = operator in every declaration of a pure virtual or defaulted function.

Changed paths:
    engines/nancy/action/actionrecord.h
    engines/nancy/font.h
    engines/nancy/renderobject.h
    engines/nancy/state/credits.h
    engines/nancy/state/map.h
    engines/nancy/state/state.h
    engines/nancy/time.h
    engines/nancy/ui/button.h
    engines/nancy/ui/fullscreenimage.h
    engines/nancy/ui/inventorybox.h
    engines/nancy/ui/scrollbar.h
    engines/nancy/ui/textbox.h


diff --git a/engines/nancy/action/actionrecord.h b/engines/nancy/action/actionrecord.h
index 458227e2af..4364aa66c8 100644
--- a/engines/nancy/action/actionrecord.h
+++ b/engines/nancy/action/actionrecord.h
@@ -93,7 +93,7 @@ public:
 		_itemRequired(-1) {}
 	virtual ~ActionRecord() {}
 
-	virtual void readData(Common::SeekableReadStream &stream) =0;
+	virtual void readData(Common::SeekableReadStream &stream) = 0;
 	virtual void execute() {}
 	virtual void onPause(bool pause) {}
 
@@ -104,7 +104,7 @@ protected:
 	void finishExecution();
 
 	// Used for debugging
-	virtual Common::String getRecordTypeName() const =0;
+	virtual Common::String getRecordTypeName() const = 0;
 
 public:
 	Common::String _description;                    // 0x00
diff --git a/engines/nancy/font.h b/engines/nancy/font.h
index b6e996b378..788e5f6277 100644
--- a/engines/nancy/font.h
+++ b/engines/nancy/font.h
@@ -37,8 +37,8 @@ class NancyEngine;
 
 class Font : public Graphics::Font {
 public:
-	Font() =default;
-	~Font() =default;
+	Font() = default;
+	~Font() = default;
 
 	void read(Common::SeekableReadStream &stream);
 
diff --git a/engines/nancy/renderobject.h b/engines/nancy/renderobject.h
index 02d3ecbc4f..d37fbe50b7 100644
--- a/engines/nancy/renderobject.h
+++ b/engines/nancy/renderobject.h
@@ -75,7 +75,7 @@ public:
 protected:
 	// Z order and blit type are extracted directly from the corresponding
 	// ZRenderStruct from the original engine
-	virtual uint16 getZOrder() const =0;
+	virtual uint16 getZOrder() const = 0;
 
 	// Needed for proper handling of objects inside the viewport
 	virtual bool isViewportRelative() const { return false; }
diff --git a/engines/nancy/state/credits.h b/engines/nancy/state/credits.h
index 5d7b50dd2d..1811a7603c 100644
--- a/engines/nancy/state/credits.h
+++ b/engines/nancy/state/credits.h
@@ -58,7 +58,7 @@ protected:
 		friend class Credits;
 	public:
 		CreditsText(RenderObject &redrawFrom) : RenderObject(redrawFrom) {}
-		virtual ~CreditsText() =default;
+		virtual ~CreditsText() = default;
 
 	protected:
 		virtual uint16 getZOrder() const override { return 1; }
diff --git a/engines/nancy/state/map.h b/engines/nancy/state/map.h
index 86f0dc23e6..e85b2f3370 100644
--- a/engines/nancy/state/map.h
+++ b/engines/nancy/state/map.h
@@ -82,7 +82,7 @@ private:
 	class MapLabel : public Nancy::RenderObject {
 	public:
 		MapLabel(RenderObject &redrawFrom, Map *parent) : Nancy::RenderObject(redrawFrom), _parent(parent) {}
-		virtual ~MapLabel() =default;
+		virtual ~MapLabel() = default;
 
 		virtual void init() override;
 
@@ -97,7 +97,7 @@ private:
 	class MapButton : public UI::Button {
 	public:
 		MapButton(RenderObject &redrawFrom, Map *parent) : Button(redrawFrom), _parent(parent) {}
-		virtual ~MapButton() =default;
+		virtual ~MapButton() = default;
 
 		virtual void init() override;
 		virtual void onClick() override;
diff --git a/engines/nancy/state/state.h b/engines/nancy/state/state.h
index 71de32554a..0eafe1152b 100644
--- a/engines/nancy/state/state.h
+++ b/engines/nancy/state/state.h
@@ -32,9 +32,9 @@ namespace State {
 class State {
 public:
 	State() {}
-	virtual ~State() =default;
+	virtual ~State() = default;
 
-	virtual void process() =0;
+	virtual void process() = 0;
 	virtual void onStateEnter() {}
 	virtual bool onStateExit() { return true; } // Returns whether the object destroyed itself after exit
 };
diff --git a/engines/nancy/time.h b/engines/nancy/time.h
index dbe75d055d..53ad05ade0 100644
--- a/engines/nancy/time.h
+++ b/engines/nancy/time.h
@@ -32,8 +32,8 @@ struct Time {
 public:
 	Time() { _milliseconds = 0; }
 	Time(const uint32 &t) { _milliseconds = t; }
-	Time(const Time &t) =default;
-	~Time() =default;
+	Time(const Time &t) = default;
+	~Time() = default;
 	explicit operator uint32() const { return _milliseconds; }
 	Time &operator=(const Time &t)                          { if (this != &t) _milliseconds = t._milliseconds; return *this; }
 	Time &operator=(const uint32 &t)                        { _milliseconds = t; return *this; }
diff --git a/engines/nancy/ui/button.h b/engines/nancy/ui/button.h
index 88a9bccc22..e73994290f 100644
--- a/engines/nancy/ui/button.h
+++ b/engines/nancy/ui/button.h
@@ -34,9 +34,9 @@ namespace UI {
 class Button : public RenderObject {
 public:
 	Button(RenderObject &redrawFrom) : RenderObject(redrawFrom) {}
-	virtual ~Button() =default;
+	virtual ~Button() = default;
 
-	virtual void onClick() =0;
+	virtual void onClick() = 0;
 
 	void handleInput(NancyInput &input);
 
@@ -47,7 +47,7 @@ protected:
 class MenuButton : public Button {
 public:
 	MenuButton(RenderObject &redrawFrom) : Button(redrawFrom) {}
-	virtual ~MenuButton() =default;
+	virtual ~MenuButton() = default;
 
 	virtual void init() override;
 	virtual void onClick() override;
@@ -56,7 +56,7 @@ public:
 class HelpButton : public Button {
 public:
 	HelpButton(RenderObject &redrawFrom) : Button(redrawFrom) {}
-	virtual ~HelpButton() =default;
+	virtual ~HelpButton() = default;
 
 	virtual void init() override;
 	virtual void onClick() override;
diff --git a/engines/nancy/ui/fullscreenimage.h b/engines/nancy/ui/fullscreenimage.h
index de0b0598c8..0de317f051 100644
--- a/engines/nancy/ui/fullscreenimage.h
+++ b/engines/nancy/ui/fullscreenimage.h
@@ -31,7 +31,7 @@ namespace UI {
 class FullScreenImage : public RenderObject {
 public:
 	FullScreenImage() : RenderObject() {}
-	virtual ~FullScreenImage() =default;
+	virtual ~FullScreenImage() = default;
 
 	void init(const Common::String &imageName);
 
diff --git a/engines/nancy/ui/inventorybox.h b/engines/nancy/ui/inventorybox.h
index ebb6d4dae2..ee84c0c497 100644
--- a/engines/nancy/ui/inventorybox.h
+++ b/engines/nancy/ui/inventorybox.h
@@ -88,7 +88,7 @@ private:
 		InventoryScrollbar(RenderObject &redrawFrom, InventoryBox *parent) :
 			Scrollbar(redrawFrom),
 			_parent(parent) {}
-		virtual ~InventoryScrollbar() =default;
+		virtual ~InventoryScrollbar() = default;
 
 		virtual void init() override;
 
@@ -104,7 +104,7 @@ private:
 			_soundTriggered(false),
 			_areOpen(false),
 			_curFrame(0) {}
-		virtual ~Shades() =default;
+		virtual ~Shades() = default;
 
 		virtual void init() override;
 		virtual void updateGraphics() override;
diff --git a/engines/nancy/ui/scrollbar.h b/engines/nancy/ui/scrollbar.h
index 57dcc15c6e..2b6b0cdce9 100644
--- a/engines/nancy/ui/scrollbar.h
+++ b/engines/nancy/ui/scrollbar.h
@@ -40,7 +40,7 @@ public:
 		_isClicked(false),
 		_currentPosition(0),
 		_maxDist(0) {}
-	virtual ~Scrollbar() =default;
+	virtual ~Scrollbar() = default;
 
 	virtual void init() override;
 
diff --git a/engines/nancy/ui/textbox.h b/engines/nancy/ui/textbox.h
index cc128c4067..61ce0e7239 100644
--- a/engines/nancy/ui/textbox.h
+++ b/engines/nancy/ui/textbox.h
@@ -83,7 +83,7 @@ private:
 		TextboxScrollbar(RenderObject &redrawFrom, Textbox *parent) :
 			Scrollbar(redrawFrom),
 			_parent(parent) {}
-		~TextboxScrollbar() =default;
+		~TextboxScrollbar() = default;
 
 		virtual void init() override;
 		Textbox *_parent;


Commit: 0d4516c8974e70f3277e89d3c7cd375df36ce902
    https://github.com/scummvm/scummvm/commit/0d4516c8974e70f3277e89d3c7cd375df36ce902
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Initialize sound channel types from a table

Added a static table of all nancy1 sound channel types, and changed initSoundChannels() so that it reads from the table instead of manually initializing channels one by one.

Changed paths:
    engines/nancy/sound.cpp


diff --git a/engines/nancy/sound.cpp b/engines/nancy/sound.cpp
index 9e26788704..7c67c513b1 100644
--- a/engines/nancy/sound.cpp
+++ b/engines/nancy/sound.cpp
@@ -40,6 +40,42 @@ enum SoundType {
 	kSoundTypeOgg
 };
 
+// Table extracted from nancy1, could be (and probably is) different between games
+static const Audio::Mixer::SoundType channelSoundTypes[] = {
+	Audio::Mixer::kMusicSoundType, // channel 0
+	Audio::Mixer::kMusicSoundType,
+	Audio::Mixer::kMusicSoundType,
+	Audio::Mixer::kSFXSoundType,
+	Audio::Mixer::kSFXSoundType,
+	Audio::Mixer::kSFXSoundType, // 5
+	Audio::Mixer::kSFXSoundType,
+	Audio::Mixer::kSpeechSoundType,
+	Audio::Mixer::kSpeechSoundType,
+	Audio::Mixer::kPlainSoundType,
+	Audio::Mixer::kPlainSoundType, // 10
+	Audio::Mixer::kPlainSoundType,
+	Audio::Mixer::kPlainSoundType,
+	Audio::Mixer::kPlainSoundType,
+	Audio::Mixer::kPlainSoundType,
+	Audio::Mixer::kPlainSoundType, // 15
+	Audio::Mixer::kPlainSoundType,
+	Audio::Mixer::kSFXSoundType,
+	Audio::Mixer::kSFXSoundType,
+	Audio::Mixer::kMusicSoundType,
+	Audio::Mixer::kSFXSoundType, // 20
+	Audio::Mixer::kSFXSoundType,
+	Audio::Mixer::kSFXSoundType,
+	Audio::Mixer::kSFXSoundType,
+	Audio::Mixer::kSFXSoundType,
+	Audio::Mixer::kSFXSoundType, // 25
+	Audio::Mixer::kSFXSoundType,
+	Audio::Mixer::kMusicSoundType,
+	Audio::Mixer::kMusicSoundType,
+	Audio::Mixer::kMusicSoundType,
+	Audio::Mixer::kSpeechSoundType, // 30
+	Audio::Mixer::kSFXSoundType
+};
+
 bool readWaveHeader(Common::SeekableReadStream *stream, SoundType &type, uint16 &numChannels,
 					uint32 &samplesPerSec, uint16 &bitsPerSample, uint32 &size) {
 	// The earliest HIS files are just WAVE files with the first 22 bytes of
@@ -293,43 +329,10 @@ void SoundManager::stopAndUnloadSpecificSounds() {
 }
 
 void SoundManager::initSoundChannels() {
-	// Original engine hardcoded these and so do we
-	_channels[7].type = Audio::Mixer::kSpeechSoundType;
-	_channels[8].type = Audio::Mixer::kSpeechSoundType;
-	_channels[30].type = Audio::Mixer::kSpeechSoundType;
-
-	_channels[0].type = Audio::Mixer::kMusicSoundType;
-	_channels[1].type = Audio::Mixer::kMusicSoundType;
-	_channels[2].type = Audio::Mixer::kMusicSoundType;
-	_channels[27].type = Audio::Mixer::kMusicSoundType;
-	_channels[28].type = Audio::Mixer::kMusicSoundType;
-	_channels[29].type = Audio::Mixer::kMusicSoundType;
-	_channels[19].type = Audio::Mixer::kMusicSoundType;
-
-	_channels[3].type = Audio::Mixer::kSFXSoundType;
-	_channels[4].type = Audio::Mixer::kSFXSoundType;
-	_channels[5].type = Audio::Mixer::kSFXSoundType;
-	_channels[6].type = Audio::Mixer::kSFXSoundType;
-	_channels[20].type = Audio::Mixer::kSFXSoundType;
-	_channels[21].type = Audio::Mixer::kSFXSoundType;
-	_channels[25].type = Audio::Mixer::kSFXSoundType;
-	_channels[26].type = Audio::Mixer::kSFXSoundType;
-	_channels[24].type = Audio::Mixer::kSFXSoundType;
-	_channels[23].type = Audio::Mixer::kSFXSoundType;
-	_channels[22].type = Audio::Mixer::kSFXSoundType;
-	_channels[31].type = Audio::Mixer::kSFXSoundType;
-	_channels[18].type = Audio::Mixer::kSFXSoundType;
-	_channels[17].type = Audio::Mixer::kSFXSoundType;
-
-	_channels[9].type = Audio::Mixer::kPlainSoundType;
-	_channels[10].type = Audio::Mixer::kPlainSoundType;
-	_channels[11].type = Audio::Mixer::kPlainSoundType;
-	_channels[12].type = Audio::Mixer::kPlainSoundType;
-	_channels[13].type = Audio::Mixer::kPlainSoundType;
-	_channels[14].type = Audio::Mixer::kPlainSoundType;
-	_channels[15].type = Audio::Mixer::kPlainSoundType;
-	_channels[16].type = Audio::Mixer::kPlainSoundType;
-
+	// Channel types are hardcoded in the original engine
+	for (uint i = 0; i < 32; ++i) {
+		_channels[i].type = channelSoundTypes[i];
+	}
 }
 
 } // End of namespace Nancy


Commit: cb63f50f715d47ea9aa0cc1c3a53d43949fb273e
    https://github.com/scummvm/scummvm/commit/cb63f50f715d47ea9aa0cc1c3a53d43949fb273e
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Remove underscore from struct member names

Removed the underscores from the names of a couple of struct members that were erroneously added a few commits back.

Changed paths:
    engines/nancy/action/primaryvideo.cpp
    engines/nancy/action/primaryvideo.h
    engines/nancy/action/telephone.cpp
    engines/nancy/action/telephone.h


diff --git a/engines/nancy/action/primaryvideo.cpp b/engines/nancy/action/primaryvideo.cpp
index db44d304f5..c9e947c3be 100644
--- a/engines/nancy/action/primaryvideo.cpp
+++ b/engines/nancy/action/primaryvideo.cpp
@@ -190,7 +190,7 @@ void PlayPrimaryVideoChan0::readData(Common::SeekableReadStream &stream) {
 		UI::Textbox::assembleTextLine(rawText, response.text, 400);
 		readFilename(stream, response.soundName);
 		stream.skip(1);
-		response._sceneChange.readData(stream);
+		response.sceneChange.readData(stream);
 		response.flagDesc.label = stream.readSint16LE();
 		response.flagDesc.flag = (NancyFlag)stream.readByte();
 
@@ -297,7 +297,7 @@ void PlayPrimaryVideoChan0::execute() {
 			g_nancy->_sound->stopSound(_responseGenericSound);
 
 			if (_pickedResponse != -1) {
-				NancySceneState.changeScene(_responses[_pickedResponse]._sceneChange);
+				NancySceneState.changeScene(_responses[_pickedResponse].sceneChange);
 			} else {
 				// Evaluate scene branch structs here
 
@@ -344,8 +344,8 @@ void PlayPrimaryVideoChan0::addConditionalResponses() {
 				ResponseStruct &newResponse = _responses.back();
 				newResponse.soundName = snd;
 				newResponse.text = file.readString();
-				newResponse._sceneChange.sceneID = res.sceneID;
-				newResponse._sceneChange.doNotStartSound = true;
+				newResponse.sceneChange.sceneID = res.sceneID;
+				newResponse.sceneChange.doNotStartSound = true;
 
 				file.close();
 			}
@@ -369,8 +369,8 @@ void PlayPrimaryVideoChan0::addGoodbye() {
 			newResponse.soundName = snd;
 			newResponse.text = file.readString();
 			// response is picked randomly
-			newResponse._sceneChange.sceneID = res.sceneIDs[g_nancy->_randomSource->getRandomNumber(3)];
-			newResponse._sceneChange.doNotStartSound = true;
+			newResponse.sceneChange.sceneID = res.sceneIDs[g_nancy->_randomSource->getRandomNumber(3)];
+			newResponse.sceneChange.doNotStartSound = true;
 
 			file.close();
 		}
diff --git a/engines/nancy/action/primaryvideo.h b/engines/nancy/action/primaryvideo.h
index 5861d9f612..66030a1e63 100644
--- a/engines/nancy/action/primaryvideo.h
+++ b/engines/nancy/action/primaryvideo.h
@@ -64,7 +64,7 @@ struct ResponseStruct {
 	ConditionFlags conditionFlags; // 0x01
 	Common::String text; // 0x06
 	Common::String soundName; // 0x196
-	SceneChangeDescription _sceneChange; // 0x1A0
+	SceneChangeDescription sceneChange; // 0x1A0
 	EventFlagDescription flagDesc; // 0x1A8
 };
 
diff --git a/engines/nancy/action/telephone.cpp b/engines/nancy/action/telephone.cpp
index 50786f1f0d..486d2fa7dd 100644
--- a/engines/nancy/action/telephone.cpp
+++ b/engines/nancy/action/telephone.cpp
@@ -117,7 +117,7 @@ void Telephone::readData(Common::SeekableReadStream &stream) {
 		stream.read(textBuf, 200);
 		textBuf[199] = '\0';
 		call.text = textBuf;
-		call._sceneChange.readData(stream);
+		call.sceneChange.readData(stream);
 		stream.skip(2);
 		call.flag.label = stream.readSint16LE();
 		call.flag.flag = (NancyFlag)stream.readUint16LE();
@@ -237,7 +237,7 @@ void Telephone::execute() {
 			break;
 		case kCall: {
 			PhoneCall &call = _calls[_selected];
-			NancySceneState.changeScene(call._sceneChange);
+			NancySceneState.changeScene(call.sceneChange);
 			NancySceneState.setEventFlag(call.flag);
 
 			break;
diff --git a/engines/nancy/action/telephone.h b/engines/nancy/action/telephone.h
index 24ce767765..a80376e33f 100644
--- a/engines/nancy/action/telephone.h
+++ b/engines/nancy/action/telephone.h
@@ -43,7 +43,7 @@ public:
 		Common::Array<byte> phoneNumber; // 0x0, 11 bytes
 		Common::String soundName; // 0xB
 		Common::String text; // 0x15, 0xC8 bytes
-		SceneChangeDescription _sceneChange; // 0xDD
+		SceneChangeDescription sceneChange; // 0xDD
 		// shouldStopRendering
 		EventFlagDescription flag; // 0xE7
 	};


Commit: 10a1d6e3a7498427030417c39ad80070c2206315
    https://github.com/scummvm/scummvm/commit/10a1d6e3a7498427030417c39ad80070c2206315
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add vorbis dependency

Added a missing vorbis dependency to configure.engine

Changed paths:
    engines/nancy/configure.engine


diff --git a/engines/nancy/configure.engine b/engines/nancy/configure.engine
index 12922994e7..1a5b7d41e1 100644
--- a/engines/nancy/configure.engine
+++ b/engines/nancy/configure.engine
@@ -1,3 +1,3 @@
 # This file is included from the main "configure" script
 # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps]
-add_engine nancy "Nancy Drew" no "" "" "16bit highres cxx11"
+add_engine nancy "Nancy Drew" no "" "" "16bit highres cxx11 vorbis"


Commit: c846247909b1542349d3bf52bc17a174c796ae95
    https://github.com/scummvm/scummvm/commit/c846247909b1542349d3bf52bc17a174c796ae95
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Fix viewport glitch

Fixed a rare glitch where the viewport would get cleared with a solid color when changing the frame.

Changed paths:
    engines/nancy/graphics.cpp


diff --git a/engines/nancy/graphics.cpp b/engines/nancy/graphics.cpp
index 1d384336ba..9ecb63deb9 100644
--- a/engines/nancy/graphics.cpp
+++ b/engines/nancy/graphics.cpp
@@ -137,12 +137,12 @@ void GraphicsManager::loadSurfacePalette(Graphics::ManagedSurface &inSurf, const
 }
 
 void GraphicsManager::copyToManaged(const Graphics::Surface &src, Graphics::ManagedSurface &dst, bool verticalFlip, bool doubleSize) {
-	if (dst.w != doubleSize ? src.w * 2 : src.w || dst.h != doubleSize ? src.h * 2 : src.h) {
+	if (dst.w != (doubleSize ? src.w * 2 : src.w) || dst.h != (doubleSize ? src.h * 2 : src.h)) {
 		const uint32 *palette = dst.getPalette();
 		bool hasTransColor = dst.hasTransparentColor();
 		dst.create(doubleSize ? src.w * 2 : src.w, doubleSize ? src.h * 2 : src.h, src.format);
 
-		if (palette) {
+		if (palette && g_nancy->getGameFlags() & NGF_8BITCOLOR) {
 			// free() clears the _hasPalette flag but doesn't clear the palette itself, so
 			// we just set it to itself; hopefully this doesn't cause any issues
 			dst.setPalette(palette, 0, 256);


Commit: eb2fe847d73f531ecbee0a89ead9da440cb210f6
    https://github.com/scummvm/scummvm/commit/eb2fe847d73f531ecbee0a89ead9da440cb210f6
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Improve code readability in actionmanager.cpp

Added a local variable to the debug output section of ActionManager::addNewActionRecord() to improve code readability.

Changed paths:
    engines/nancy/action/actionmanager.cpp


diff --git a/engines/nancy/action/actionmanager.cpp b/engines/nancy/action/actionmanager.cpp
index a4f19c6ccd..bac48aceb6 100644
--- a/engines/nancy/action/actionmanager.cpp
+++ b/engines/nancy/action/actionmanager.cpp
@@ -160,63 +160,64 @@ bool ActionManager::addNewActionRecord(Common::SeekableReadStream &inputData) {
 			newRecord->_execType == ActionRecord::kRepeating ? "kRepeating" : "kOneShot");
 	for (uint i = 0; i < newRecord->_dependencies.size(); ++i) {
 		debugCN(1, kDebugActionRecord, "\tDependency %i: type ", i);
-		switch (newRecord->_dependencies[i].type) {
+		DependencyRecord &dep = newRecord->_dependencies[i];
+		switch (dep.type) {
 		case kNone :
 			debugCN(1, kDebugActionRecord, "kNone");
 			break;
 		case kInventory :
 			debugCN(1, kDebugActionRecord, "kInventory, item ID %i %s",
-						newRecord->_dependencies[i].label,
-						newRecord->_dependencies[i].condition == kTrue ? "is in possession" : "is not in possession");
+						dep.label,
+						dep.condition == kTrue ? "is in possession" : "is not in possession");
 			break;
 		case kEventFlag :
 			debugCN(1, kDebugActionRecord, "kEventFlag, flag ID %i == %s",
-						newRecord->_dependencies[i].label,
-						newRecord->_dependencies[i].condition == kTrue ? "true" : "false");
+						dep.label,
+						dep.condition == kTrue ? "true" : "false");
 			break;
 		case kLogicCondition :
 			debugCN(1, kDebugActionRecord, "kLogicCondition, logic condition ID %i == %s",
-						newRecord->_dependencies[i].label,
-						newRecord->_dependencies[i].condition == kTrue ? "true" : "false");
+						dep.label,
+						dep.condition == kTrue ? "true" : "false");
 			break;
 		case kTotalTime :
 			debugCN(1, kDebugActionRecord, "kTotalTime, %i hours, %i minutes, %i seconds, %i milliseconds",
-						newRecord->_dependencies[i].hours,
-						newRecord->_dependencies[i].minutes,
-						newRecord->_dependencies[i].seconds,
-						newRecord->_dependencies[i].milliseconds);
+						dep.hours,
+						dep.minutes,
+						dep.seconds,
+						dep.milliseconds);
 			break;
 		case kSceneTime :
 			debugCN(1, kDebugActionRecord, "kSceneTime, %i hours, %i minutes, %i seconds, %i milliseconds",
-						newRecord->_dependencies[i].hours,
-						newRecord->_dependencies[i].minutes,
-						newRecord->_dependencies[i].seconds,
-						newRecord->_dependencies[i].milliseconds);
+						dep.hours,
+						dep.minutes,
+						dep.seconds,
+						dep.milliseconds);
 			break;
 		case kPlayerTime :
 			debugCN(1, kDebugActionRecord, "kPlayerTime, %i days, %i hours, %i minutes, %i seconds",
-						newRecord->_dependencies[i].hours,
-						newRecord->_dependencies[i].minutes,
-						newRecord->_dependencies[i].seconds,
-						newRecord->_dependencies[i].milliseconds);
+						dep.hours,
+						dep.minutes,
+						dep.seconds,
+						dep.milliseconds);
 			break;
 		case kSceneCount :
 			debugCN(1, kDebugActionRecord, "kSceneCount, scene ID %i, hit count %s %i",
-						newRecord->_dependencies[i].hours,
-						newRecord->_dependencies[i].milliseconds == 1 ? ">" : newRecord->_dependencies[i].milliseconds == 2 ? "<" : "==",
-						newRecord->_dependencies[i].seconds);
+						dep.hours,
+						dep.milliseconds == 1 ? ">" : dep.milliseconds == 2 ? "<" : "==",
+						dep.seconds);
 			break;
 		case kResetOnNewDay :
 			debugCN(1, kDebugActionRecord, "kResetOnNewDay");
 			break;
 		case kUseItem :
 			debugCN(1, kDebugActionRecord, "kUseItem, item ID %i %s",
-						newRecord->_dependencies[i].label,
-						newRecord->_dependencies[i].condition == kTrue ? "is held" : "is not held");
+						dep.label,
+						dep.condition == kTrue ? "is held" : "is not held");
 			break;
 		case kTimeOfDay :
 			debugCN(1, kDebugActionRecord, "kTimeOfDay, %s",
-						newRecord->_dependencies[i].label == 0 ? "day" : newRecord->_dependencies[i].label == 1 ? "night" : "dusk/dawn");
+						dep.label == 0 ? "day" : dep.label == 1 ? "night" : "dusk/dawn");
 			break;
 		case kTimerNotDone :
 			debugCN(1, kDebugActionRecord, "kTimerNotDone");
@@ -225,13 +226,13 @@ bool ActionManager::addNewActionRecord(Common::SeekableReadStream &inputData) {
 			debugCN(1, kDebugActionRecord, "kTimerDone");
 			break;
 		case kDifficultyLevel :
-			debugCN(1, kDebugActionRecord, "kDifficultyLevel, level %i", newRecord->_dependencies[i].condition);
+			debugCN(1, kDebugActionRecord, "kDifficultyLevel, level %i", dep.condition);
 			break;
 		default:
 			debugCN(1, kDebugActionRecord, "unknown");
 			break;
 		}
-		debugC(1, kDebugActionRecord, ", orFlag == %s", newRecord->_dependencies[i].orFlag == true ? "true" : "false");
+		debugC(1, kDebugActionRecord, ", orFlag == %s", dep.orFlag == true ? "true" : "false");
 	}
 
 	return true;


Commit: 16c083d015ff24bd2b3849ab007468f300235eba
    https://github.com/scummvm/scummvm/commit/16c083d015ff24bd2b3849ab007468f300235eba
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Add warnings for unknown dependency types

Added the two unknown dependency types to the ActionRecord::DependencyType enum and added warnings for when they get used.

Changed paths:
    engines/nancy/action/actionmanager.cpp
    engines/nancy/action/actionrecord.h


diff --git a/engines/nancy/action/actionmanager.cpp b/engines/nancy/action/actionmanager.cpp
index bac48aceb6..0d2dd4dd63 100644
--- a/engines/nancy/action/actionmanager.cpp
+++ b/engines/nancy/action/actionmanager.cpp
@@ -313,12 +313,12 @@ void ActionManager::processActionRecords() {
 						}
 
 						break;
-					/*case 7:
-						// TODO
+					case kUnknownType7:
+						warning("Unknown Dependency type 7");
+						break;
+					case kUnknownType8:
+						warning("Unknown Dependency type 8");
 						break;
-					case 8:
-						// TODO
-						break;*/
 					case kSceneCount:
 						// This dependency type keeps its data in the time variables
 						// Also, I'm pretty sure it never gets used
@@ -407,6 +407,7 @@ void ActionManager::processActionRecords() {
 
 						break;
 					default:
+						warning("Unknown Dependency type %i", (int)dep.type);
 						break;
 					}
 				}
diff --git a/engines/nancy/action/actionrecord.h b/engines/nancy/action/actionrecord.h
index 4364aa66c8..1bfb04e4ad 100644
--- a/engines/nancy/action/actionrecord.h
+++ b/engines/nancy/action/actionrecord.h
@@ -46,7 +46,8 @@ enum DependencyType : byte {
 	kTotalTime          = 4,
 	kSceneTime          = 5,
 	kPlayerTime         = 6,
-	// ...
+	kUnknownType7       = 7,
+	kUnknownType8       = 8,
 	kSceneCount         = 9,
 	kResetOnNewDay      = 10,
 	kUseItem            = 11,


Commit: 1907b5f74140793453b33f9265ac065db218ec30
    https://github.com/scummvm/scummvm/commit/1907b5f74140793453b33f9265ac065db218ec30
Author: fracturehill (strahy at outlook.com)
Date: 2021-03-24T17:20:46+01:00

Commit Message:
NANCY: Remove static members of GraphicsManager

Made GraphicsManager's static members non-static.

Changed paths:
    engines/nancy/action/leverpuzzle.cpp
    engines/nancy/action/orderingpuzzle.cpp
    engines/nancy/action/passwordpuzzle.cpp
    engines/nancy/action/rotatinglockpuzzle.cpp
    engines/nancy/action/secondarymovie.cpp
    engines/nancy/action/secondaryvideo.cpp
    engines/nancy/action/sliderpuzzle.cpp
    engines/nancy/action/telephone.cpp
    engines/nancy/cursor.cpp
    engines/nancy/font.cpp
    engines/nancy/graphics.cpp
    engines/nancy/graphics.h
    engines/nancy/renderobject.cpp
    engines/nancy/resource.cpp
    engines/nancy/ui/inventorybox.cpp
    engines/nancy/ui/textbox.cpp
    engines/nancy/video.cpp


diff --git a/engines/nancy/action/leverpuzzle.cpp b/engines/nancy/action/leverpuzzle.cpp
index 7a7d5794ce..a7e672191f 100644
--- a/engines/nancy/action/leverpuzzle.cpp
+++ b/engines/nancy/action/leverpuzzle.cpp
@@ -33,8 +33,8 @@ namespace Nancy {
 namespace Action {
 
 void LeverPuzzle::init() {
-	_drawSurface.create(_screenPosition.width(), _screenPosition.height(), GraphicsManager::getInputPixelFormat());
-	_drawSurface.clear(GraphicsManager::getTransColor());
+	_drawSurface.create(_screenPosition.width(), _screenPosition.height(), g_nancy->_graphicsManager->getInputPixelFormat());
+	_drawSurface.clear(g_nancy->_graphicsManager->getTransColor());
 
 	setTransparent(true);
 
diff --git a/engines/nancy/action/orderingpuzzle.cpp b/engines/nancy/action/orderingpuzzle.cpp
index e9be9770b2..b83f513d84 100644
--- a/engines/nancy/action/orderingpuzzle.cpp
+++ b/engines/nancy/action/orderingpuzzle.cpp
@@ -40,7 +40,7 @@ namespace Action {
 void OrderingPuzzle::init() {
 	// Screen position is initialized in readData and fits exactly the bounds of all elements on screen.
 	// This is a hacky way to make this particular action record work with this implementation's graphics manager
-	_drawSurface.create(_screenPosition.width(), _screenPosition.height(), GraphicsManager::getInputPixelFormat());
+	_drawSurface.create(_screenPosition.width(), _screenPosition.height(), g_nancy->_graphicsManager->getInputPixelFormat());
 	clearAllElements();
 
 	setTransparent(true);
@@ -223,12 +223,12 @@ void OrderingPuzzle::undrawElement(uint id) {
 	Common::Rect bounds = _destRects[id];
 	bounds.translate(-_screenPosition.left, -_screenPosition.top);
 
-	_drawSurface.fillRect(bounds, GraphicsManager::getTransColor());
+	_drawSurface.fillRect(bounds, g_nancy->_graphicsManager->getTransColor());
 	_needsRedraw = true;
 }
 
 void OrderingPuzzle::clearAllElements() {
-	_drawSurface.clear(GraphicsManager::getTransColor());
+	_drawSurface.clear(g_nancy->_graphicsManager->getTransColor());
 	setVisible(false);
 	_clickedSequence.clear();
 	return;
diff --git a/engines/nancy/action/passwordpuzzle.cpp b/engines/nancy/action/passwordpuzzle.cpp
index 0f7bb6f3a4..b4b1f00a0f 100644
--- a/engines/nancy/action/passwordpuzzle.cpp
+++ b/engines/nancy/action/passwordpuzzle.cpp
@@ -35,8 +35,8 @@ namespace Nancy {
 namespace Action {
 
 void PasswordPuzzle::init() {
-	_drawSurface.create(_screenPosition.width(), _screenPosition.height(), GraphicsManager::getInputPixelFormat());
-	_drawSurface.clear(GraphicsManager::getTransColor());
+	_drawSurface.create(_screenPosition.width(), _screenPosition.height(), g_nancy->_graphicsManager->getInputPixelFormat());
+	_drawSurface.clear(g_nancy->_graphicsManager->getTransColor());
 
 	setTransparent(true);
 
@@ -219,7 +219,7 @@ void PasswordPuzzle::onPause(bool pause) {
 }
 
 void PasswordPuzzle::drawText() {
-	_drawSurface.clear(GraphicsManager::getTransColor());
+	_drawSurface.clear(g_nancy->_graphicsManager->getTransColor());
 	const Graphics::Font *font = g_nancy->_graphicsManager->getFont(_fontID);
 
 	Common::Rect bounds = _nameBounds;
diff --git a/engines/nancy/action/rotatinglockpuzzle.cpp b/engines/nancy/action/rotatinglockpuzzle.cpp
index 4e9f1d05bf..6e1f604361 100644
--- a/engines/nancy/action/rotatinglockpuzzle.cpp
+++ b/engines/nancy/action/rotatinglockpuzzle.cpp
@@ -35,8 +35,8 @@ namespace Nancy {
 namespace Action {
 
 void RotatingLockPuzzle::init() {
-	_drawSurface.create(_screenPosition.width(), _screenPosition.height(), GraphicsManager::getInputPixelFormat());
-	_drawSurface.clear(GraphicsManager::getTransColor());
+	_drawSurface.create(_screenPosition.width(), _screenPosition.height(), g_nancy->_graphicsManager->getInputPixelFormat());
+	_drawSurface.clear(g_nancy->_graphicsManager->getTransColor());
 
 	setTransparent(true);
 
diff --git a/engines/nancy/action/secondarymovie.cpp b/engines/nancy/action/secondarymovie.cpp
index ecfc65c977..c1e2b3f545 100644
--- a/engines/nancy/action/secondarymovie.cpp
+++ b/engines/nancy/action/secondarymovie.cpp
@@ -72,7 +72,7 @@ void PlaySecondaryMovie::init() {
 	}
 
 	_decoder.loadFile(_videoName + ".avf");
-	_drawSurface.create(_decoder.getWidth(), _decoder.getHeight(), GraphicsManager::getInputPixelFormat());
+	_drawSurface.create(_decoder.getWidth(), _decoder.getHeight(), g_nancy->_graphicsManager->getInputPixelFormat());
 	_screenPosition = _drawSurface.getBounds();
 
 	RenderObject::init();
diff --git a/engines/nancy/action/secondaryvideo.cpp b/engines/nancy/action/secondaryvideo.cpp
index 318bd10ffc..c8bd4b4b4f 100644
--- a/engines/nancy/action/secondaryvideo.cpp
+++ b/engines/nancy/action/secondaryvideo.cpp
@@ -49,7 +49,7 @@ void PlaySecondaryVideo::init() {
 	// Every secondary video frame (in nancy1) plays exactly 12ms slower than what its metadata says.
 	// I'm still not sure how/why that happens so for now I'm using this hack to fix the timings
 	_decoder.addFrameTime(12);
-	_drawSurface.create(_decoder.getWidth(), _decoder.getHeight(), GraphicsManager::getInputPixelFormat());
+	_drawSurface.create(_decoder.getWidth(), _decoder.getHeight(), g_nancy->_graphicsManager->getInputPixelFormat());
 
 	if (_paletteFilename.size()) {
 		GraphicsManager::loadSurfacePalette(_drawSurface, _paletteFilename);
diff --git a/engines/nancy/action/sliderpuzzle.cpp b/engines/nancy/action/sliderpuzzle.cpp
index d808a56e7b..cf8706ca00 100644
--- a/engines/nancy/action/sliderpuzzle.cpp
+++ b/engines/nancy/action/sliderpuzzle.cpp
@@ -35,8 +35,8 @@ namespace Nancy {
 namespace Action {
 
 void SliderPuzzle::init() {
-	_drawSurface.create(_screenPosition.width(), _screenPosition.height(), GraphicsManager::getInputPixelFormat());
-	_drawSurface.clear(GraphicsManager::getTransColor());
+	_drawSurface.create(_screenPosition.width(), _screenPosition.height(), g_nancy->_graphicsManager->getInputPixelFormat());
+	_drawSurface.clear(g_nancy->_graphicsManager->getTransColor());
 
 	setTransparent(true);
 
@@ -310,7 +310,7 @@ void SliderPuzzle::drawTile(int tileID, uint posX, uint posY) {
 void SliderPuzzle::undrawTile(uint posX, uint posY) {
 	Common::Rect bounds = _destRects[posY][posX];
 	bounds.translate(-_screenPosition.left, -_screenPosition.top);
-	_drawSurface.fillRect(bounds, GraphicsManager::getTransColor());
+	_drawSurface.fillRect(bounds, g_nancy->_graphicsManager->getTransColor());
 
 	_needsRedraw = true;
 }
diff --git a/engines/nancy/action/telephone.cpp b/engines/nancy/action/telephone.cpp
index 486d2fa7dd..f3c08e866f 100644
--- a/engines/nancy/action/telephone.cpp
+++ b/engines/nancy/action/telephone.cpp
@@ -39,8 +39,8 @@ namespace Nancy {
 namespace Action {
 
 void Telephone::init() {
-	_drawSurface.create(_screenPosition.width(), _screenPosition.height(), GraphicsManager::getInputPixelFormat());
-	_drawSurface.clear(GraphicsManager::getTransColor());
+	_drawSurface.create(_screenPosition.width(), _screenPosition.height(), g_nancy->_graphicsManager->getInputPixelFormat());
+	_drawSurface.clear(g_nancy->_graphicsManager->getTransColor());
 
 	setTransparent(true);
 
@@ -316,7 +316,7 @@ void Telephone::undrawButton(uint id) {
 	Common::Rect bounds = _destRects[id];
 	bounds.translate(-_screenPosition.left, -_screenPosition.top);
 
-	_drawSurface.fillRect(bounds, GraphicsManager::getTransColor());
+	_drawSurface.fillRect(bounds, g_nancy->_graphicsManager->getTransColor());
 	_needsRedraw = true;
 }
 
diff --git a/engines/nancy/cursor.cpp b/engines/nancy/cursor.cpp
index 6094418585..170b260dec 100644
--- a/engines/nancy/cursor.cpp
+++ b/engines/nancy/cursor.cpp
@@ -117,7 +117,7 @@ void CursorManager::setCursor(CursorType type, int16 itemID) {
 	s.copyRectToSurface(*surf, 0, 0, bounds);
 
 	// TODO hotspots are terrible for arrow cursors, fix that??
-	CursorMan.replaceCursor(s.getPixels(), s.w, s.h, hotspot.x, hotspot.y, GraphicsManager::getTransColor(), false, &GraphicsManager::getInputPixelFormat());
+	CursorMan.replaceCursor(s.getPixels(), s.w, s.h, hotspot.x, hotspot.y, g_nancy->_graphicsManager->getTransColor(), false, &g_nancy->_graphicsManager->getInputPixelFormat());
 
 	s.free();
 
diff --git a/engines/nancy/font.cpp b/engines/nancy/font.cpp
index dbe484a5d6..820dbb08ec 100644
--- a/engines/nancy/font.cpp
+++ b/engines/nancy/font.cpp
@@ -30,7 +30,7 @@
 namespace Nancy {
 
 void Font::read(Common::SeekableReadStream &stream) {
-	_transColor = GraphicsManager::getTransColor();
+	_transColor = g_nancy->_graphicsManager->getTransColor();
 	_maxCharWidth = 0;
 	_fontHeight = 0;
 
@@ -97,7 +97,7 @@ void Font::drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 col
 
 	for (uint curY = 0; curY < height; ++curY) {
 		for (uint curX = 0; curX < width; ++curX) {
-			switch (GraphicsManager::getInputPixelFormat().bytesPerPixel) {
+			switch (g_nancy->_graphicsManager->getInputPixelFormat().bytesPerPixel) {
 			case 1:
 				// TODO
 				break;
diff --git a/engines/nancy/graphics.cpp b/engines/nancy/graphics.cpp
index 9ecb63deb9..76dd952dfd 100644
--- a/engines/nancy/graphics.cpp
+++ b/engines/nancy/graphics.cpp
@@ -39,9 +39,11 @@
 
 namespace Nancy {
 
-const Graphics::PixelFormat GraphicsManager::_inputPixelFormat = Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0);
-const Graphics::PixelFormat GraphicsManager::_screenPixelFormat = Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0);
-const Graphics::PixelFormat GraphicsManager::_clut8Format = Graphics::PixelFormat::createFormatCLUT8();
+GraphicsManager::GraphicsManager() :
+	_objects(objectComparator),
+	_inputPixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0),
+	_screenPixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0), 
+	_clut8Format(Graphics::PixelFormat::createFormatCLUT8()) {}
 
 void GraphicsManager::init() {
 	initGraphics(640, 480, &_screenPixelFormat);
@@ -232,6 +234,10 @@ const Graphics::PixelFormat &GraphicsManager::getInputPixelFormat() {
 	}
 }
 
+const Graphics::PixelFormat &GraphicsManager::getScreenPixelFormat() {
+	return _screenPixelFormat;
+}
+
 uint GraphicsManager::getTransColor() {
 	if (g_nancy->getGameFlags() & NGF_8BITCOLOR) {
 		return 1; // If this isn't correct, try picking the pixel at [0, 0] inside the palette bitmap
diff --git a/engines/nancy/graphics.h b/engines/nancy/graphics.h
index 31ffae87fb..7be0d3ab16 100644
--- a/engines/nancy/graphics.h
+++ b/engines/nancy/graphics.h
@@ -37,7 +37,7 @@ class NancyEngine;
 // Graphics class that handles multilayered surface rendering with minimal redraw
 class GraphicsManager {
 public:
-	GraphicsManager() : _objects(objectComparator) {}
+	GraphicsManager();
 
 	void init();
 	void draw();
@@ -50,16 +50,17 @@ public:
 
 	const Font *getFont(uint id) const { return id < _fonts.size() ? &_fonts[id] : nullptr; }
 
+	const Graphics::PixelFormat &getInputPixelFormat();
+	const Graphics::PixelFormat &getScreenPixelFormat();
+	uint getTransColor();
+
 	static void loadSurfacePalette(Graphics::ManagedSurface &inSurf, const Common::String paletteFilename);
 	static void copyToManaged(const Graphics::Surface &src, Graphics::ManagedSurface &dst, bool verticalFlip = false, bool doubleSize = false);
 	static void copyToManaged(void *src, Graphics::ManagedSurface &dst, uint srcW, uint srcH, const Graphics::PixelFormat &format, bool verticalFlip = false, bool doubleSize = false);
 
-	static const Graphics::PixelFormat &getInputPixelFormat();
-	static uint getTransColor();
-
 	Graphics::ManagedSurface _object0;
 
-	static const Graphics::PixelFormat _screenPixelFormat;
+	Graphics::PixelFormat _screenPixelFormat;
 
 private:
 	void loadFonts();
@@ -69,8 +70,8 @@ private:
 
 	Common::SortedArray<RenderObject *> _objects;
 
-	static const Graphics::PixelFormat _inputPixelFormat;
-	static const Graphics::PixelFormat _clut8Format;
+	Graphics::PixelFormat _inputPixelFormat;
+	Graphics::PixelFormat _clut8Format;
 
 	Graphics::Screen _screen;
 	Common::Array<Font> _fonts;
diff --git a/engines/nancy/renderobject.cpp b/engines/nancy/renderobject.cpp
index ae31613b9f..70e57b43d1 100644
--- a/engines/nancy/renderobject.cpp
+++ b/engines/nancy/renderobject.cpp
@@ -60,7 +60,7 @@ void RenderObject::setVisible(bool visible) {
 
 void RenderObject::setTransparent(bool isTransparent) {
 	if (isTransparent) {
-		_drawSurface.setTransparentColor(GraphicsManager::getTransColor());
+		_drawSurface.setTransparentColor(g_nancy->_graphicsManager->getTransColor());
 	} else {
 		_drawSurface.clearTransparentColor();
 	}
diff --git a/engines/nancy/resource.cpp b/engines/nancy/resource.cpp
index 2a8eb34b6b..a303af92f1 100644
--- a/engines/nancy/resource.cpp
+++ b/engines/nancy/resource.cpp
@@ -789,7 +789,7 @@ bool ResourceManager::loadImage(const Common::String &name, Graphics::Surface &s
 	surf.h = info.height;
 	surf.pitch = info.pitch;
 	surf.setPixels(buf);
-	surf.format = GraphicsManager::getInputPixelFormat();
+	surf.format = g_nancy->_graphicsManager->getInputPixelFormat();
 	return true;
 }
 
@@ -830,7 +830,7 @@ bool ResourceManager::loadImage(const Common::String &name, Graphics::ManagedSur
 			return false;
 		}
 
-		GraphicsManager::copyToManaged(buf, surf, info.width, info.height, GraphicsManager::getInputPixelFormat());
+		GraphicsManager::copyToManaged(buf, surf, info.width, info.height, g_nancy->_graphicsManager->getInputPixelFormat());
 		return true;
 	}
 }
diff --git a/engines/nancy/ui/inventorybox.cpp b/engines/nancy/ui/inventorybox.cpp
index ee5cbcad33..badfcafd15 100644
--- a/engines/nancy/ui/inventorybox.cpp
+++ b/engines/nancy/ui/inventorybox.cpp
@@ -74,7 +74,7 @@ void InventoryBox::init() {
 	g_nancy->_resource->loadImage(inventoryBoxIconsImageName, _iconsSurface);
 
 	uint numItems = 11; // TODO
-	_fullInventorySurface.create(_screenPosition.width(), _screenPosition.height() * ((numItems / 4) + 1), GraphicsManager::_screenPixelFormat);
+	_fullInventorySurface.create(_screenPosition.width(), _screenPosition.height() * ((numItems / 4) + 1), g_nancy->_graphicsManager->getScreenPixelFormat());
 	Common::Rect sourceRect = _screenPosition;
 	sourceRect.moveTo(0, 0);
 	_drawSurface.create(_fullInventorySurface, sourceRect);
@@ -223,7 +223,7 @@ void InventoryBox::InventoryScrollbar::init() {
 
 void InventoryBox::Shades::init() {
 	Common::Rect bounds = _parent->getBounds();
-	_drawSurface.create(bounds.width(), bounds.height(), GraphicsManager::_screenPixelFormat);
+	_drawSurface.create(bounds.width(), bounds.height(), g_nancy->_graphicsManager->getScreenPixelFormat());
 	_screenPosition = _parent->getScreenPosition();
 	_nextFrameTime = 0;
 	setAnimationFrame(_curFrame);
@@ -274,7 +274,7 @@ void InventoryBox::Shades::setAnimationFrame(uint frame) {
 		setVisible(true);
 	}
 
-	_drawSurface.clear(GraphicsManager::getTransColor());
+	_drawSurface.clear(g_nancy->_graphicsManager->getTransColor());
 
 	// Draw left shade
 	srcRect = _parent->_shadesSrc[frame * 2];
diff --git a/engines/nancy/ui/textbox.cpp b/engines/nancy/ui/textbox.cpp
index 9db17ef1d4..f560847e57 100644
--- a/engines/nancy/ui/textbox.cpp
+++ b/engines/nancy/ui/textbox.cpp
@@ -55,7 +55,7 @@ void Textbox::init() {
 	chunk->seek(0x20);
 	Common::Rect innerBoundingBox;
 	readRect(*chunk, innerBoundingBox);
-	_fullSurface.create(innerBoundingBox.width(), innerBoundingBox.height(), GraphicsManager::_screenPixelFormat);
+	_fullSurface.create(innerBoundingBox.width(), innerBoundingBox.height(), g_nancy->_graphicsManager->getScreenPixelFormat());
 
 	_scrollbarDefaultDest.x = chunk->readUint16LE();
 	_scrollbarDefaultDest.y = chunk->readUint16LE();
diff --git a/engines/nancy/video.cpp b/engines/nancy/video.cpp
index 32ca875f98..41b3d4e14a 100644
--- a/engines/nancy/video.cpp
+++ b/engines/nancy/video.cpp
@@ -23,6 +23,7 @@
 #include "engines/nancy/video.h"
 #include "engines/nancy/decompress.h"
 #include "engines/nancy/graphics.h"
+#include "engines/nancy/nancy.h"
 
 #include "common/endian.h"
 #include "common/stream.h"
@@ -110,7 +111,7 @@ AVFDecoder::AVFVideoTrack::AVFVideoTrack(Common::SeekableReadStream *stream, uin
 		error("Unknown compression type %d found in AVF", comp);
 
 	_surface = new Graphics::Surface();
-	_pixelFormat = GraphicsManager::getInputPixelFormat();
+	_pixelFormat = g_nancy->_graphicsManager->getInputPixelFormat();
 	_surface->create(_width, _height, _pixelFormat);
 	_frameSize = _width * _height * _pixelFormat.bytesPerPixel;
 




More information about the Scummvm-git-logs mailing list