[Scummvm-git-logs] scummvm master -> 6bab26725ee6faf5ed1c005af1e364624459993a

sev- noreply at scummvm.org
Mon Apr 6 23:11:41 UTC 2026


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

Summary:
6752e7286d ENGINES: BOLT: Add engine stub and detection
22990cc4ee BOLT: CARNIVAL: Initial engine function stubs
f204ad4fdf BOLT: Stub the entirety of the XPLIB.DLL code
2745133738 BOLT: Implement the entirety of XPLIB.DLL except sound
e358ac0d46 BOLT: CARNIVAL: Implement main part of the game engine
efb9fe5455 BOLT: CARNIVAL: Fix screensaver dimming routine
ca9d735bd4 BOLT: CARNIVAL: Fix palette setup when playing a video over a booth
8f27afd84e BOLT: CARNIVAL: Implement Fred's minigame
584db066a0 BOLT: CARNIVAL: Implement TopCat's minigame
1ee07b6140 BOLT: CARNIVAL: Add support for full Italian version
cc069232f4 BOLT: CARNIVAL: Implement Scooby-Doo's minigame
bb96b5c22d BOLT: CARNIVAL: Fix Fred's minigame regression
4ae8a3557a BOLT: CARNIVAL: Implement George's minigame
5a08cf4461 BOLT: CARNIVAL: Implement Huck's minigame
d0c9be04ab BOLT: CARNIVAL: Implement Yogi's minigame
9f25c67c6d BOLT: CARNIVAL: Final cleanup
a4e8528b28 BOLT: Fix module.mk
564a389c58 BOLT: Fix linux build
556f126ceb BOLT: Fix warnings
7960ce109f BOLT: Clean-up detection
b31b7569bb BOLT: Fix assets not being found in release build
87e6e8d8c1 BOLT: CARNIVAL: Some fixes and clean-ups
9fb3cfc755 BOLT: CARNIVAL: Fix more bugs
5b289038dc BOLT: Reduce include list in detection.cpp
f21d8e6800 BOLT: Add myself to engine credits
eb07f69e08 BOLT: Reduce number of files for detection
8e4480acb2 BOLT: Don't build the engine by default
14ceee7511 BOLT: Tidy up includes order
6bab26725e BOLT: Address more review comments


Commit: 6752e7286d00e4447d5f7f232802b0ed95d23302
    https://github.com/scummvm/scummvm/commit/6752e7286d00e4447d5f7f232802b0ed95d23302
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-04-07T01:11:14+02:00

Commit Message:
ENGINES: BOLT: Add engine stub and detection

Changed paths:
  A engines/bolt/POTFILES
  A engines/bolt/bolt.cpp
  A engines/bolt/bolt.h
  A engines/bolt/configure.engine
  A engines/bolt/console.cpp
  A engines/bolt/console.h
  A engines/bolt/credits.pl
  A engines/bolt/detection.cpp
  A engines/bolt/detection.h
  A engines/bolt/detection_tables.h
  A engines/bolt/metaengine.cpp
  A engines/bolt/metaengine.h
  A engines/bolt/module.mk


diff --git a/engines/bolt/POTFILES b/engines/bolt/POTFILES
new file mode 100644
index 00000000000..980f96f1cd6
--- /dev/null
+++ b/engines/bolt/POTFILES
@@ -0,0 +1 @@
+engines/bolt/metaengine.cpp
diff --git a/engines/bolt/bolt.cpp b/engines/bolt/bolt.cpp
new file mode 100644
index 00000000000..9df339fdd00
--- /dev/null
+++ b/engines/bolt/bolt.cpp
@@ -0,0 +1,109 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "bolt/bolt.h"
+#include "graphics/framelimiter.h"
+#include "bolt/detection.h"
+#include "bolt/console.h"
+#include "common/scummsys.h"
+#include "common/config-manager.h"
+#include "common/debug-channels.h"
+#include "common/events.h"
+#include "common/system.h"
+#include "engines/util.h"
+#include "graphics/paletteman.h"
+
+namespace Bolt {
+
+BoltEngine *g_engine;
+
+BoltEngine::BoltEngine(OSystem *syst, const ADGameDescription *gameDesc) : Engine(syst),
+	_gameDescription(gameDesc), _randomSource("Bolt") {
+	g_engine = this;
+}
+
+BoltEngine::~BoltEngine() {
+	delete _screen;
+}
+
+uint32 BoltEngine::getFeatures() const {
+	return _gameDescription->flags;
+}
+
+Common::String BoltEngine::getGameId() const {
+	return _gameDescription->gameId;
+}
+
+Common::Error BoltEngine::run() {
+	// Initialize 320x200 paletted graphics mode
+	initGraphics(320, 200);
+	_screen = new Graphics::Screen();
+
+	// Set the engine's debugger console
+	setDebugger(new Console());
+
+	// If a savegame was selected from the launcher, load it
+	int saveSlot = ConfMan.getInt("save_slot");
+	if (saveSlot != -1)
+		(void)loadGameState(saveSlot);
+
+	// Draw a series of boxes on screen as a sample
+	for (int i = 0; i < 100; ++i)
+		_screen->frameRect(Common::Rect(i, i, 320 - i, 200 - i), i);
+	_screen->update();
+
+	// Simple event handling loop
+	byte pal[256 * 3] = { 0 };
+	Common::Event e;
+	int offset = 0;
+
+	Graphics::FrameLimiter limiter(g_system, 60);
+	while (!shouldQuit()) {
+		while (g_system->getEventManager()->pollEvent(e)) {
+		}
+
+		// Cycle through a simple palette
+		++offset;
+		for (int i = 0; i < 256; ++i)
+			pal[i * 3 + 1] = (i + offset) % 256;
+		g_system->getPaletteManager()->setPalette(pal, 0, 256);
+		// Delay for a bit. All events loops should have a delay
+		// to prevent the system being unduly loaded
+		limiter.delayBeforeSwap();
+		_screen->update();
+		limiter.startFrame();
+	}
+
+	return Common::kNoError;
+}
+
+Common::Error BoltEngine::syncGame(Common::Serializer &s) {
+	// The Serializer has methods isLoading() and isSaving()
+	// if you need to specific steps; for example setting
+	// an array size after reading it's length, whereas
+	// for saving it would write the existing array's length
+	int dummy = 0;
+	s.syncAsUint32LE(dummy);
+
+	return Common::kNoError;
+}
+
+} // End of namespace Bolt
diff --git a/engines/bolt/bolt.h b/engines/bolt/bolt.h
new file mode 100644
index 00000000000..9c6b11b12d9
--- /dev/null
+++ b/engines/bolt/bolt.h
@@ -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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef BOLT_H
+#define BOLT_H
+
+#include "common/scummsys.h"
+#include "common/system.h"
+#include "common/error.h"
+#include "common/fs.h"
+#include "common/hash-str.h"
+#include "common/random.h"
+#include "common/serializer.h"
+#include "common/util.h"
+#include "engines/engine.h"
+#include "engines/savestate.h"
+#include "graphics/screen.h"
+
+#include "bolt/detection.h"
+
+namespace Bolt {
+
+struct BoltGameDescription;
+
+class BoltEngine : public Engine {
+private:
+	const ADGameDescription *_gameDescription;
+	Common::RandomSource _randomSource;
+protected:
+	// Engine APIs
+	Common::Error run() override;
+public:
+	Graphics::Screen *_screen = nullptr;
+public:
+	BoltEngine(OSystem *syst, const ADGameDescription *gameDesc);
+	~BoltEngine() override;
+
+	uint32 getFeatures() const;
+
+	/**
+	 * Returns the game Id
+	 */
+	Common::String getGameId() const;
+
+	/**
+	 * Gets a random number
+	 */
+	uint32 getRandomNumber(uint maxNum) {
+		return _randomSource.getRandomNumber(maxNum);
+	}
+
+	bool hasFeature(EngineFeature f) const override {
+		return
+		    (f == kSupportsLoadingDuringRuntime) ||
+		    (f == kSupportsSavingDuringRuntime) ||
+		    (f == kSupportsReturnToLauncher);
+	};
+
+	bool canLoadGameStateCurrently(Common::U32String *msg = nullptr) override {
+		return true;
+	}
+	bool canSaveGameStateCurrently(Common::U32String *msg = nullptr) override {
+		return true;
+	}
+
+	/**
+	 * Uses a serializer to allow implementing savegame
+	 * loading and saving using a single method
+	 */
+	Common::Error syncGame(Common::Serializer &s);
+
+	Common::Error saveGameStream(Common::WriteStream *stream, bool isAutosave = false) override {
+		Common::Serializer s(nullptr, stream);
+		return syncGame(s);
+	}
+	Common::Error loadGameStream(Common::SeekableReadStream *stream) override {
+		Common::Serializer s(stream, nullptr);
+		return syncGame(s);
+	}
+};
+
+extern BoltEngine *g_engine;
+#define SHOULD_QUIT ::Bolt::g_engine->shouldQuit()
+
+} // End of namespace Bolt
+
+#endif // BOLT_H
diff --git a/engines/bolt/configure.engine b/engines/bolt/configure.engine
new file mode 100644
index 00000000000..6629b5b25b9
--- /dev/null
+++ b/engines/bolt/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] [components]
+add_engine bolt "Bolt" no "" "" "" ""
diff --git a/engines/bolt/console.cpp b/engines/bolt/console.cpp
new file mode 100644
index 00000000000..2bb36865027
--- /dev/null
+++ b/engines/bolt/console.cpp
@@ -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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "bolt/console.h"
+
+namespace Bolt {
+
+Console::Console() : GUI::Debugger() {
+	registerCmd("test",   WRAP_METHOD(Console, Cmd_test));
+}
+
+Console::~Console() {
+}
+
+bool Console::Cmd_test(int argc, const char **argv) {
+	debugPrintf("Test\n");
+	return true;
+}
+
+} // End of namespace Bolt
diff --git a/engines/bolt/console.h b/engines/bolt/console.h
new file mode 100644
index 00000000000..c697e77a1fd
--- /dev/null
+++ b/engines/bolt/console.h
@@ -0,0 +1,40 @@
+
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef BOLT_CONSOLE_H
+#define BOLT_CONSOLE_H
+
+#include "gui/debugger.h"
+
+namespace Bolt {
+
+class Console : public GUI::Debugger {
+private:
+	bool Cmd_test(int argc, const char **argv);
+public:
+	Console();
+	~Console() override;
+};
+
+} // End of namespace Bolt
+
+#endif // BOLT_CONSOLE_H
diff --git a/engines/bolt/credits.pl b/engines/bolt/credits.pl
new file mode 100644
index 00000000000..3e137cd1a0f
--- /dev/null
+++ b/engines/bolt/credits.pl
@@ -0,0 +1,3 @@
+begin_section("Bolt");
+	add_person("Name 1", "Handle 1", "");
+end_section();
diff --git a/engines/bolt/detection.cpp b/engines/bolt/detection.cpp
new file mode 100644
index 00000000000..4defbfd281f
--- /dev/null
+++ b/engines/bolt/detection.cpp
@@ -0,0 +1,45 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "base/plugins.h"
+#include "common/config-manager.h"
+#include "common/file.h"
+#include "common/md5.h"
+#include "common/str-array.h"
+#include "common/translation.h"
+#include "common/util.h"
+#include "bolt/detection.h"
+#include "bolt/detection_tables.h"
+
+const DebugChannelDef BoltMetaEngineDetection::debugFlagList[] = {
+	{ Bolt::kDebugGraphics, "Graphics", "Graphics debug level" },
+	{ Bolt::kDebugPath, "Path", "Pathfinding debug level" },
+	{ Bolt::kDebugFilePath, "FilePath", "File path debug level" },
+	{ Bolt::kDebugScan, "Scan", "Scan for unrecognised games" },
+	{ Bolt::kDebugScript, "Script", "Enable debug script dump" },
+	DEBUG_CHANNEL_END
+};
+
+BoltMetaEngineDetection::BoltMetaEngineDetection() : AdvancedMetaEngineDetection(
+	Bolt::gameDescriptions, Bolt::boltGames) {
+}
+
+REGISTER_PLUGIN_STATIC(BOLT_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, BoltMetaEngineDetection);
diff --git a/engines/bolt/detection.h b/engines/bolt/detection.h
new file mode 100644
index 00000000000..ead614b2ab2
--- /dev/null
+++ b/engines/bolt/detection.h
@@ -0,0 +1,69 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef BOLT_DETECTION_H
+#define BOLT_DETECTION_H
+
+#include "engines/advancedDetector.h"
+
+namespace Bolt {
+
+enum BoltDebugChannels {
+	kDebugGraphics = 1,
+	kDebugPath,
+	kDebugScan,
+	kDebugFilePath,
+	kDebugScript,
+};
+
+extern const PlainGameDescriptor boltGames[];
+
+extern const ADGameDescription gameDescriptions[];
+
+#define GAMEOPTION_ORIGINAL_SAVELOAD GUIO_GAMEOPTIONS1
+
+} // End of namespace Bolt
+
+class BoltMetaEngineDetection : public AdvancedMetaEngineDetection<ADGameDescription> {
+	static const DebugChannelDef debugFlagList[];
+
+public:
+	BoltMetaEngineDetection();
+	~BoltMetaEngineDetection() override {}
+
+	const char *getName() const override {
+		return "bolt";
+	}
+
+	const char *getEngineName() const override {
+		return "Bolt";
+	}
+
+	const char *getOriginalCopyright() const override {
+		return "Bolt (C)";
+	}
+
+	const DebugChannelDef *getDebugChannels() const override {
+		return debugFlagList;
+	}
+};
+
+#endif // BOLT_DETECTION_H
diff --git a/engines/bolt/detection_tables.h b/engines/bolt/detection_tables.h
new file mode 100644
index 00000000000..f3785c88ad5
--- /dev/null
+++ b/engines/bolt/detection_tables.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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace Bolt {
+
+const PlainGameDescriptor boltGames[] = {
+	{ "carnival", "Cartoon Carnival" },
+	{ 0, 0 }
+};
+
+const ADGameDescription gameDescriptions[] = {
+	{
+		"carnival",
+		nullptr,
+		{
+			{"BOOTHS.BLT", 0, "db13e86a850b9844bf1bab19e9ec2f6f", 1326163},
+			{"FRED.BLT",   0, "19540d4ebce8bedd6e5d8523ad82b333", 302133 },
+			{"GEORGE.BLT", 0, "57cfd68c99dfb13f139886eaf36b09ca", 1017533},
+			{"HUCK.BLT",   0, "c4587a4d613616bfde5e0af9ccf8465e", 1225354},
+			{"SCOOBY.BLT", 0, "c0918e36e532a24e2fc4942261d907aa", 3733392},
+			{"TOPCAT.BLT", 0, "33ed048eef63c54962ef37998cc19366", 579091 },
+			{"YOGI.BLT",   0, "e47d619a79b94f0dfff72a49599fdbcb", 5533358},
+			AD_LISTEND
+		},
+		Common::EN_USA,
+		Common::kPlatformWindows,
+		ADGF_UNSTABLE,
+		GUIO1(GUIO_NONE)
+	},
+	{
+		"carnival",
+		nullptr,
+		{
+			{"ASSETS/BOOTHS.BLT", 0, "db13e86a850b9844bf1bab19e9ec2f6f", 1326163},
+			{"ASSETS/FRED.BLT",   0, "19540d4ebce8bedd6e5d8523ad82b333", 302133 },
+			{"ASSETS/GEORGE.BLT", 0, "57cfd68c99dfb13f139886eaf36b09ca", 1017533},
+			{"ASSETS/HUCK.BLT",   0, "c4587a4d613616bfde5e0af9ccf8465e", 1225354},
+			{"ASSETS/SCOOBY.BLT", 0, "c0918e36e532a24e2fc4942261d907aa", 3733392},
+			{"ASSETS/TOPCAT.BLT", 0, "33ed048eef63c54962ef37998cc19366", 579091 },
+			{"ASSETS/YOGI.BLT",   0, "e47d619a79b94f0dfff72a49599fdbcb", 5533358},
+			AD_LISTEND
+		},
+		Common::EN_USA,
+		Common::kPlatformWindows,
+		ADGF_UNSTABLE,
+		GUIO1(GUIO_NONE)
+	},
+	{
+		"carnival",
+		"Demo",
+		{
+			{"BOOTHS.BLT", 0, "3fc80c0feaaa300720a3ed921496233a", 878177 },
+			{"GEORGE.BLT", 0, "ca29738fe8655b9adec81aa48914c213", 388257 },
+			{"SCOOBY.BLT", 0, "4dfbdd87fdd2ec05fbdc7ae6435fb239", 677438 },
+			{"YOGI.BLT",   0, "06da5c2b889b5ecca371e99d8209347c", 5209352},
+			AD_LISTEND
+		},
+		Common::IT_ITA,
+		Common::kPlatformWindows,
+		ADGF_UNSTABLE | ADGF_DEMO,
+		GUIO1(GUIO_NONE)
+	},
+	{
+		"carnival",
+		"Demo",
+		{
+			{"ASSETS/BOOTHS.BLT", 0, "3fc80c0feaaa300720a3ed921496233a", 878177 },
+			{"ASSETS/GEORGE.BLT", 0, "ca29738fe8655b9adec81aa48914c213", 388257 },
+			{"ASSETS/SCOOBY.BLT", 0, "4dfbdd87fdd2ec05fbdc7ae6435fb239", 677438 },
+			{"ASSETS/YOGI.BLT",   0, "06da5c2b889b5ecca371e99d8209347c", 5209352},
+			AD_LISTEND
+		},
+		Common::IT_ITA,
+		Common::kPlatformWindows,
+		ADGF_UNSTABLE | ADGF_DEMO,
+		GUIO1(GUIO_NONE)
+	},
+
+	AD_TABLE_END_MARKER
+};
+
+} // End of namespace Bolt
diff --git a/engines/bolt/metaengine.cpp b/engines/bolt/metaengine.cpp
new file mode 100644
index 00000000000..9beb569542f
--- /dev/null
+++ b/engines/bolt/metaengine.cpp
@@ -0,0 +1,69 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "common/translation.h"
+
+#include "bolt/metaengine.h"
+#include "bolt/detection.h"
+#include "bolt/bolt.h"
+
+namespace Bolt {
+
+static const ADExtraGuiOptionsMap optionsList[] = {
+	{
+		GAMEOPTION_ORIGINAL_SAVELOAD,
+		{
+			_s("Use original save/load screens"),
+			_s("Use the original save/load screens instead of the ScummVM ones"),
+			"original_menus",
+			false,
+			0,
+			0
+		}
+	},
+	AD_EXTRA_GUI_OPTIONS_TERMINATOR
+};
+
+} // End of namespace Bolt
+
+const char *BoltMetaEngine::getName() const {
+	return "bolt";
+}
+
+const ADExtraGuiOptionsMap *BoltMetaEngine::getAdvancedExtraGuiOptions() const {
+	return Bolt::optionsList;
+}
+
+Common::Error BoltMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
+	*engine = new Bolt::BoltEngine(syst, desc);
+	return Common::kNoError;
+}
+
+bool BoltMetaEngine::hasFeature(MetaEngineFeature f) const {
+	return checkExtendedSaves(f) ||
+		(f == kSupportsLoadingDuringStartup);
+}
+
+#if PLUGIN_ENABLED_DYNAMIC(BOLT)
+REGISTER_PLUGIN_DYNAMIC(BOLT, PLUGIN_TYPE_ENGINE, BoltMetaEngine);
+#else
+REGISTER_PLUGIN_STATIC(BOLT, PLUGIN_TYPE_ENGINE, BoltMetaEngine);
+#endif
diff --git a/engines/bolt/metaengine.h b/engines/bolt/metaengine.h
new file mode 100644
index 00000000000..e9228d86bf5
--- /dev/null
+++ b/engines/bolt/metaengine.h
@@ -0,0 +1,43 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef BOLT_METAENGINE_H
+#define BOLT_METAENGINE_H
+
+#include "engines/advancedDetector.h"
+
+class BoltMetaEngine : public AdvancedMetaEngine<ADGameDescription> {
+public:
+	const char *getName() const override;
+
+	Common::Error createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const override;
+
+	/**
+	 * Determine whether the engine supports the specified MetaEngine feature.
+	 *
+	 * Used by e.g. the launcher to determine whether to enable the Load button.
+	 */
+	bool hasFeature(MetaEngineFeature f) const override;
+
+	const ADExtraGuiOptionsMap *getAdvancedExtraGuiOptions() const override;
+};
+
+#endif // BOLT_METAENGINE_H
diff --git a/engines/bolt/module.mk b/engines/bolt/module.mk
new file mode 100644
index 00000000000..82c0d83fe01
--- /dev/null
+++ b/engines/bolt/module.mk
@@ -0,0 +1,17 @@
+MODULE := engines/bolt
+
+MODULE_OBJS = \
+	bolt.o \
+	console.o \
+	metaengine.o
+
+# This module can be built as a plugin
+ifeq ($(ENABLE_BOLT), DYNAMIC_PLUGIN)
+PLUGIN := 1
+endif
+
+# Include common rules
+include $(srcdir)/rules.mk
+
+# Detection objects
+DETECT_OBJS += $(MODULE)/detection.o


Commit: 22990cc4ee7004cda9c105ee1716fad6af34083a
    https://github.com/scummvm/scummvm/commit/22990cc4ee7004cda9c105ee1716fad6af34083a
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-04-07T01:11:14+02:00

Commit Message:
BOLT: CARNIVAL: Initial engine function stubs

Changed paths:
  A engines/bolt/booth.cpp
  A engines/bolt/gfx.cpp
  A engines/bolt/resource.cpp
  A engines/bolt/video.cpp
  A engines/bolt/xplib/xp_cursor.cpp
  A engines/bolt/xplib/xp_events.cpp
  A engines/bolt/xplib/xp_file.cpp
  A engines/bolt/xplib/xp_gfx.cpp
  A engines/bolt/xplib/xp_mem.cpp
  A engines/bolt/xplib/xp_sound.cpp
  A engines/bolt/xplib/xplib.cpp
  A engines/bolt/xplib/xplib.h
    engines/bolt/bolt.cpp
    engines/bolt/bolt.h
    engines/bolt/module.mk


diff --git a/engines/bolt/bolt.cpp b/engines/bolt/bolt.cpp
index 9df339fdd00..19dfdd81f81 100644
--- a/engines/bolt/bolt.cpp
+++ b/engines/bolt/bolt.cpp
@@ -31,6 +31,8 @@
 #include "engines/util.h"
 #include "graphics/paletteman.h"
 
+#include "bolt/xplib/xplib.h"
+
 namespace Bolt {
 
 BoltEngine *g_engine;
@@ -38,10 +40,12 @@ BoltEngine *g_engine;
 BoltEngine::BoltEngine(OSystem *syst, const ADGameDescription *gameDesc) : Engine(syst),
 	_gameDescription(gameDesc), _randomSource("Bolt") {
 	g_engine = this;
+	_xp = new XpLib(this);
 }
 
 BoltEngine::~BoltEngine() {
 	delete _screen;
+	delete _xp;
 }
 
 uint32 BoltEngine::getFeatures() const {
@@ -57,40 +61,41 @@ Common::Error BoltEngine::run() {
 	initGraphics(320, 200);
 	_screen = new Graphics::Screen();
 
-	// Set the engine's debugger console
-	setDebugger(new Console());
-
-	// If a savegame was selected from the launcher, load it
-	int saveSlot = ConfMan.getInt("save_slot");
-	if (saveSlot != -1)
-		(void)loadGameState(saveSlot);
-
-	// Draw a series of boxes on screen as a sample
-	for (int i = 0; i < 100; ++i)
-		_screen->frameRect(Common::Rect(i, i, 320 - i, 200 - i), i);
-	_screen->update();
-
-	// Simple event handling loop
-	byte pal[256 * 3] = { 0 };
-	Common::Event e;
-	int offset = 0;
-
-	Graphics::FrameLimiter limiter(g_system, 60);
-	while (!shouldQuit()) {
-		while (g_system->getEventManager()->pollEvent(e)) {
-		}
-
-		// Cycle through a simple palette
-		++offset;
-		for (int i = 0; i < 256; ++i)
-			pal[i * 3 + 1] = (i + offset) % 256;
-		g_system->getPaletteManager()->setPalette(pal, 0, 256);
-		// Delay for a bit. All events loops should have a delay
-		// to prevent the system being unduly loaded
-		limiter.delayBeforeSwap();
-		_screen->update();
-		limiter.startFrame();
-	}
+	//// Set the engine's debugger console
+	//setDebugger(new Console());
+	//
+	//// If a savegame was selected from the launcher, load it
+	//int saveSlot = ConfMan.getInt("save_slot");
+	//if (saveSlot != -1)
+	//	(void)loadGameState(saveSlot);
+	//
+	//// Draw a series of boxes on screen as a sample
+	//for (int i = 0; i < 100; ++i)
+	//	_screen->frameRect(Common::Rect(i, i, 320 - i, 200 - i), i);
+	//_screen->update();
+	//
+	//// Simple event handling loop
+	//byte pal[256 * 3] = { 0 };
+	//Common::Event e;
+	//int offset = 0;
+	//
+	//Graphics::FrameLimiter limiter(g_system, 60);
+	//while (!shouldQuit()) {
+	//	while (g_system->getEventManager()->pollEvent(e)) {
+	//	}
+	//
+	//	// Cycle through a simple palette
+	//	++offset;
+	//	for (int i = 0; i < 256; ++i)
+	//		pal[i * 3 + 1] = (i + offset) % 256;
+	//	g_system->getPaletteManager()->setPalette(pal, 0, 256);
+	//	// Delay for a bit. All events loops should have a delay
+	//	// to prevent the system being unduly loaded
+	//	limiter.delayBeforeSwap();
+	//	_screen->update();
+	//	limiter.startFrame();
+	//}
+	boltMain();
 
 	return Common::kNoError;
 }
@@ -106,4 +111,135 @@ Common::Error BoltEngine::syncGame(Common::Serializer &s) {
 	return Common::kNoError;
 }
 
+void BoltEngine::boltMain() {
+	DisplaySpecs displaySpecs[2];
+	void *testAlloc;
+	BarkerTable *barkerTable;
+	void *boothSprite;
+
+	memcpy(displaySpecs, g_displaySpecs, 16);
+
+	// g_callbackPtr = DS:0xCB; 
+
+	testAlloc = _xp->allocMem(0x100000);
+	if (testAlloc == nullptr)
+		return;
+
+	_xp->freeMem(testAlloc);
+
+	_xp->randomize();
+
+	if (!allocResourceIndex())
+		goto cleanup;
+
+	g_boothsBoltLib = 0;
+
+	if (!openBOLTLib(AssetPath("booths.blt"), &g_boothsBoltIndex, &g_boothsBoltLib))
+		goto cleanup;
+
+	if (!_xp->chooseDisplaySpec(&g_displayMode, 2, displaySpecs))
+		goto cleanup;
+
+	g_displayWidth = displaySpecs[g_displayMode].width;
+	g_displayHeight = displaySpecs[g_displayMode].height;
+
+	// Center within 384x240 virtual coordinate space...
+	g_displayX = (384 - g_displayWidth) / 2;
+	g_displayY = (240 - g_displayHeight) / 2;
+
+	_xp->setCoordSpec(g_displayX, g_displayY, g_displayWidth, g_displayHeight);
+
+	if (g_displayMode != 0)
+		g_rtfHandle = openRTF(AssetPath("booths.pal"));
+	else
+		g_rtfHandle = openRTF(AssetPath("booths4.pal"));
+
+	if (g_rtfHandle == nullptr)
+		goto cleanup;
+
+	playAV(g_rtfHandle, 0, g_displayWidth, g_displayHeight, g_displayX, g_displayY);
+
+	boothSprite = getBOLTMember(g_boothsBoltLib, (g_displayMode != 0) ? 0x1701 : 0x1702);
+
+	_xp->setTransparency(false);
+
+	displayColors(getBOLTMember(g_boothsBoltLib, 0x1700), 0, 0);
+	displayPic(boothSprite, g_displayX, g_displayY, 0);
+
+	_xp->updateDisplay();
+
+	displayColors(getBOLTMember(g_boothsBoltLib, 0x1700), 1, 0);
+	displayPic(boothSprite, g_displayX, g_displayY, 1);
+
+	playAV(g_rtfHandle, 2, g_displayWidth, g_displayHeight, g_displayX, g_displayY);
+
+	freeBOLTGroup(g_boothsBoltLib, 0x1700, 1);
+
+	if (!getBOLTGroup(g_boothsBoltLib, 0, 1))
+		goto cleanup;
+
+	setCursorPict(memberAddr(g_boothsBoltLib, 0));
+
+	if (!initVRam(1500))
+		goto cleanup;
+
+	barkerTable = createBarker(17, 2);
+	if (barkerTable == nullptr)
+		goto cleanup;
+
+	// Register booth handlers...
+	registerSideShow(barkerTable, &BoltEngine::hucksBooth, 3);
+	registerSideShow(barkerTable, &BoltEngine::fredsBooth, 4);
+	registerSideShow(barkerTable, &BoltEngine::scoobysBooth, 5);
+	registerSideShow(barkerTable, &BoltEngine::yogisBooth, 6);
+	registerSideShow(barkerTable, &BoltEngine::georgesBooth, 7);
+	registerSideShow(barkerTable, &BoltEngine::topCatsBooth, 8);
+	registerSideShow(barkerTable, &BoltEngine::mainEntrance, 9);
+	registerSideShow(barkerTable, &BoltEngine::huckGame, 10);
+	registerSideShow(barkerTable, &BoltEngine::fredGame, 11);
+	registerSideShow(barkerTable, &BoltEngine::scoobyGame, 12);
+	registerSideShow(barkerTable, &BoltEngine::yogiGame, 13);
+	registerSideShow(barkerTable, &BoltEngine::georgeGame, 14);
+	registerSideShow(barkerTable, &BoltEngine::topCatGame, 15);
+	registerSideShow(barkerTable, &BoltEngine::winALetter, 16);
+
+	g_lettersWon = 0;
+	_xp->setScreenSaverTimer(1800);
+
+	// The barker function runs the main loop, starting at mainEntrance()...
+	if (!checkError())
+		barker(barkerTable, 9);
+
+	freeBarker(barkerTable);
+
+cleanup:
+	freeBOLTGroup(g_boothsBoltLib, 0, 1);
+
+	if (g_boothsBoltLib != 0)
+		closeBOLTLib(&g_boothsBoltLib);
+
+	if (g_rtfHandle != nullptr)
+		closeRTF(g_rtfHandle);
+
+	freeVRam();
+	freeResourceIndex();
+}
+
+BarkerTable *BoltEngine::createBarker(int16 minIndex, int16 maxIndex) {
+	return nullptr;
+}
+
+void BoltEngine::freeBarker(BarkerTable *table) {
+}
+
+void BoltEngine::registerSideShow(BarkerTable *table, SideShowHandler handler, short boothId) {
+}
+
+void BoltEngine::barker(BarkerTable *table, int16 startBooth) {
+}
+
+bool BoltEngine::checkError() {
+	return false;
+}
+
 } // End of namespace Bolt
diff --git a/engines/bolt/bolt.h b/engines/bolt/bolt.h
index 9c6b11b12d9..48e3cb8d19f 100644
--- a/engines/bolt/bolt.h
+++ b/engines/bolt/bolt.h
@@ -35,10 +35,35 @@
 #include "graphics/screen.h"
 
 #include "bolt/detection.h"
+#include "bolt/xplib/xplib.h"
 
 namespace Bolt {
 
 struct BoltGameDescription;
+class XpLib;
+
+struct DisplaySpecs {
+	int16 width;
+	int16 height;
+	int16 depth;
+	int16 unk;
+};
+
+struct BarkerTable {
+	
+};
+
+struct BOLTLib {
+	int16 refCount;
+	int16 groupCount;
+	int32 fileHandle;
+	int32 *funcTable1;
+	int32 *funcTable2;
+	int32 *dwordPtr1;
+	int32 *dwordPtr2;
+	int32 *dwordPtr3;
+	int32 *dwordPtr4;
+};
 
 class BoltEngine : public Engine {
 private:
@@ -95,6 +120,151 @@ public:
 		Common::Serializer s(stream, nullptr);
 		return syncGame(s);
 	}
+
+protected:
+	DisplaySpecs g_displaySpecs[2] = {
+		{384, 240, 1, 1},
+		{320, 200, 1, 1}
+	};
+
+	XpLib *_xp;
+
+	// Entry point
+	void boltMain();
+
+	// Booths logic
+	typedef void (BoltEngine::*SideShowHandler)(void);
+
+	int g_lettersWon = 0;
+
+	void displayBooth();
+	void playAVOverBooth();
+	void hucksBooth();
+	void fredsBooth();
+	void scoobysBooth();
+	void yogisBooth();
+	void georgesBooth();
+	void topCatsBooth();
+	void mainEntrance();
+	void huckGame();
+	void fredGame();
+	void scoobyGame();
+	void yogiGame();
+	void georgeGame();
+	void topCatGame();
+	void winALetter();
+
+	void loadBooth();
+	void unloadBooth();
+	void openBooth();
+	void closeBooth();
+	void playTour();
+	void finishPlayingHelp();
+	void hotSpotActive();
+	void hoverHotSpot();
+	void boothEventLoop();
+	void resetInactivityState();
+	void handleButtonPress();
+	void playBoothAV();
+	void mapIdleAnimation();
+	void boothIdleAnimation();
+	void screensaverStep();
+	void flushInput();
+	
+	BarkerTable *createBarker(int16 minIndex, int16 maxIndex);
+	void freeBarker(BarkerTable *table);
+	void registerSideShow(BarkerTable *table, SideShowHandler handler, short boothId);
+	void barker(BarkerTable *table, int16 startBooth);
+	bool checkError();
+
+	// Graphics
+	int g_displayMode = 0;
+	int32 g_displayX = 0;
+	int32 g_displayY = 0;
+	int32 g_displayWidth = 0;
+	int32 g_displayHeight = 0;
+
+	void setCursorPict(void *sprite);
+	void startCycle(void *cycleResource);
+	void blastColors();
+	void setColors();
+	void restoreColors();
+	void loadColors();
+	void shiftColorMap();
+	void fadeToBlack();
+	void displayColors(void *palette, int16 page, int16 flags);
+	void boltPict2Pict(void *dest, void *boltSprite);
+	void displayPic(void *boltSprite, int16 xOff, int16 yOff, int16 flags);
+	void boltCycleToXPCycle();
+	void unpackColors();
+	void inRect();
+	void rectOverlap();
+
+	// Resource handling
+	BOLTLib *g_boothsBoltLib = nullptr;
+	int g_boothsBoltIndex = 0;
+
+	#define AssetPath(x) x
+
+	bool openBOLTLib(const char *fileName, int *outIdx, BOLTLib **outLib);
+	void closeBOLTLib(BOLTLib **lib);
+	bool attemptFreeIndex(BOLTLib *lib, int16 groupId);
+	void loadGroupDirectory();
+	bool getBOLTGroup(BOLTLib *lib, int16 groupId, int16 flags);
+	void freeBOLTGroup(BOLTLib *lib, int16 groupId, int16 flags);
+	void *getBOLTMember(BOLTLib *lib, int16 resId);
+	bool freeBOLTMember(BOLTLib *lib, int16 resId);
+
+	void *memberAddr(BOLTLib *lib, int16 resId);
+	void *memberAddrOffset(BOLTLib *lib, uint32 resIdAndOffset);
+	uint32 memberSize(BOLTLib *lib, int16 resId);
+	void *groupAddr(BOLTLib *lib, int16 groupId);
+
+	bool allocResourceIndex();
+	void freeResourceIndex();
+
+	bool initVRam(int16 poolSize);
+	void freeVRam();
+	bool vLoad(void *dest, const char *name);
+	bool vSave(void *src, uint16 srcSize, const char *name);
+	bool vDelete(const char *name);
+	void memMove(void *dest, void *src, uint16 count);
+	void *dataAddress(int16 recordOffset);
+	uint16 dataSize(int16 recordOffset);
+	bool findRecord(const char *name, int16 *outOffset);
+
+	// Videos
+	void *g_rtfHandle = nullptr;
+
+	void *openRTF(const char *fileName);
+	void closeRTF(void *rtf);
+	bool playRTF(void *rtfFile, int16 animIndex, void *ringBuffer, int32 bufferSize);
+	bool fillRTFBuffer();
+	void flushRTFSoundQueue();
+	bool maintainRTF(int16 mode, void *outFrameData);
+	bool isRTFPlaying();
+	void killRTF();
+	void readPacket();
+	void preProcessPacket();
+	void queuePacket();
+	void deQueuePacket();
+	void allocPacket();
+	void freePacket();
+	void resetPlaybackState();
+	void sub_12980();
+	void prepareAV();
+	void maintainAV();
+	void stopAV();
+	bool playAV(void *rtfHandle, int16 animIndex, int16 width, int16 height, int16 xOff, int16 yOff);
+	void processPacket();
+	void processRL7();
+	void processPLTE();
+	void initAV();
+	void cleanUpAV();
+	void startAnimation();
+	void maintainAudioPlay();
+	void initAnim();
+	void cleanUpAnim();
 };
 
 extern BoltEngine *g_engine;
diff --git a/engines/bolt/booth.cpp b/engines/bolt/booth.cpp
new file mode 100644
index 00000000000..2af00442565
--- /dev/null
+++ b/engines/bolt/booth.cpp
@@ -0,0 +1,122 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "bolt/bolt.h"
+
+namespace Bolt {
+
+void BoltEngine::displayBooth() {
+}
+
+void BoltEngine::playAVOverBooth() {
+}
+
+void BoltEngine::hucksBooth() {
+}
+
+void BoltEngine::fredsBooth() {
+}
+
+void BoltEngine::scoobysBooth() {
+}
+
+void BoltEngine::yogisBooth() {
+}
+
+void BoltEngine::georgesBooth() {
+}
+
+void BoltEngine::topCatsBooth() {
+}
+
+void BoltEngine::mainEntrance() {
+}
+
+void BoltEngine::huckGame() {
+}
+
+void BoltEngine::fredGame() {
+}
+
+void BoltEngine::scoobyGame() {
+}
+
+void BoltEngine::yogiGame() {
+}
+
+void BoltEngine::georgeGame() {
+}
+
+void BoltEngine::topCatGame() {
+}
+
+void BoltEngine::winALetter() {
+}
+
+void BoltEngine::loadBooth() {
+}
+
+void BoltEngine::unloadBooth() {
+}
+
+void BoltEngine::openBooth() {
+}
+
+void BoltEngine::closeBooth() {
+}
+
+void BoltEngine::playTour() {
+}
+
+void BoltEngine::finishPlayingHelp() {
+}
+
+void BoltEngine::hotSpotActive() {
+}
+
+void BoltEngine::hoverHotSpot() {
+}
+
+void BoltEngine::boothEventLoop() {
+}
+
+void BoltEngine::resetInactivityState() {
+}
+
+void BoltEngine::handleButtonPress() {
+}
+
+void BoltEngine::playBoothAV() {
+}
+
+void BoltEngine::mapIdleAnimation() {
+}
+
+void BoltEngine::boothIdleAnimation() {
+}
+
+void BoltEngine::screensaverStep() {
+}
+
+void BoltEngine::flushInput() {
+}
+
+} // End of namespace Bolt
diff --git a/engines/bolt/gfx.cpp b/engines/bolt/gfx.cpp
new file mode 100644
index 00000000000..d9fb5e8f211
--- /dev/null
+++ b/engines/bolt/gfx.cpp
@@ -0,0 +1,71 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "bolt/bolt.h"
+
+namespace Bolt {
+
+void BoltEngine::setCursorPict(void *sprite) {
+}
+
+void BoltEngine::startCycle(void *cycleResource) {
+}
+
+void BoltEngine::blastColors() {
+}
+
+void BoltEngine::setColors() {
+}
+
+void BoltEngine::restoreColors() {
+}
+
+void BoltEngine::loadColors() {
+}
+
+void BoltEngine::shiftColorMap() {
+}
+
+void BoltEngine::fadeToBlack() {
+}
+
+void BoltEngine::displayColors(void *palette, int16 page, int16 flags) {
+}
+
+void BoltEngine::boltPict2Pict(void *dest, void *boltSprite) {
+}
+
+void BoltEngine::displayPic(void *boltSprite, int16 xOff, int16 yOff, int16 flags) {
+}
+
+void BoltEngine::boltCycleToXPCycle() {
+}
+
+void BoltEngine::unpackColors() {
+}
+
+void BoltEngine::inRect() {
+}
+
+void BoltEngine::rectOverlap() {
+}
+
+} // End of namespace Bolt
diff --git a/engines/bolt/module.mk b/engines/bolt/module.mk
index 82c0d83fe01..a0e9761d7e8 100644
--- a/engines/bolt/module.mk
+++ b/engines/bolt/module.mk
@@ -2,8 +2,19 @@ MODULE := engines/bolt
 
 MODULE_OBJS = \
 	bolt.o \
+	booth.o \
 	console.o \
-	metaengine.o
+	gfx.o \
+	metaengine.o \
+	resource.o \
+	video.o \
+	xplib\xp_cursor.o \
+	xplib\xp_events.o \
+	xplib\xp_file.o \
+	xplib\xp_gfx.o \
+	xplib\xp_mem.o \
+	xplib\xp_sound.o \
+	xplib\xplib.o
 
 # This module can be built as a plugin
 ifeq ($(ENABLE_BOLT), DYNAMIC_PLUGIN)
diff --git a/engines/bolt/resource.cpp b/engines/bolt/resource.cpp
new file mode 100644
index 00000000000..ba4f0f3d2e5
--- /dev/null
+++ b/engines/bolt/resource.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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "bolt/bolt.h"
+
+namespace Bolt {
+
+bool BoltEngine::openBOLTLib(const char *fileName, int *outIdx, BOLTLib **outLib) {
+	return false;
+}
+
+void BoltEngine::closeBOLTLib(BOLTLib **lib) {
+}
+
+bool BoltEngine::attemptFreeIndex(BOLTLib *lib, int16 groupId) {
+	return false;
+}
+
+void BoltEngine::loadGroupDirectory() {
+}
+
+bool BoltEngine::getBOLTGroup(BOLTLib *lib, int16 groupId, int16 flags) {
+	return false;
+}
+
+void BoltEngine::freeBOLTGroup(BOLTLib *lib, int16 groupId, int16 flags) {
+}
+
+void *BoltEngine::getBOLTMember(BOLTLib *lib, int16 resId) {
+	return nullptr;
+}
+
+bool BoltEngine::freeBOLTMember(BOLTLib *lib, int16 resId) {
+	return false;
+}
+
+void *BoltEngine::memberAddr(BOLTLib *lib, int16 resId) {
+	return nullptr;
+}
+
+void *BoltEngine::memberAddrOffset(BOLTLib *lib, uint32 resIdAndOffset) {
+	return nullptr;
+}
+
+uint32 BoltEngine::memberSize(BOLTLib *lib, int16 resId) {
+	return uint32();
+}
+
+void *BoltEngine::groupAddr(BOLTLib *lib, int16 groupId) {
+	return nullptr;
+}
+
+bool BoltEngine::allocResourceIndex() {
+	return false;
+}
+
+void BoltEngine::freeResourceIndex() {
+}
+
+bool BoltEngine::initVRam(int16 poolSize) {
+	return false;
+}
+
+void BoltEngine::freeVRam() {
+}
+
+bool BoltEngine::vLoad(void *dest, const char *name) {
+	return false;
+}
+
+bool BoltEngine::vSave(void *src, uint16 srcSize, const char *name) {
+	return false;
+}
+
+bool BoltEngine::vDelete(const char *name) {
+	return false;
+}
+
+void BoltEngine::memMove(void *dest, void *src, uint16 count) {
+}
+
+void *BoltEngine::dataAddress(int16 recordOffset) {
+	return nullptr;
+}
+
+uint16 BoltEngine::dataSize(int16 recordOffset) {
+	return uint16();
+}
+
+bool BoltEngine::findRecord(const char *name, int16 *outOffset) {
+	return false;
+}
+
+} // End of namespace Bolt
diff --git a/engines/bolt/video.cpp b/engines/bolt/video.cpp
new file mode 100644
index 00000000000..fb1fd5bdb65
--- /dev/null
+++ b/engines/bolt/video.cpp
@@ -0,0 +1,120 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "bolt/bolt.h"
+
+namespace Bolt {
+
+void *BoltEngine::openRTF(const char *fileName) {
+	return nullptr;
+}
+
+void BoltEngine::closeRTF(void *rtf) {
+}
+
+bool BoltEngine::playRTF(void *rtfFile, int16 animIndex, void *ringBuffer, int32 bufferSize) {
+	return false;
+}
+
+bool BoltEngine::fillRTFBuffer() {
+	return false;
+}
+
+void BoltEngine::flushRTFSoundQueue() {
+}
+
+bool BoltEngine::maintainRTF(int16 mode, void *outFrameData) {
+	return false;
+}
+
+bool BoltEngine::isRTFPlaying() {
+	return false;
+}
+
+void BoltEngine::killRTF() {
+}
+
+void BoltEngine::readPacket() {
+
+}
+
+void BoltEngine::preProcessPacket() {
+}
+
+void BoltEngine::queuePacket() {
+}
+
+void BoltEngine::deQueuePacket() {
+}
+
+void BoltEngine::allocPacket() {
+}
+
+void BoltEngine::freePacket() {
+}
+
+void BoltEngine::resetPlaybackState() {
+}
+
+void BoltEngine::sub_12980() {
+}
+
+void BoltEngine::prepareAV() {
+}
+
+void BoltEngine::maintainAV() {
+}
+
+void BoltEngine::stopAV() {
+}
+
+bool BoltEngine::playAV(void *rtfHandle, int16 animIndex, int16 width, int16 height, int16 xOff, int16 yOff) {
+	return false;
+}
+
+void BoltEngine::processPacket() {
+}
+
+void BoltEngine::processRL7() {
+}
+
+void BoltEngine::processPLTE() {
+}
+
+void BoltEngine::initAV() {
+}
+
+void BoltEngine::cleanUpAV() {
+}
+
+void BoltEngine::startAnimation() {
+}
+
+void BoltEngine::maintainAudioPlay() {
+}
+
+void BoltEngine::initAnim() {
+}
+
+void BoltEngine::cleanUpAnim() {
+}
+
+} // End of namespace Bolt
diff --git a/engines/bolt/xplib/xp_cursor.cpp b/engines/bolt/xplib/xp_cursor.cpp
new file mode 100644
index 00000000000..07d9ba64af4
--- /dev/null
+++ b/engines/bolt/xplib/xp_cursor.cpp
@@ -0,0 +1,45 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "bolt/bolt.h"
+#include "bolt/xplib/xplib.h"
+
+namespace Bolt {
+
+void XpLib::readCursor() {
+}
+
+void XpLib::setCursorPos() {
+}
+
+void XpLib::setCursorImage() {
+}
+
+void XpLib::setCursorColor() {
+}
+
+void XpLib::showCursor() {
+}
+
+void XpLib::hideCursor() {
+}
+
+} // End of namespace Bolt
diff --git a/engines/bolt/xplib/xp_events.cpp b/engines/bolt/xplib/xp_events.cpp
new file mode 100644
index 00000000000..32d5f0c5930
--- /dev/null
+++ b/engines/bolt/xplib/xp_events.cpp
@@ -0,0 +1,54 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "bolt/bolt.h"
+#include "bolt/xplib/xplib.h"
+
+namespace Bolt {
+
+void XpLib::getEvent() {
+}
+
+void XpLib::peekEvent() {
+}
+
+void XpLib::postEvent() {
+}
+
+void XpLib::enableController() {
+}
+
+void XpLib::disableController() {
+}
+
+void XpLib::setInactivityTimer() {
+}
+
+void XpLib::setScreenSaverTimer(int32 time) {
+}
+
+void XpLib::startTimer() {
+}
+
+void XpLib::killTimer() {
+}
+
+} // End of namespace Bolt
diff --git a/engines/bolt/xplib/xp_file.cpp b/engines/bolt/xplib/xp_file.cpp
new file mode 100644
index 00000000000..9c49123c13e
--- /dev/null
+++ b/engines/bolt/xplib/xp_file.cpp
@@ -0,0 +1,39 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "bolt/bolt.h"
+#include "bolt/xplib/xplib.h"
+
+namespace Bolt {
+
+void XpLib::openFile() {
+}
+
+void XpLib::closeFile() {
+}
+
+void XpLib::readFile() {
+}
+
+void XpLib::setFilePos() {
+}
+
+} // End of namespace Bolt
diff --git a/engines/bolt/xplib/xp_gfx.cpp b/engines/bolt/xplib/xp_gfx.cpp
new file mode 100644
index 00000000000..9c3f159737f
--- /dev/null
+++ b/engines/bolt/xplib/xp_gfx.cpp
@@ -0,0 +1,71 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "bolt/bolt.h"
+#include "bolt/xplib/xplib.h"
+
+namespace Bolt {
+
+void XpLib::blit() {
+
+}
+
+void XpLib::maskBlit() {
+}
+
+void XpLib::getPalette() {
+}
+
+void XpLib::setPalette() {
+}
+
+void XpLib::startCycle() {
+}
+
+void XpLib::stopCycle() {
+}
+
+void XpLib::setScreenBrightness() {
+}
+
+bool XpLib::chooseDisplaySpec(int *outMode, int numSpecs, DisplaySpecs *specs) {
+	return false;
+}
+
+void XpLib::setCoordSpec(int32 x, int32 y, int32 width, int32 height) {
+}
+
+void XpLib::displayPic() {
+}
+
+void XpLib::updateDisplay() {
+}
+
+void XpLib::setFrameRate() {
+}
+
+void XpLib::setTransparency(bool toggle) {
+}
+
+void XpLib::fillDisplay() {
+}
+
+} // End of namespace Bolt
diff --git a/engines/bolt/xplib/xp_mem.cpp b/engines/bolt/xplib/xp_mem.cpp
new file mode 100644
index 00000000000..fd636326497
--- /dev/null
+++ b/engines/bolt/xplib/xp_mem.cpp
@@ -0,0 +1,40 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "bolt/bolt.h"
+#include "bolt/xplib/xplib.h"
+
+namespace Bolt {
+
+void XpLib::setMem() {
+}
+
+byte *XpLib::allocMem(int32 size) {
+	return nullptr;
+}
+
+void XpLib::tryAllocMem() {
+}
+
+void XpLib::freeMem(void *mem) {
+}
+
+} // End of namespace Bolt
diff --git a/engines/bolt/xplib/xp_sound.cpp b/engines/bolt/xplib/xp_sound.cpp
new file mode 100644
index 00000000000..1e4835f8377
--- /dev/null
+++ b/engines/bolt/xplib/xp_sound.cpp
@@ -0,0 +1,33 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "bolt/bolt.h"
+#include "bolt/xplib/xplib.h"
+
+namespace Bolt {
+
+void XpLib::playSound() {
+}
+
+void XpLib::stopSound() {
+}
+
+} // End of namespace Bolt
diff --git a/engines/bolt/xplib/xplib.cpp b/engines/bolt/xplib/xplib.cpp
new file mode 100644
index 00000000000..681c750872a
--- /dev/null
+++ b/engines/bolt/xplib/xplib.cpp
@@ -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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "bolt/bolt.h"
+#include "bolt/xplib/xplib.h"
+
+namespace Bolt {
+
+XpLib::XpLib(BoltEngine *bolt) {
+	_bolt = bolt;
+
+	initialize();
+}
+
+XpLib::~XpLib() {
+	terminate();
+}
+
+void XpLib::initialize() {
+}
+
+void XpLib::terminate() {
+}
+
+void XpLib::swapWord() {
+}
+
+void XpLib::swapLong() {
+}
+
+void XpLib::getRandom() {
+}
+
+void XpLib::randomize() {
+}
+
+} // End of namespace Bolt
diff --git a/engines/bolt/xplib/xplib.h b/engines/bolt/xplib/xplib.h
new file mode 100644
index 00000000000..e3267840d46
--- /dev/null
+++ b/engines/bolt/xplib/xplib.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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef XPLIB_XPLIB_H
+#define XPLIB_XPLIB_H
+
+#include "bolt/bolt.h"
+
+namespace Bolt {
+
+struct DisplaySpecs;
+class BoltEngine;
+
+class XpLib {
+public:
+	XpLib(BoltEngine *bolt);
+	~XpLib();
+
+	void swapWord();
+	void swapLong();
+	void blit();
+	void maskBlit();
+	void setMem();
+	void terminate();
+	void initialize();
+	void getPalette();
+	void setPalette();
+	void startCycle();
+	void stopCycle();
+	void setScreenBrightness();
+	void readCursor();
+	void setCursorPos();
+	void setCursorImage();
+	void setCursorColor();
+	void showCursor();
+	void hideCursor();
+	void getEvent();
+	void peekEvent();
+	void postEvent();
+	void enableController();
+	void disableController();
+	void setInactivityTimer();
+	void setScreenSaverTimer(int32 time);
+	bool chooseDisplaySpec(int *outMode, int numSpecs, DisplaySpecs *specs);
+	void setCoordSpec(int32 x, int32 y, int32 width, int32 height);
+	void displayPic();
+	void updateDisplay();
+	void setFrameRate();
+	void setTransparency(bool toggle);
+	void fillDisplay();
+	void getRandom();
+	void randomize();
+	void openFile();
+	void closeFile();
+	void readFile();
+	void setFilePos();
+	byte *allocMem(int32 size);
+	void tryAllocMem();
+	void freeMem(void *mem);
+	void playSound();
+	void stopSound();
+	void startTimer();
+	void killTimer();
+
+protected:
+	BoltEngine *_bolt;
+};
+
+} // End of namespace Bolt
+
+#endif // XPLIB_XPLIB_H


Commit: f204ad4fdf2992e5392b9ccdccf2ebece8a13c42
    https://github.com/scummvm/scummvm/commit/f204ad4fdf2992e5392b9ccdccf2ebece8a13c42
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-04-07T01:11:14+02:00

Commit Message:
BOLT: Stub the entirety of the XPLIB.DLL code

Changed paths:
  A engines/bolt/anim.cpp
  A engines/bolt/xplib/blit.cpp
  A engines/bolt/xplib/cursor.cpp
  A engines/bolt/xplib/display.cpp
  A engines/bolt/xplib/events.cpp
  A engines/bolt/xplib/file.cpp
  A engines/bolt/xplib/mem.cpp
  A engines/bolt/xplib/palette.cpp
  A engines/bolt/xplib/random.cpp
  A engines/bolt/xplib/sound.cpp
  A engines/bolt/xplib/timer.cpp
  R engines/bolt/video.cpp
  R engines/bolt/xplib/xp_cursor.cpp
  R engines/bolt/xplib/xp_events.cpp
  R engines/bolt/xplib/xp_file.cpp
  R engines/bolt/xplib/xp_gfx.cpp
  R engines/bolt/xplib/xp_mem.cpp
  R engines/bolt/xplib/xp_sound.cpp
    engines/bolt/bolt.cpp
    engines/bolt/bolt.h
    engines/bolt/module.mk
    engines/bolt/xplib/xplib.cpp
    engines/bolt/xplib/xplib.h


diff --git a/engines/bolt/video.cpp b/engines/bolt/anim.cpp
similarity index 100%
rename from engines/bolt/video.cpp
rename to engines/bolt/anim.cpp
diff --git a/engines/bolt/bolt.cpp b/engines/bolt/bolt.cpp
index 19dfdd81f81..b358bb00c26 100644
--- a/engines/bolt/bolt.cpp
+++ b/engines/bolt/bolt.cpp
@@ -122,7 +122,7 @@ void BoltEngine::boltMain() {
 	// g_callbackPtr = DS:0xCB; 
 
 	testAlloc = _xp->allocMem(0x100000);
-	if (testAlloc == nullptr)
+	if (!testAlloc)
 		return;
 
 	_xp->freeMem(testAlloc);
diff --git a/engines/bolt/bolt.h b/engines/bolt/bolt.h
index 48e3cb8d19f..3fddfafb3b5 100644
--- a/engines/bolt/bolt.h
+++ b/engines/bolt/bolt.h
@@ -66,6 +66,8 @@ struct BOLTLib {
 };
 
 class BoltEngine : public Engine {
+friend class XpLib;
+
 private:
 	const ADGameDescription *_gameDescription;
 	Common::RandomSource _randomSource;
diff --git a/engines/bolt/module.mk b/engines/bolt/module.mk
index a0e9761d7e8..b4ef08ebf3b 100644
--- a/engines/bolt/module.mk
+++ b/engines/bolt/module.mk
@@ -1,19 +1,23 @@
 MODULE := engines/bolt
 
 MODULE_OBJS = \
+	anim.o \
 	bolt.o \
 	booth.o \
 	console.o \
 	gfx.o \
 	metaengine.o \
 	resource.o \
-	video.o \
-	xplib\xp_cursor.o \
-	xplib\xp_events.o \
-	xplib\xp_file.o \
-	xplib\xp_gfx.o \
-	xplib\xp_mem.o \
-	xplib\xp_sound.o \
+	xplib\blit.o \
+	xplib\cursor.o \
+	xplib\display.o \
+	xplib\events.o \
+	xplib\file.o \
+	xplib\mem.o \
+	xplib\palette.o \
+	xplib\random.o \
+	xplib\sound.o \
+	xplib\timer.o \
 	xplib\xplib.o
 
 # This module can be built as a plugin
diff --git a/engines/bolt/xplib/blit.cpp b/engines/bolt/xplib/blit.cpp
new file mode 100644
index 00000000000..4237ed71a67
--- /dev/null
+++ b/engines/bolt/xplib/blit.cpp
@@ -0,0 +1,63 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "bolt/bolt.h"
+#include "bolt/xplib/xplib.h"
+
+namespace Bolt {
+
+void XpLib::blit(void *src, uint16 srcStride, void *dst, uint16 dstStride, uint16 width, uint16 height) {
+
+}
+
+void XpLib::dirtyBlit(void *src, void *dst, void *dirtyFlags, uint16 width, uint16 height) {
+
+}
+
+void XpLib::maskBlit(void *src, uint16 srcStride, void *dst, uint16 dstStride, uint16 width, uint16 height) {
+
+}
+
+uint32 XpLib::compositeBlit(void *src, void *background, void *dst, uint16 stride, uint16 width, uint16 height) {
+	return 0;
+}
+
+void XpLib::rleBlit(void *src, void *dst, uint16 dstStride, uint16 width, uint16 height) {
+
+}
+
+void XpLib::rleMaskBlit(void *src, void *dst, uint16 dstStride, uint16 width, uint16 height) {
+
+}
+
+uint32 XpLib::rleCompositeBlit(void *rle, void *background, void *dst, uint16 width, uint16 height, void *dirtyFlags) {
+	return 0;
+}
+
+uint16 XpLib::rleDataSize(void *rleData, uint16 height) {
+	return 0;
+}
+
+void XpLib::markCursorPixels(void *buffer, uint32 count) {
+
+}
+
+} // End of namespace Bolt
diff --git a/engines/bolt/xplib/xp_gfx.cpp b/engines/bolt/xplib/cursor.cpp
similarity index 66%
rename from engines/bolt/xplib/xp_gfx.cpp
rename to engines/bolt/xplib/cursor.cpp
index 9c3f159737f..923f8fb76ef 100644
--- a/engines/bolt/xplib/xp_gfx.cpp
+++ b/engines/bolt/xplib/cursor.cpp
@@ -24,48 +24,37 @@
 
 namespace Bolt {
 
-void XpLib::blit() {
-
+bool XpLib::initCoords() {
+	return false;
 }
 
-void XpLib::maskBlit() {
+void XpLib::shutdownCoords() {
 }
 
-void XpLib::getPalette() {
+bool XpLib::readCursor(uint16 *outButtons, int16 *outX, int16 *outY) {
+	return false;
 }
 
-void XpLib::setPalette() {
+void XpLib::readJoystick(int16 *outX, int16 *outY) {
 }
 
-void XpLib::startCycle() {
+void XpLib::setCursorPos(int16 x, int16 y) {
 }
 
-void XpLib::stopCycle() {
+void XpLib::setCursorImage(void *bitmap, int16 hotspotX, int16 hotspotY) {
 }
 
-void XpLib::setScreenBrightness() {
+void XpLib::setCursorColor(byte r, byte g, byte b) {
 }
 
-bool XpLib::chooseDisplaySpec(int *outMode, int numSpecs, DisplaySpecs *specs) {
+bool XpLib::showCursor() {
 	return false;
 }
 
-void XpLib::setCoordSpec(int32 x, int32 y, int32 width, int32 height) {
-}
-
-void XpLib::displayPic() {
-}
-
-void XpLib::updateDisplay() {
-}
-
-void XpLib::setFrameRate() {
-}
-
-void XpLib::setTransparency(bool toggle) {
+void XpLib::hideCursor() {
 }
 
-void XpLib::fillDisplay() {
+void XpLib::updateCursorPosition() {
 }
 
 } // End of namespace Bolt
diff --git a/engines/bolt/xplib/display.cpp b/engines/bolt/xplib/display.cpp
new file mode 100644
index 00000000000..cc779d2ba07
--- /dev/null
+++ b/engines/bolt/xplib/display.cpp
@@ -0,0 +1,110 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "bolt/bolt.h"
+#include "bolt/xplib/xplib.h"
+
+namespace Bolt {
+
+int16 XpLib::switchDisplayMode(int16 mode) {
+	return int16();
+}
+
+bool XpLib::initDisplay() {
+	return false;
+}
+
+void XpLib::shutdownDisplay() {
+}
+
+bool XpLib::createSurface(XPSurface *surf) {
+	return false;
+}
+
+void XpLib::freeSurface(XPSurface *surf) {
+}
+
+bool XpLib::chooseDisplaySpec(int *outMode, int numSpecs, DisplaySpecs *specs) {
+	return false;
+}
+
+void XpLib::setCoordSpec(int16 x, int16 y, int16 width, int16 height) {
+}
+
+void XpLib::virtualToScreen(int16 *x, int16 *y) {
+}
+
+void XpLib::screenToVirtual(int16 *x, int16 *y) {
+}
+
+void XpLib::displayPic(XPPicDesc *pic, int16 x, int16 y, int16 page) {
+}
+
+bool XpLib::clipAndBlit(XPPicDesc *src, XPSurface *dest, int16 x, int16 y, ClipRect *outClip) {
+	return false;
+}
+
+void XpLib::addDirtyRect(ClipRect *rect) {
+}
+
+void XpLib::setFrameRate(int16 fps) {
+}
+
+void XpLib::updateDisplay() {
+}
+
+void XpLib::waitForFrameRate() {
+}
+
+void XpLib::handlePaletteTransitions() {
+}
+
+void XpLib::flushPalette() {
+}
+
+void XpLib::overlayComposite() {
+}
+
+void XpLib::compositeToScreen() {
+}
+
+void XpLib::mergeDirtyRects() {
+}
+
+void XpLib::blitDirtyRects(ClipRect *rects, int16 count) {
+}
+
+void XpLib::compositeDirtyRects(ClipRect *rects, int16 count) {
+}
+
+void XpLib::applyCursorPalette(bool enable) {
+}
+
+void XpLib::prepareBackSurface() {
+}
+
+void XpLib::setTransparency(bool toggle) {
+}
+
+void XpLib::fillDisplay(byte color, int16 page) {
+}
+
+} // End of namespace Bolt
diff --git a/engines/bolt/xplib/events.cpp b/engines/bolt/xplib/events.cpp
new file mode 100644
index 00000000000..77650c75153
--- /dev/null
+++ b/engines/bolt/xplib/events.cpp
@@ -0,0 +1,119 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "bolt/bolt.h"
+#include "bolt/xplib/xplib.h"
+
+namespace Bolt {
+
+bool XpLib::initWindow() {
+	return false;
+}
+
+void XpLib::shutdownWindow() {
+
+}
+
+int16 XpLib::getEvent(int16 filter, uint32 *outData) {
+	return 0;
+}
+
+int16 XpLib::peekEvent(int16 filter, uint32 *outData) {
+	return 0;
+}
+
+void XpLib::unlinkEvent(int16 node) {
+
+}
+
+void XpLib::enqueueEvent(int16 node) {
+
+}
+
+void XpLib::pumpMessages() {
+
+}
+
+void XpLib::handleTimer(uint32 timerId) {
+
+}
+
+void XpLib::handleMouseMove(bool *mouseMoved) {
+
+}
+
+void XpLib::handleMouseButton(int16 down, int16 button) {
+
+}
+
+void XpLib::handleKey(int16 vkey, int16 down) {
+
+}
+
+void XpLib::postJoystickEvent(int16 source, int16 dx, int16 dy) {
+
+}
+
+void XpLib::postEvent(int16 type, uint32 data) {
+
+}
+
+bool XpLib::canDropEvent(int16 type) {
+	return false;
+}
+
+int16 XpLib::setInactivityTimer(int16 seconds) {
+	return 0;
+}
+
+int16 XpLib::setScreenSaverTimer(int16 time) {
+	return 0;
+}
+
+void XpLib::activateScreenSaver() {
+
+}
+
+void XpLib::resetInactivity() {
+
+}
+
+bool XpLib::enableController() {
+	return false;
+}
+
+void XpLib::disableController() {
+
+}
+
+void XpLib::enableMouse() {
+
+}
+
+void XpLib::disableMouse() {
+
+}
+
+int16 XpLib::getButtonState() {
+	return 0;
+}
+
+} // End of namespace Bolt
diff --git a/engines/bolt/xplib/file.cpp b/engines/bolt/xplib/file.cpp
new file mode 100644
index 00000000000..6a06ad897a6
--- /dev/null
+++ b/engines/bolt/xplib/file.cpp
@@ -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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "bolt/bolt.h"
+#include "bolt/xplib/xplib.h"
+
+namespace Bolt {
+
+void XpLib::fileError(const char *message) {
+}
+
+int32 XpLib::createFile(const char *fileName) {
+	return int32();
+}
+
+void XpLib::deleteFile(const char *fileName) {
+}
+
+int32 XpLib::openFile(const char *fileName, short flags) {
+	return int32();
+}
+
+void XpLib::closeFile(int32 handle) {
+}
+
+bool XpLib::readFile(int32 handle, void *buffer, uint32 *size) {
+	return false;
+}
+
+bool XpLib::setFilePos(int32 handle, uint32 offset, int16 origin) {
+	return false;
+}
+
+void *XpLib::allocMem(uint32 size) {
+	void *result = malloc(size);
+	if (!result)
+		error("XpLib::allocMem(): Not enough memory");
+
+	return result;
+}
+
+void *XpLib::tryAllocMem(uint32 size) {
+	void *result = (void *)malloc(size);
+	if (!result)
+		warning("XpLib::tryAllocMem(): Couldn't allocate memory, returning nullptr");
+
+	return result;
+}
+
+void XpLib::freeMem(void *mem) {
+	free(mem);
+}
+
+} // End of namespace Bolt
diff --git a/engines/bolt/xplib/xp_sound.cpp b/engines/bolt/xplib/mem.cpp
similarity index 94%
rename from engines/bolt/xplib/xp_sound.cpp
rename to engines/bolt/xplib/mem.cpp
index 1e4835f8377..fe512e1b830 100644
--- a/engines/bolt/xplib/xp_sound.cpp
+++ b/engines/bolt/xplib/mem.cpp
@@ -24,10 +24,4 @@
 
 namespace Bolt {
 
-void XpLib::playSound() {
-}
-
-void XpLib::stopSound() {
-}
-
 } // End of namespace Bolt
diff --git a/engines/bolt/xplib/xp_events.cpp b/engines/bolt/xplib/palette.cpp
similarity index 76%
rename from engines/bolt/xplib/xp_events.cpp
rename to engines/bolt/xplib/palette.cpp
index 32d5f0c5930..81c6b5534f1 100644
--- a/engines/bolt/xplib/xp_events.cpp
+++ b/engines/bolt/xplib/palette.cpp
@@ -24,31 +24,24 @@
 
 namespace Bolt {
 
-void XpLib::getEvent() {
-}
-
-void XpLib::peekEvent() {
-}
-
-void XpLib::postEvent() {
-}
 
-void XpLib::enableController() {
+void XpLib::getPalette(uint16 startIndex, uint16 count, void *destBuf) {
 }
 
-void XpLib::disableController() {
+void XpLib::setPalette(uint16 count, uint16 startIndex, void *srcBu) {
 }
 
-void XpLib::setInactivityTimer() {
+bool XpLib::startCycle(XPCycleSpec *specs) {
+	return false;
 }
 
-void XpLib::setScreenSaverTimer(int32 time) {
+void XpLib::cycleColors() {
 }
 
-void XpLib::startTimer() {
+void XpLib::stopCycle() {
 }
 
-void XpLib::killTimer() {
+void XpLib::setScreenBrightness(uint8 percent) {
 }
 
 } // End of namespace Bolt
diff --git a/engines/bolt/xplib/xp_mem.cpp b/engines/bolt/xplib/random.cpp
similarity index 82%
rename from engines/bolt/xplib/xp_mem.cpp
rename to engines/bolt/xplib/random.cpp
index fd636326497..412c0d47861 100644
--- a/engines/bolt/xplib/xp_mem.cpp
+++ b/engines/bolt/xplib/random.cpp
@@ -24,17 +24,12 @@
 
 namespace Bolt {
 
-void XpLib::setMem() {
+int16 XpLib::getRandom(int16 range) {
+	return (int16)(((uint32)_bolt->_randomSource.getRandomNumber(UINT_MAX) * (uint32)range) / 0x8000);
 }
 
-byte *XpLib::allocMem(int32 size) {
-	return nullptr;
-}
-
-void XpLib::tryAllocMem() {
-}
-
-void XpLib::freeMem(void *mem) {
+void XpLib::randomize() {
+	_bolt->_randomSource.generateNewSeed();
 }
 
 } // End of namespace Bolt
diff --git a/engines/bolt/xplib/xp_file.cpp b/engines/bolt/xplib/sound.cpp
similarity index 72%
rename from engines/bolt/xplib/xp_file.cpp
rename to engines/bolt/xplib/sound.cpp
index 9c49123c13e..b1a26a29671 100644
--- a/engines/bolt/xplib/xp_file.cpp
+++ b/engines/bolt/xplib/sound.cpp
@@ -24,16 +24,36 @@
 
 namespace Bolt {
 
-void XpLib::openFile() {
+bool XpLib::pollSound(void *outData) {
+	return false;
 }
 
-void XpLib::closeFile() {
+bool XpLib::initSound() {
+	return false;
 }
 
-void XpLib::readFile() {
+void XpLib::waveCb() {
+
+}
+
+void XpLib::shutdownSound() {
+
+}
+
+bool XpLib::playSound(void *data, uint32 size, int16 sampleRate) {
+	return false;
+}
+
+bool XpLib::pauseSound() {
+	return false;
+}
+
+bool XpLib::resumeSound() {
+	return false;
 }
 
-void XpLib::setFilePos() {
+bool XpLib::stopSound() {
+	return false;
 }
 
 } // End of namespace Bolt
diff --git a/engines/bolt/xplib/xp_cursor.cpp b/engines/bolt/xplib/timer.cpp
similarity index 83%
rename from engines/bolt/xplib/xp_cursor.cpp
rename to engines/bolt/xplib/timer.cpp
index 07d9ba64af4..9b8d710f982 100644
--- a/engines/bolt/xplib/xp_cursor.cpp
+++ b/engines/bolt/xplib/timer.cpp
@@ -24,22 +24,22 @@
 
 namespace Bolt {
 
-void XpLib::readCursor() {
+bool XpLib::initTimer() {
+	return false;
 }
 
-void XpLib::setCursorPos() {
+void XpLib::shutdownTimer() {
 }
 
-void XpLib::setCursorImage() {
+int32 XpLib::startTimer(int16 delay) {
+	return 0;
 }
 
-void XpLib::setCursorColor() {
+void XpLib::timeCb() {
 }
 
-void XpLib::showCursor() {
-}
-
-void XpLib::hideCursor() {
+bool XpLib::killTimer(int32 timerId) {
+	return false;
 }
 
 } // End of namespace Bolt
diff --git a/engines/bolt/xplib/xplib.cpp b/engines/bolt/xplib/xplib.cpp
index 681c750872a..4cdc6f476bb 100644
--- a/engines/bolt/xplib/xplib.cpp
+++ b/engines/bolt/xplib/xplib.cpp
@@ -40,16 +40,4 @@ void XpLib::initialize() {
 void XpLib::terminate() {
 }
 
-void XpLib::swapWord() {
-}
-
-void XpLib::swapLong() {
-}
-
-void XpLib::getRandom() {
-}
-
-void XpLib::randomize() {
-}
-
 } // End of namespace Bolt
diff --git a/engines/bolt/xplib/xplib.h b/engines/bolt/xplib/xplib.h
index e3267840d46..fd4031a5c35 100644
--- a/engines/bolt/xplib/xplib.h
+++ b/engines/bolt/xplib/xplib.h
@@ -29,59 +29,186 @@ namespace Bolt {
 struct DisplaySpecs;
 class BoltEngine;
 
+typedef struct XPCycleSpec {
+	short startIndex; // +0x00: first palette index
+	short endIndex;   // +0x02: last palette index
+	short delay;      // +0x04: cycle period in ms
+} XPCycleSpec;        // 6 bytes
+
+typedef struct XPSurface {
+	byte *pixelData;     // +0x00: pixel buffer (width * height bytes, 8bpp)
+	int16 width;         // +0x04: surface width
+	int16 height;        // +0x06: surface height
+	byte *palette;       // +0x08: RGB palette data (127 * 3 = 381 bytes)
+	int16 paletteStart;  // +0x0C: first palette index (1)
+	int16 paletteCount;  // +0x0E: number of palette entries (127)
+	int16 flags;         // +0x10
+	long overlayData;    // +0x12
+	char _pad[0x10];     // +0x16..+0x23 (unknown)
+	int16 clipX;         // +0x24
+	int16 clipY;         // +0x26
+} XPSurface;             // total: 0x28 = 40 bytes
+
+typedef struct XPPicDesc {
+	byte *pixelData;     // +0x00: pixel buffer (8bpp)
+	int16 width;         // +0x04: image width
+	int16 height;        // +0x06: image height
+	byte *palette;       // +0x08: RGB palette data
+	int16 paletteStart;  // +0x0C: first palette index
+	int16 paletteCount;  // +0x0E: number of palette entries
+	int16 flags;         // +0x10: bit 0 = transparent, bit 1 = RLE
+} XPPicDesc;             // 0x12 = 18 bytes
+
+typedef void (*BlitFunc)(void *src, int16 srcStride, void *dest, int16 destStride, int16 width, int16 height);
+
+typedef struct ClipRect {
+	short left;
+	short top;
+	short right;
+	short bottom;
+} ClipRect;
+
 class XpLib {
 public:
 	XpLib(BoltEngine *bolt);
 	~XpLib();
 
-	void swapWord();
-	void swapLong();
-	void blit();
-	void maskBlit();
-	void setMem();
-	void terminate();
 	void initialize();
-	void getPalette();
-	void setPalette();
-	void startCycle();
+	void terminate();
+
+	// Blit
+	void blit(void *src, uint16 srcStride, void *dst, uint16 dstStride, uint16 width, uint16 height);
+	void maskBlit(void *src, uint16 srcStride, void *dst, uint16 dstStride, uint16 width, uint16 height);
+
+	// Palette
+	void getPalette(uint16 startIndex, uint16 count, void *destBuf);
+	void setPalette(uint16 count, uint16 startIndex, void *srcBuf);
+	bool startCycle(XPCycleSpec *specs);
+	void cycleColors();
 	void stopCycle();
-	void setScreenBrightness();
-	void readCursor();
-	void setCursorPos();
-	void setCursorImage();
-	void setCursorColor();
-	void showCursor();
+	void setScreenBrightness(uint8 percent);
+
+	// Cursor
+	bool readCursor(uint16 *outButtons, int16 *outX, int16 *outY);
+	void setCursorPos(int16 x, int16 y);
+	void setCursorImage(void *bitmap, int16 hotspotX, int16 hotspotY);
+	void setCursorColor(byte r, byte g, byte b);
+	bool showCursor();
 	void hideCursor();
-	void getEvent();
-	void peekEvent();
-	void postEvent();
-	void enableController();
+	void updateCursorPosition();
+
+	// Events
+	int16 getEvent(int16 filter, uint32 *outData);
+	int16 peekEvent(int16 filter, uint32 *outData);
+	void postEvent(int16 type, uint32 data);
+	int16 setInactivityTimer(int16 seconds);
+	int16 setScreenSaverTimer(int16 time);
+	bool enableController();
 	void disableController();
-	void setInactivityTimer();
-	void setScreenSaverTimer(int32 time);
+
+	// Display
 	bool chooseDisplaySpec(int *outMode, int numSpecs, DisplaySpecs *specs);
-	void setCoordSpec(int32 x, int32 y, int32 width, int32 height);
-	void displayPic();
+	void setCoordSpec(int16 x, int16 y, int16 width, int16 height);
+	void displayPic(XPPicDesc *pic, int16 x, int16 y, int16 page);
+	void setFrameRate(int16 fps);
 	void updateDisplay();
-	void setFrameRate();
 	void setTransparency(bool toggle);
-	void fillDisplay();
-	void getRandom();
+	void fillDisplay(byte color, int16 page);
+
+	// Random
+	int16 getRandom(int16 range);
 	void randomize();
-	void openFile();
-	void closeFile();
-	void readFile();
-	void setFilePos();
-	byte *allocMem(int32 size);
-	void tryAllocMem();
+
+	// File
+	int32 createFile(const char *fileName);
+	void deleteFile(const char *fileName);
+	int32 openFile(const char *fileName, short flags);
+	void closeFile(int32 handle);
+	bool readFile(int32 handle, void *buffer, uint32 *size);
+	bool setFilePos(int32 handle, uint32 offset, int16 origin);
+	void *allocMem(uint32 size);
+	void *tryAllocMem(uint32 size);
 	void freeMem(void *mem);
-	void playSound();
-	void stopSound();
-	void startTimer();
-	void killTimer();
+
+	// Sound
+	void waveCb();
+	bool playSound(void *data, uint32 size, int16 sampleRate);
+	bool pauseSound();
+	bool resumeSound();
+	bool stopSound();
+
+	// Timer
+	int32 startTimer(int16 delay);
+	void timeCb();
+	bool killTimer(int32 timerId);
 
 protected:
 	BoltEngine *_bolt;
+
+	// Blit
+	void dirtyBlit(void *src, void *dst, void *dirtyFlags, uint16 width, uint16 height);
+	uint32 compositeBlit(void *src, void *background, void *dst, uint16 stride, uint16 width, uint16 height);
+	void rleBlit(void *src, void *dst, uint16 dstStride, uint16 width, uint16 height);
+	void rleMaskBlit(void *src, void *dst, uint16 dstStride, uint16 width, uint16 height);
+	uint32 rleCompositeBlit(void *rle, void *background, void *dst, uint16 width, uint16 height, void *dirtyFlags);
+	uint16 rleDataSize(void *rleData, uint16 height);
+	void markCursorPixels(void *buffer, uint32 count);
+
+	// Cursor
+	bool initCoords();
+	void shutdownCoords();
+	void readJoystick(int16 *outX, int16 *outY);
+
+	// Events
+	bool initWindow();
+	void shutdownWindow();
+	void unlinkEvent(int16 node);
+	void enqueueEvent(int16 node);
+	void pumpMessages();
+	void handleTimer(uint32 timerId);
+	void handleMouseMove(bool *mouseMoved);
+	void handleMouseButton(int16 down, int16 button);
+	void handleKey(int16 vkey, int16 down);
+	void postJoystickEvent(int16 source, int16 dx, int16 dy);
+	bool canDropEvent(int16 type);
+	void activateScreenSaver();
+	void resetInactivity();
+	void enableMouse();
+	void disableMouse();
+	int16 getButtonState();
+
+	// Display
+	int16 switchDisplayMode(int16 mode);
+	bool initDisplay();
+	void shutdownDisplay();
+	bool createSurface(XPSurface *surf);
+	void freeSurface(XPSurface *surf);
+	void virtualToScreen(int16 *x, int16 *y);
+	void screenToVirtual(int16 *x, int16 *y);
+	bool clipAndBlit(XPPicDesc *src, XPSurface *dest, int16 x, int16 y, ClipRect *outClip);
+	void addDirtyRect(ClipRect *rect);
+	void waitForFrameRate();
+	void handlePaletteTransitions();
+	void flushPalette();
+	void overlayComposite();
+	void compositeToScreen();
+	void mergeDirtyRects();
+	void blitDirtyRects(ClipRect *rects, int16 count);
+	void compositeDirtyRects(ClipRect *rects, int16 count);
+	void applyCursorPalette(bool enable);
+	void prepareBackSurface();
+
+	// File
+	void fileError(const char *message);
+
+	// Sound
+	bool pollSound(void *outData);
+	bool initSound();
+	void shutdownSound();
+
+	// Timer
+	bool initTimer();
+	void shutdownTimer();
 };
 
 } // End of namespace Bolt


Commit: 27451337381673857357aa4a70331a173abf818f
    https://github.com/scummvm/scummvm/commit/27451337381673857357aa4a70331a173abf818f
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-04-07T01:11:14+02:00

Commit Message:
BOLT: Implement the entirety of XPLIB.DLL except sound

Changed paths:
  R engines/bolt/xplib/mem.cpp
    engines/bolt/bolt.cpp
    engines/bolt/bolt.h
    engines/bolt/module.mk
    engines/bolt/resource.cpp
    engines/bolt/xplib/blit.cpp
    engines/bolt/xplib/cursor.cpp
    engines/bolt/xplib/display.cpp
    engines/bolt/xplib/events.cpp
    engines/bolt/xplib/file.cpp
    engines/bolt/xplib/palette.cpp
    engines/bolt/xplib/sound.cpp
    engines/bolt/xplib/timer.cpp
    engines/bolt/xplib/xplib.cpp
    engines/bolt/xplib/xplib.h


diff --git a/engines/bolt/bolt.cpp b/engines/bolt/bolt.cpp
index b358bb00c26..e4683cdf938 100644
--- a/engines/bolt/bolt.cpp
+++ b/engines/bolt/bolt.cpp
@@ -44,7 +44,6 @@ BoltEngine::BoltEngine(OSystem *syst, const ADGameDescription *gameDesc) : Engin
 }
 
 BoltEngine::~BoltEngine() {
-	delete _screen;
 	delete _xp;
 }
 
@@ -57,12 +56,21 @@ Common::String BoltEngine::getGameId() const {
 }
 
 Common::Error BoltEngine::run() {
-	// Initialize 320x200 paletted graphics mode
-	initGraphics(320, 200);
-	_screen = new Graphics::Screen();
+	ConfMan.registerDefault("extended_viewport", false);
+	if (ConfMan.hasKey("extended_viewport", _targetName)) {
+		g_extendedViewport = ConfMan.getBool("extended_viewport");
+	}
+
+	// Initialize paletted graphics mode
+	if (g_extendedViewport) {
+		initGraphics(SCREEN_WIDTH, SCREEN_HEIGHT);
+	} else {
+		initGraphics(EXTENDED_SCREEN_WIDTH, EXTENDED_SCREEN_HEIGHT);
+	}
+
+	// Set the engine's debugger console
+	setDebugger(new Console());
 
-	//// Set the engine's debugger console
-	//setDebugger(new Console());
 	//
 	//// If a savegame was selected from the launcher, load it
 	//int saveSlot = ConfMan.getInt("save_slot");
@@ -95,6 +103,8 @@ Common::Error BoltEngine::run() {
 	//	_screen->update();
 	//	limiter.startFrame();
 	//}
+
+	_xp->initialize();
 	boltMain();
 
 	return Common::kNoError;
@@ -112,13 +122,10 @@ Common::Error BoltEngine::syncGame(Common::Serializer &s) {
 }
 
 void BoltEngine::boltMain() {
-	DisplaySpecs displaySpecs[2];
 	void *testAlloc;
 	BarkerTable *barkerTable;
 	void *boothSprite;
 
-	memcpy(displaySpecs, g_displaySpecs, 16);
-
 	// g_callbackPtr = DS:0xCB; 
 
 	testAlloc = _xp->allocMem(0x100000);
@@ -134,14 +141,14 @@ void BoltEngine::boltMain() {
 
 	g_boothsBoltLib = 0;
 
-	if (!openBOLTLib(AssetPath("booths.blt"), &g_boothsBoltIndex, &g_boothsBoltLib))
+	if (!openBOLTLib(&g_boothsBoltLib, &g_boothsBoltIndex, AssetPath("booths.blt")))
 		goto cleanup;
 
-	if (!_xp->chooseDisplaySpec(&g_displayMode, 2, displaySpecs))
+	if (!_xp->chooseDisplaySpec(&g_displayMode, 2, g_displaySpecs))
 		goto cleanup;
 
-	g_displayWidth = displaySpecs[g_displayMode].width;
-	g_displayHeight = displaySpecs[g_displayMode].height;
+	g_displayWidth = g_displaySpecs[g_displayMode].width;
+	g_displayHeight = g_displaySpecs[g_displayMode].height;
 
 	// Center within 384x240 virtual coordinate space...
 	g_displayX = (384 - g_displayWidth) / 2;
diff --git a/engines/bolt/bolt.h b/engines/bolt/bolt.h
index 3fddfafb3b5..4042d3f725b 100644
--- a/engines/bolt/bolt.h
+++ b/engines/bolt/bolt.h
@@ -39,14 +39,17 @@
 
 namespace Bolt {
 
+#define SCREEN_WIDTH           320
+#define SCREEN_HEIGHT          200
+#define EXTENDED_SCREEN_WIDTH  384
+#define EXTENDED_SCREEN_HEIGHT 240
+
 struct BoltGameDescription;
 class XpLib;
 
 struct DisplaySpecs {
 	int16 width;
 	int16 height;
-	int16 depth;
-	int16 unk;
 };
 
 struct BarkerTable {
@@ -74,8 +77,7 @@ private:
 protected:
 	// Engine APIs
 	Common::Error run() override;
-public:
-	Graphics::Screen *_screen = nullptr;
+
 public:
 	BoltEngine(OSystem *syst, const ADGameDescription *gameDesc);
 	~BoltEngine() override;
@@ -125,11 +127,12 @@ public:
 
 protected:
 	DisplaySpecs g_displaySpecs[2] = {
-		{384, 240, 1, 1},
-		{320, 200, 1, 1}
+		{384, 240},
+		{320, 200}
 	};
 
 	XpLib *_xp;
+	bool g_extendedViewport = false;
 
 	// Entry point
 	void boltMain();
@@ -205,10 +208,12 @@ protected:
 	// Resource handling
 	BOLTLib *g_boothsBoltLib = nullptr;
 	int g_boothsBoltIndex = 0;
+	int16 g_resourceIndexCount = 1000;
+	uint32 *g_resourceIndex = nullptr;
 
 	#define AssetPath(x) x
 
-	bool openBOLTLib(const char *fileName, int *outIdx, BOLTLib **outLib);
+	bool openBOLTLib(BOLTLib **outLib, int *outIdx, const char *fileName);
 	void closeBOLTLib(BOLTLib **lib);
 	bool attemptFreeIndex(BOLTLib *lib, int16 groupId);
 	void loadGroupDirectory();
diff --git a/engines/bolt/module.mk b/engines/bolt/module.mk
index b4ef08ebf3b..4f9c5b5ac04 100644
--- a/engines/bolt/module.mk
+++ b/engines/bolt/module.mk
@@ -13,7 +13,6 @@ MODULE_OBJS = \
 	xplib\display.o \
 	xplib\events.o \
 	xplib\file.o \
-	xplib\mem.o \
 	xplib\palette.o \
 	xplib\random.o \
 	xplib\sound.o \
diff --git a/engines/bolt/resource.cpp b/engines/bolt/resource.cpp
index ba4f0f3d2e5..e1204a58c00 100644
--- a/engines/bolt/resource.cpp
+++ b/engines/bolt/resource.cpp
@@ -23,7 +23,7 @@
 
 namespace Bolt {
 
-bool BoltEngine::openBOLTLib(const char *fileName, int *outIdx, BOLTLib **outLib) {
+bool BoltEngine::openBOLTLib(BOLTLib **outLib, int *outIdx, const char *fileName) {
 	return false;
 }
 
diff --git a/engines/bolt/xplib/blit.cpp b/engines/bolt/xplib/blit.cpp
index 4237ed71a67..d7aae972728 100644
--- a/engines/bolt/xplib/blit.cpp
+++ b/engines/bolt/xplib/blit.cpp
@@ -24,40 +24,236 @@
 
 namespace Bolt {
 
-void XpLib::blit(void *src, uint16 srcStride, void *dst, uint16 dstStride, uint16 width, uint16 height) {
+void XpLib::blit(byte *src, uint16 srcStride, byte *dst, uint16 dstStride, uint16 width, uint16 height) {
+	int16 srcSkip = srcStride - width;
+	int16 dstSkip = dstStride - width;
 
+	for (int16 row = 0; row < height; row++) {
+		memcpy(dst, src, width);
+		dst += dstSkip + width;
+		src += srcSkip + width;
+	}
 }
 
-void XpLib::dirtyBlit(void *src, void *dst, void *dirtyFlags, uint16 width, uint16 height) {
+void XpLib::dirtyBlit(byte *src, byte *dst, uint16 width, uint16 height, byte *dirtyFlags) {
+	for (int16 row = 0; row < height; row++) {
+		byte flag = dirtyFlags[row];
 
-}
+		// Clean row? Skip...
+		if (flag == 3) {
+			src += width;
+			dst += width;
+			continue;
+		}
 
-void XpLib::maskBlit(void *src, uint16 srcStride, void *dst, uint16 dstStride, uint16 width, uint16 height) {
+		// Mark row: 2 -> 3 (was dirty, now clean), else -> 0 (newly copied)
+		dirtyFlags[row] = (flag == 2) ? 3 : 0;
 
+		memcpy(dst, src, width);
+	}
 }
 
-uint32 XpLib::compositeBlit(void *src, void *background, void *dst, uint16 stride, uint16 width, uint16 height) {
-	return 0;
+void XpLib::maskBlit(byte *src, uint16 srcStride, byte *dst, uint16 dstStride, uint16 width, uint16 height) {
+	int16 srcSkip = srcStride - width;
+	int16 dstSkip = dstStride - width;
+
+	for (int16 row = 0; row < height; row++) {
+		for (int16 col = 0; col < width; col++) {
+			byte pixel = *src++;
+			if (pixel != 0)
+				*dst = pixel;
+
+			dst++;
+		}
+
+		dst += dstSkip;
+		src += srcSkip;
+	}
 }
 
-void XpLib::rleBlit(void *src, void *dst, uint16 dstStride, uint16 width, uint16 height) {
+void XpLib::compositeBlit(byte *src, byte *background, byte *dst, uint16 stride, uint16 width, uint16 height) {
+	int16 skip = stride - width;
 
+	for (int16 row = 0; row < height; row++) {
+		for (int16 col = 0; col < width; col++) {
+			byte pixel = *src++;
+			if (pixel == 0)
+				pixel = *background;
+
+			*dst++ = pixel;
+			background++;
+		}
+
+		src += skip;
+		background += skip;
+		dst += skip;
+	}
 }
 
-void XpLib::rleMaskBlit(void *src, void *dst, uint16 dstStride, uint16 width, uint16 height) {
+void XpLib::rleBlit(byte *src, uint16 srcStride, byte *dst, uint16 dstStride, uint16 width, uint16 height) {
+	int16 dstSkip = dstStride - width;
+
+	for (int16 row = 0; row < height; row++) {
+		byte *rowEnd = dst + width;
 
+		while (true) {
+			byte b = *src++;
+			if ((b & 0x80) == 0) {
+				// Literal pixel
+				*dst++ = b;
+			} else {
+				// Run
+				byte val = b & 0x7F;
+				int16 count = *src++;
+				if (count == 0) {
+					count = (int16)(rowEnd - dst);
+					rowEnd = 0;
+				}
+
+				memset(dst, val, count);
+				dst += count;
+
+				if (rowEnd == 0) {
+					dst += dstSkip;
+					break;
+				}
+			}
+		}
+	}
 }
 
-uint32 XpLib::rleCompositeBlit(void *rle, void *background, void *dst, uint16 width, uint16 height, void *dirtyFlags) {
-	return 0;
+void XpLib::rleMaskBlit(byte *src, uint16 srcStride, byte *dst, uint16 dstStride, uint16 width, uint16 height) {
+	int16 dstSkip = dstStride - width;
+
+	for (int16 row = 0; row < height; row++) {
+		byte *rowEnd = dst + width;
+
+		while (true) {
+			byte b = *src++;
+			if ((b & 0x80) == 0) {
+				// Literal pixel
+				if (b != 0)
+					*dst = b;
+
+				dst++;
+			} else {
+				// Run
+				byte val = b & 0x7F;
+				int16 count = *src++;
+				if (count == 0) {
+					count = (int16)(rowEnd - dst);
+					rowEnd = 0;
+				}
+
+				if (val == 0) {
+					// Transparent run skip...
+					dst += count;
+				} else {
+					memset(dst, val, count);
+					dst += count;
+				}
+
+				if (rowEnd == 0) {
+					dst += dstSkip;
+					break;
+				}
+			}
+		}
+	}
 }
 
-uint16 XpLib::rleDataSize(void *rleData, uint16 height) {
-	return 0;
+void XpLib::rleCompositeBlit(byte *rle, byte *background, byte *dst, uint16 width, uint16 height, byte *dirtyFlags) {
+	byte *rlePtr = rle;
+	byte *bgPtr = background;
+	byte *dstPtr = dst;
+
+	for (int16 row = 0; row < height; row++) {
+		byte *rowEnd = dstPtr + width;
+
+		// Check if row is all transparent...
+		if (rlePtr[0] == 0x80 && rlePtr[1] == 0x00) {
+			if (dirtyFlags[row] & 2) {
+				// Row was already transparent: mark clean and skip...
+				dirtyFlags[row] = 3;
+				rlePtr += 2;
+				dstPtr = rowEnd;
+				bgPtr += width;
+				continue;
+			}
+
+			dirtyFlags[row] = 2;
+		} else {
+			dirtyFlags[row] = 0;
+		}
+
+		// Decode RLE tokens for this row...
+		while (true) {
+			byte b = *rlePtr++;
+			if ((b & 0x80) == 0) {
+				// Literal pixel
+				if (b != 0) {
+					*dstPtr++ = b; // opaque
+					bgPtr++;
+				} else {
+					*dstPtr++ = *bgPtr++; // transparent (copy from background)
+				}
+			} else if (b != 0x80) {
+				// Opaque run
+				byte val = b & 0x7F;
+				int16 count = *rlePtr++;
+				if (count == 0) {
+					count = rowEnd - dstPtr;
+					rowEnd = nullptr;
+				}
+
+				bgPtr += count;
+				memset(dstPtr, val, count);
+				dstPtr += count;
+
+				if (rowEnd == nullptr)
+					break;
+			} else {
+				// Transparent run (copy from background)
+				int16 count = *rlePtr++;
+				if (count == 0) {
+					count = rowEnd - dstPtr;
+					rowEnd = nullptr;
+				}
+
+				memcpy(dstPtr, bgPtr, count);
+				dstPtr += count;
+				bgPtr += count;
+
+				if (rowEnd == nullptr)
+					break;
+			}
+		}
+	}
 }
 
-void XpLib::markCursorPixels(void *buffer, uint32 count) {
+uint16 XpLib::rleDataSize(byte *rleData, uint16 height) {
+	byte *start = rleData;
+	byte *ptr = start;
+
+	for (int16 y = 0; y < height; y++) {
+		while (true) {
+			byte b = *ptr++;
+			if (b & 0x80) {
+				byte count = *ptr++;
+				if (count == 0)
+					break;
+			}
+		}
+	}
+
+	return (uint16)(ptr - start);
+}
 
+void XpLib::markCursorPixels(byte *buffer, uint32 count) {
+	byte *ptr = buffer;
+	for (uint32 i = 0; i < count; i++) {
+		ptr[i] |= 0x80;
+	}
 }
 
 } // End of namespace Bolt
diff --git a/engines/bolt/xplib/cursor.cpp b/engines/bolt/xplib/cursor.cpp
index 923f8fb76ef..cf1f53a3dcb 100644
--- a/engines/bolt/xplib/cursor.cpp
+++ b/engines/bolt/xplib/cursor.cpp
@@ -22,39 +22,131 @@
 #include "bolt/bolt.h"
 #include "bolt/xplib/xplib.h"
 
+#include "graphics/cursorman.h"
+
 namespace Bolt {
 
-bool XpLib::initCoords() {
-	return false;
+bool XpLib::initCursor() {
+	if (!_bolt->g_extendedViewport) {
+		g_cursorViewportWidth = SCREEN_WIDTH;
+		g_cursorViewportHeight = SCREEN_HEIGHT;
+	} else {
+		g_cursorViewportWidth = EXTENDED_SCREEN_WIDTH;
+		g_cursorViewportHeight = EXTENDED_SCREEN_HEIGHT;
+	}
+
+	g_cursorSprite.pixelData = g_cursorBuffer;
+
+	g_cursorWidth = 16;
+	g_cursorHeight = 16;
+	g_cursorScale = 2;
+
+	return true;
 }
 
-void XpLib::shutdownCoords() {
+void XpLib::shutdownCursor() {
 }
 
 bool XpLib::readCursor(uint16 *outButtons, int16 *outX, int16 *outY) {
-	return false;
+	if (g_cursorHidden == 0)
+		return false;
+
+	*outX = g_lastCursorX;
+	*outY = g_lastCursorY;
+
+	screenToVirtual(outX, outY);
+
+	if (outButtons != nullptr)
+		*outButtons = getButtonState();
+
+	return true;
 }
 
 void XpLib::readJoystick(int16 *outX, int16 *outY) {
+	g_lastCursorX = (int16)((int32)g_lastRegisteredMousePos.x * (int32)g_virtualWidth / (int32)g_cursorViewportWidth);
+	g_lastCursorY = (int16)((int32)g_lastRegisteredMousePos.y * (int32)g_virtualHeight / (int32)g_cursorViewportHeight);
+
+	*outX = g_lastCursorX;
+	*outY = g_lastCursorY;
+
+	screenToVirtual(outX, outY);
 }
 
 void XpLib::setCursorPos(int16 x, int16 y) {
+	virtualToScreen(&x, &y);
+
+	g_lastCursorX = x;
+	g_lastCursorY = y;
+
+	if (g_cursorHidden == 0)
+		updateDisplay();
+
+	int16 screenX = (int16)((int32)g_lastCursorX * (int32)g_cursorViewportWidth / (int32)g_virtualWidth);
+	int16 screenY = (int16)((int32)g_lastCursorY * (int32)g_cursorViewportHeight / (int32)g_virtualHeight);
+
+	_bolt->_system->warpMouse(screenX, screenY);
 }
 
-void XpLib::setCursorImage(void *bitmap, int16 hotspotX, int16 hotspotY) {
+void XpLib::setCursorImage(byte *bitmap, int16 hotspotX, int16 hotspotY) {
+	byte *dest = g_cursorSprite.pixelData;
+
+	for (int16 row = 32; row > 0; row--) {
+		for (int16 mask = 0x80; mask != 0; mask >>= 1) {
+			*dest++ = (*bitmap & mask) ? 0x80 : 0x00;
+		}
+
+		bitmap++;
+	}
+
+	g_cursorHotspotX = hotspotX;
+	g_cursorHotspotY = hotspotY;
+
+	if (g_cursorHidden == 0)
+		updateDisplay();
 }
 
 void XpLib::setCursorColor(byte r, byte g, byte b) {
+	byte rgb[3] = {r, g, b};
+	setPalette(1, 128, rgb);
 }
 
 bool XpLib::showCursor() {
-	return false;
+	if (g_cursorHidden <= 0)
+		return true;
+
+	g_cursorHidden--;
+	if (g_cursorHidden != 0)
+		return false;
+
+	screenToVirtual(&g_lastCursorX, &g_lastCursorY);
+	setCursorPos(g_lastCursorX, g_lastCursorY);
+	updateDisplay();
+	enableMouse();
+
+	return true;
 }
 
 void XpLib::hideCursor() {
+	g_cursorHidden++;
+
+	if (g_cursorHidden == 1) {
+		updateDisplay();
+		disableMouse();
+	}
 }
 
 void XpLib::updateCursorPosition() {
+	if (g_cursorHidden != 0)
+		return;
+
+	int16 x = (int16)((int32)g_lastRegisteredMousePos.x * (int32)g_virtualWidth / (int32)g_cursorViewportWidth);
+	int16 y = (int16)((int32)g_lastRegisteredMousePos.y * (int32)g_virtualHeight / (int32)g_cursorViewportHeight);
+
+	if (x != g_lastCursorX || y != g_lastCursorY) {
+		g_lastCursorX = x;
+		g_lastCursorY = y;
+		updateDisplay();
+	}
 }
 
 } // End of namespace Bolt
diff --git a/engines/bolt/xplib/display.cpp b/engines/bolt/xplib/display.cpp
index cc779d2ba07..27b2ee82b16 100644
--- a/engines/bolt/xplib/display.cpp
+++ b/engines/bolt/xplib/display.cpp
@@ -24,87 +24,705 @@
 
 namespace Bolt {
 
-int16 XpLib::switchDisplayMode(int16 mode) {
-	return int16();
-}
-
 bool XpLib::initDisplay() {
-	return false;
+	g_virtualWidth = 320;
+	g_virtualHeight = 200;
+	g_currentDisplayPage = 0;
+
+	// Create front and back display surfaces
+	if (!createSurface(&g_surfaces[0]))
+		return false;
+
+	if (!createSurface(&g_surfaces[1]))
+		return false;
+
+	fillDisplay(0, 0);
+	fillDisplay(1, 0);
+
+	g_spriteOverlayActive = 0;
+	g_spriteOverlayEnabled = 0;
+	g_frameRateFPS = 0;
+	g_overlayCount = 0;
+	g_prevDirtyCount = 0;
+	g_prevDirtyValid = 0;
+
+	g_cursorBackgroundSave.pixelData = g_cursorBackgroundSaveBuffer;
+	g_cursorBackgroundSave.width = 16;
+	g_cursorBackgroundSave.height = 16;
+
+	return true;
 }
 
 void XpLib::shutdownDisplay() {
+	freeSurface(&g_surfaces[0]);
+	freeSurface(&g_surfaces[1]);
+	stopCycle();
 }
 
 bool XpLib::createSurface(XPSurface *surf) {
-	return false;
+	if (surf->mainPic.width == g_surfaceWidth && surf->mainPic.height == g_surfaceHeight)
+		return true;
+
+	freeSurface(surf);
+
+	surf->mainPic.pixelData = (byte *)allocMem((uint32)g_surfaceWidth * (uint32)g_surfaceHeight);
+	if (!surf->mainPic.pixelData)
+		return false;
+
+	surf->mainPic.palette = (byte *)allocMem(127 * 3);
+	if (!surf->mainPic.palette)
+		return false;
+
+	surf->mainPic.width = g_surfaceWidth;
+	surf->mainPic.height = g_surfaceHeight;
+
+	for (int16 i = 0; i < 127 * 3; i += 3) {
+		surf->mainPic.palette[i] = 0;
+		surf->mainPic.palette[i + 1] = 0;
+		surf->mainPic.palette[i + 2] = 0;
+	}
+
+	surf->mainPic.paletteStart = 1;
+	surf->mainPic.paletteCount = 127;
+	surf->mainPic.flags = 0;
+	surf->overlayPic.pixelData = nullptr;
+	surf->dirtyPalStart = 0;
+	surf->dirtyPalEnd = 0;
+
+	return true;
 }
 
 void XpLib::freeSurface(XPSurface *surf) {
+	if (surf->mainPic.palette != nullptr) {
+		freeMem(surf->mainPic.palette);
+		surf->mainPic.palette = nullptr;
+	}
+
+	if (surf->mainPic.pixelData != nullptr) {
+		freeMem(surf->mainPic.pixelData);
+		surf->mainPic.pixelData = nullptr;
+	}
 }
 
 bool XpLib::chooseDisplaySpec(int *outMode, int numSpecs, DisplaySpecs *specs) {
+	for (int16 i = 0; i < numSpecs; i++) {
+
+		if (specs[i].width == 320 && specs[i].height == 200) {
+			*outMode = i;
+			g_surfaceWidth = specs[i].width;
+			g_surfaceHeight = specs[i].height;
+
+			if (!createSurface(&g_surfaces[0]))
+				return false;
+
+			if (!createSurface(&g_surfaces[1]))
+				return false;
+
+			return true;
+		}
+	}
+
 	return false;
 }
 
 void XpLib::setCoordSpec(int16 x, int16 y, int16 width, int16 height) {
+	if (width == g_surfaceWidth && height == g_surfaceHeight) {
+		g_viewportOffsetX = x;
+		g_viewportOffsetY = y;
+	}
 }
 
 void XpLib::virtualToScreen(int16 *x, int16 *y) {
+	*x -= g_viewportOffsetX;
+	*y -= g_viewportOffsetY;
 }
 
 void XpLib::screenToVirtual(int16 *x, int16 *y) {
+	*x += g_viewportOffsetX;
+	*y += g_viewportOffsetY;
 }
 
 void XpLib::displayPic(XPPicDesc *pic, int16 x, int16 y, int16 page) {
+	XPSurface *surf = &g_surfaces[page];
+
+	virtualToScreen(&x, &y);
+
+	// --- Pixel data ---
+	if (pic->pixelData) {
+		bool isFullScreen = (pic->width == g_surfaceWidth &&  pic->height == g_surfaceHeight);
+
+		if (isFullScreen)
+			g_spriteOverlayEnabled |= 4; // Mark full-screen overwrite
+
+		// Full-screen transparent non-RLE overlayPic on surface 0: store as overlay source...
+		if (isFullScreen && page == 0 && (pic->flags & 1) && !(pic->flags & 2)) {
+			memcpy(&surf->overlayPic, pic, sizeof(pic));
+			g_spriteOverlayEnabled |= 1;
+		} else {
+			// Clear previous overlay if needed...
+			if ((g_spriteOverlayEnabled & 1) && page == 0) {
+				if (!isFullScreen)
+					clipAndBlit(&surf->overlayPic, surf, 0, 0, nullptr);
+
+				g_spriteOverlayEnabled &= ~1;
+			}
+
+			// Clip and blit to surface...
+			Common::Rect clipResult;
+			if (clipAndBlit(pic, surf, x, y, &clipResult))
+				addDirtyRect(&clipResult);
+		}
+
+		// Mark page as dirty...
+		g_spriteOverlayEnabled |= (page != 0) ? 0x10 : 0x08;
+	}
+
+	// --- Palette data ---
+	if (pic->palette != nullptr) {
+		int16 palStart = pic->paletteStart;
+		int16 palEnd = palStart + pic->paletteCount - 1;
+		int16 adjStart = palStart;
+		int16 adjCount = pic->paletteCount;
+		byte *palData = pic->palette;
+
+		// Skip index 0...
+		if (adjStart == 0) {
+			adjStart = 1;
+			palData += 3;
+		}
+
+		// Clamp to page range (indices 1-127)...
+		if (adjStart > 127)
+			adjStart = 127;
+
+		if (palEnd > 127)
+			palEnd = 127;
+
+		adjCount = palEnd - adjStart + 1;
+
+		if (adjCount > 0) {
+			// Copy palette to surface's palette buffer...
+			int16 destOffset = (adjStart - surf->mainPic.paletteStart) * 3;
+			memcpy(surf->mainPic.palette + destOffset, palData, adjCount * 3);
+		}
+
+		int16 dirtyFlag = (page != 0) ? 0x40 : 0x20;
+
+		if (g_spriteOverlayEnabled & dirtyFlag) {
+			// Merge dirty range...
+			if (surf->dirtyPalStart > adjStart)
+				surf->dirtyPalStart = adjStart;
+
+			if (surf->dirtyPalEnd < palEnd)
+				surf->dirtyPalEnd = palEnd;
+		} else {
+			// First palette update, set range...
+			surf->dirtyPalStart = adjStart;
+			surf->dirtyPalEnd = palEnd;
+			g_spriteOverlayEnabled |= dirtyFlag;
+		}
+	}
 }
 
-bool XpLib::clipAndBlit(XPPicDesc *src, XPSurface *dest, int16 x, int16 y, ClipRect *outClip) {
-	return false;
+void XpLib::dispatchBlit(int16 mode, byte *src, uint16 srcStride, byte *dst, uint16 dstStride, uint16 width, uint16 height) {
+	switch (mode) {
+	case 0:
+		blit(src, srcStride, dst, dstStride, width, height);
+		break;
+	case 1:
+		maskBlit(src, srcStride, dst, dstStride, width, height);
+		break;
+	case 2:
+		rleBlit(src, srcStride, dst, dstStride, width, height);
+		break;
+	case 3:
+		rleMaskBlit(src, srcStride, dst, dstStride, width, height);
+		break;
+	}
+}
+
+bool XpLib::clipAndBlit(XPPicDesc *src, XPSurface *dest, int16 x, int16 y, Common::Rect *outClip) {
+	int16 blitMode = 0;
+
+	if (src->flags & 1)
+		blitMode |= 2; // Transparent
+
+	if (src->flags & 2)
+		blitMode |= 1; // RLE
+
+	Common::Rect srcRect(x, y, x + src->width, y + src->height);
+	Common::Rect destRect(0, 0, dest->mainPic.width, dest->mainPic.height);
+
+	Common::Rect clipped;
+	clipped = srcRect.findIntersectingRect(destRect);
+	if (clipped.isEmpty())
+		return false;
+
+	// For transparent blits, check if content actually changed...
+	if (src->flags & 1) {
+		if (memcmp(&clipped, &srcRect, 8) == 0)
+			return false;
+	}
+
+	if (outClip) {
+		outClip->left = clipped.left;
+		outClip->top = clipped.top;
+		outClip->right = clipped.right;
+		outClip->bottom = clipped.bottom;
+	}
+
+	int16 srcOff = (clipped.top - srcRect.top) * src->width + (clipped.left - srcRect.left);
+	int16 destOff = clipped.top * dest->mainPic.width + clipped.left;
+	int16 clipW = clipped.right - clipped.left;
+	int16 clipH = clipped.bottom - clipped.top;
+
+	dispatchBlit(blitMode,
+		src->pixelData + srcOff, src->width,
+		dest->mainPic.pixelData + destOff, dest->mainPic.width,
+		clipW, clipH
+	);
+
+	return true;
 }
 
-void XpLib::addDirtyRect(ClipRect *rect) {
+void XpLib::addDirtyRect(Common::Rect *rect) {
+	if (g_spriteOverlayEnabled & 4)
+		return; // Full-screen overwrite, no need to track...
+
+	if (g_overlayCount >= 30) {
+		g_spriteOverlayEnabled |= 4; // Too many rects, mark full dirty...
+		return;
+	}
+
+	g_dirtyRects[g_overlayCount].left = rect->left;
+	g_dirtyRects[g_overlayCount].right = rect->right;
+	g_dirtyRects[g_overlayCount].top = rect->top;
+	g_dirtyRects[g_overlayCount].bottom = rect->bottom;
+
+	g_overlayCount++;
 }
 
 void XpLib::setFrameRate(int16 fps) {
+	g_frameRateFPS = fps;
+	g_nextFrameTime = _bolt->_system->getMillis();
 }
 
 void XpLib::updateDisplay() {
+	if (g_cursorHidden == 0)
+		g_spriteOverlayEnabled |= 0x80;
+	else
+		g_spriteOverlayEnabled &= ~0x80;
+
+	// This will call delayMillis (but not pollEvent, since that has to be handled elsewhere)...
+	waitForFrameRate();
+
+	handlePaletteTransitions();
+
+	// If back surface pixels changed, mark all with cursor bit and reset row flags...
+	if (g_spriteOverlayEnabled & 0x10) {
+		markCursorPixels(g_surfaces[1].mainPic.pixelData, (uint32)g_surfaces[1].mainPic.width * (uint32)g_surfaces[1].mainPic.height);
+		memset(&g_rowDirtyFlags, 0, 200);
+	}
+
+	// Render to screen!
+	if (g_spriteOverlayEnabled & 1) {
+		overlayComposite(); // RLE overlay path
+	} else {
+		compositeToScreen(); // Standard dirty-rect path
+	}
+
+	// Save current state, clear per-frame flags...
+	g_spriteOverlayActive = g_spriteOverlayEnabled;
+	g_spriteOverlayEnabled &= 0x03; // Keep only overlay + double-buffer bits...
+
+	_bolt->_system->copyRectToScreen(g_vgaFramebuffer, 320, 0, 0, 320, 200);
+	_bolt->_system->updateScreen();
 }
 
 void XpLib::waitForFrameRate() {
+	if (g_frameRateFPS == 0)
+		return;
+
+	while (_bolt->_system->getMillis() < g_nextFrameTime) {
+		_bolt->_system->delayMillis(16);
+	}
+
+	g_nextFrameTime = (uint32)(_bolt->_system->getMillis() + 1000 / (uint32)g_frameRateFPS);
 }
 
 void XpLib::handlePaletteTransitions() {
+	// Only act if palette dirty flags are set alongside pixel dirty flags...
+	if (!((g_spriteOverlayEnabled & 0x20) && (g_spriteOverlayEnabled & 0x08)) &&
+		!((g_spriteOverlayEnabled & 0x40) && (g_spriteOverlayEnabled & 0x10)))
+		return;
+
+	// Build transition code: bit 0 = entering double-buffer, bit 1 = was double-buffer
+	int16 transition = 0;
+	if (g_spriteOverlayEnabled & 2)
+		transition |= 1;
+	if (g_spriteOverlayActive & 2)
+		transition |= 2;
+
+	switch (transition) {
+	case 0:
+		// Single -> Single
+		applyCursorPalette();
+		break;
+
+	case 1: {
+		// Single -> Double
+		if (!(g_spriteOverlayEnabled & 0x40))
+			break;
+
+		// Check if back surface palette actually differs from VGA palette...
+		XPSurface *back = &g_surfaces[1];
+		int16 count = back->dirtyPalEnd - back->dirtyPalStart + 1;
+		int16 surfOffset = (back->dirtyPalStart - back->mainPic.paletteStart) * 3;
+
+		if (memcmp(&g_paletteBuffer[back->dirtyPalStart * 3],
+				   back->mainPic.palette + surfOffset,
+				   count * 3) != 0)
+			break;
+
+		applyCursorPalette();
+		break;
+	}
+	
+	case 2: {
+		// Double -> Single
+		if (!(g_spriteOverlayEnabled & 0x20))
+			break;
+
+		// Check if front surface palette differs from cursor palette page...
+		XPSurface *front = &g_surfaces[0];
+		int16 count = front->dirtyPalEnd - front->dirtyPalStart + 1;
+		int16 surfOffset = (front->dirtyPalStart - front->mainPic.paletteStart) * 3;
+
+		if (memcmp(&g_paletteBuffer[(front->dirtyPalStart + 128) * 3],
+				   front->mainPic.palette + surfOffset,
+				   count * 3) != 0)
+			break;
+
+		prepareBackSurface();
+		break;
+	}
+
+	case 3:
+		// Double -> Double: no special handling
+		break;
+	}
 }
 
 void XpLib::flushPalette() {
+	// Front surface palette -> VGA indices 1-127
+	if (g_spriteOverlayEnabled & 0x20) {
+		XPSurface *front = &g_surfaces[0];
+		int16 start = front->dirtyPalStart;
+		int16 count = front->dirtyPalEnd - start + 1;
+		int16 offset = (start - front->mainPic.paletteStart) * 3;
+		setPalette(count, start, front->mainPic.palette + offset);
+	}
+
+	// Back surface palette -> VGA indices 129-255 (start + 128)
+	if (g_spriteOverlayEnabled & 0x40) {
+		XPSurface *back = &g_surfaces[1];
+		int16 start = back->dirtyPalStart;
+		int16 count = back->dirtyPalEnd - start + 1;
+		int16 offset = (start - back->mainPic.paletteStart) * 3;
+		setPalette(count, start + 128, back->mainPic.palette + offset);
+	}
 }
 
 void XpLib::overlayComposite() {
+	bool pixelsDirty = (g_spriteOverlayEnabled & 0x18) != 0;
+	bool cursorNow = (g_spriteOverlayEnabled & 0x80) != 0;
+	bool cursorPrev = (g_spriteOverlayActive & 0x80) != 0;
+
+	// Nothing changed at all, just flush the palette...
+	if (!pixelsDirty && !cursorNow && !cursorPrev) {
+		flushPalette();
+		return;
+	}
+
+	int16 cursorX = 0, cursorY = 0;
+
+	// Decode RLE overlay onto front surface...
+	if (pixelsDirty) {
+		if (g_spriteOverlayEnabled & 2) {
+			// Double-buffer: RLE composite (transparent pixels from back surface)
+			rleCompositeBlit(
+				g_surfaces[0].overlayPic.pixelData,
+				g_surfaces[1].mainPic.pixelData,
+				g_surfaces[0].mainPic.pixelData,
+				g_virtualWidth, g_virtualHeight,
+				g_rowDirtyFlags);
+		} else {
+			// Single-buffer: straight RLE decode to front
+			rleBlit(
+				g_surfaces[0].overlayPic.pixelData,
+				g_surfaces[0].overlayPic.width,
+				g_surfaces[0].mainPic.pixelData,
+				g_surfaces[0].mainPic.width,
+				g_virtualWidth, g_virtualHeight);
+		}
+	}
+
+	// Draw cursor onto front surface...
+	if (cursorNow) {
+		cursorX = g_lastCursorX - g_cursorHotspotX;
+		cursorY = g_lastCursorY - g_cursorHotspotY;
+
+		// Save background under cursor...
+		clipAndBlit(&g_cursorBackgroundSave, &g_surfaces[0],
+					  -cursorX, -cursorY, nullptr);
+
+		// Draw cursor sprite...
+		clipAndBlit(&g_cursorBackgroundSave, &g_surfaces[0],
+					  cursorX, cursorY, &g_cursorClipRect2);
+	}
+
+	flushPalette();
+
+	// Transfer front surface to main framebuffer...
+	if (pixelsDirty) {
+		if (g_spriteOverlayEnabled & 2) {
+			// Tracked blit: only copy rows that changed...
+			dirtyBlit(
+				g_surfaces[0].mainPic.pixelData,
+				g_vgaFramebuffer,
+				g_virtualWidth, g_virtualHeight,
+				g_rowDirtyFlags);
+		} else {
+			memcpy(g_vgaFramebuffer, g_surfaces[0].mainPic.pixelData,
+				   (uint32)g_virtualWidth * (uint32)g_virtualHeight);
+		}
+	} else {
+		if (cursorNow)
+			compositeDirtyRects(&g_cursorClipRect2, 1);
+
+		if (cursorPrev)
+			compositeDirtyRects(&g_prevCursorRect2, 1);
+	}
+
+	// Save cursor rect and restore front surface
+	if (cursorNow) {
+		g_prevCursorRect2 = g_cursorClipRect2;
+
+		clipAndBlit(&g_cursorBackgroundSave, &g_surfaces[0],
+					  cursorX, cursorY, nullptr);
+	}
 }
 
 void XpLib::compositeToScreen() {
+	int16 cursorX = 0, cursorY = 0;
+
+	// Draw cursor onto front surface...
+	if (g_spriteOverlayEnabled & 0x80) {
+		cursorX = g_lastCursorX - g_cursorHotspotX;
+		cursorY = g_lastCursorY - g_cursorHotspotY;
+
+		// Save background under cursor position...
+		clipAndBlit(&g_cursorBackgroundSave, &g_surfaces[0], -cursorX, -cursorY, nullptr);
+
+		// Draw cursor sprite at position...
+		clipAndBlit(&g_cursorSprite, &g_surfaces[0], cursorX, cursorY, &g_cursorDirtyRect);
+	}
+
+	flushPalette();
+
+	if (!(g_spriteOverlayEnabled & 2)) {
+		// SINGLE-BUFFER MODE
+
+		if (g_spriteOverlayEnabled & 0x08) {
+			if (g_spriteOverlayEnabled & 4) {
+				// Full-screen dirty, blit entire front surface to framebuffer...
+				blit(g_surfaces[0].mainPic.pixelData, g_surfaces[0].mainPic.width,
+					   g_vgaFramebuffer, g_virtualWidth,
+					   g_virtualWidth, g_virtualHeight);
+			} else {
+				// Partial dirty, blit changed rects only...
+				if (g_prevDirtyValid > 0) {
+					mergeDirtyRects();
+					blitDirtyRects(g_prevDirtyRects, g_prevDirtyCount);
+				}
+
+				blitDirtyRects(g_dirtyRects, g_overlayCount);
+
+				if (g_spriteOverlayEnabled & 0x80)
+					blitDirtyRects(&g_cursorDirtyRect, 1);
+
+				if (g_spriteOverlayActive & 0x80)
+					blitDirtyRects(&g_prevCursorRect, 1);
+			}
+		} else {
+			// No pixel changes - just cursor
+			if (g_spriteOverlayEnabled & 0x80)
+				blitDirtyRects(&g_cursorDirtyRect, 1);
+
+			if (g_spriteOverlayActive & 0x80)
+				blitDirtyRects(&g_prevCursorRect, 1);
+		}
+
+	} else {
+		// DOUBLE-BUFFER MODE
+
+		if (g_spriteOverlayEnabled & 0x18) {
+			if ((g_spriteOverlayEnabled & 4) ||
+				g_prevDirtyValid > 0 || g_overlayCount > 0) {
+				// Full composite...
+				compositeBlit(g_surfaces[0].mainPic.pixelData, g_surfaces[1].mainPic.pixelData,
+							  g_vgaFramebuffer, g_virtualWidth, g_virtualWidth, g_virtualHeight);
+				g_overlayCount = 0;
+				g_prevDirtyCount = 0;
+			} else {
+				// Partial composite...
+				if (g_prevDirtyValid > 0) {
+					mergeDirtyRects();
+					compositeDirtyRects(g_prevDirtyRects, g_prevDirtyCount);
+				}
+
+				compositeDirtyRects(g_dirtyRects, g_overlayCount);
+
+				if (g_spriteOverlayEnabled & 0x80)
+					compositeDirtyRects(&g_cursorDirtyRect, 1);
+
+				if (g_spriteOverlayActive & 0x80)
+					compositeDirtyRects(&g_prevCursorRect, 1);
+			}
+		} else {
+			if (g_spriteOverlayEnabled & 0x80)
+				compositeDirtyRects(&g_cursorDirtyRect, 1);
+
+			if (g_spriteOverlayActive & 0x80)
+				compositeDirtyRects(&g_prevCursorRect, 1);
+		}
+	}
+
+	// Save current dirty rects for next frame...
+	if (g_prevDirtyValid > 0) {
+		g_prevDirtyValid = 0;
+		if (g_overlayCount > 0) {
+			for (int i = 0; i < g_overlayCount; i++) {
+				g_prevDirtyRects[i] = g_dirtyRects[i];
+			}
+		}
+
+		g_prevDirtyCount = g_overlayCount;
+	}
+
+	g_overlayCount = 0;
+
+	// Save cursor state for next frame...
+	if (g_spriteOverlayEnabled & 0x80) {
+		g_prevCursorRect = g_cursorDirtyRect;
+
+		// Restore front surface under cursor...
+		clipAndBlit(&g_cursorBackgroundSave, &g_surfaces[0], cursorX, cursorY, nullptr);
+	}
 }
 
 void XpLib::mergeDirtyRects() {
+	for (int16 i = 0; i < g_prevDirtyCount; i++) {
+		Common::Rect *prev = &g_prevDirtyRects[i];
+
+		for (int16 j = 0; j < g_overlayCount; j++) {
+			Common::Rect *curr = &g_dirtyRects[j];
+
+			if (curr->right < 0)
+				continue; // A negative value indicates "already consumed"...
+
+			if (!prev->intersects(*curr))
+				continue;
+
+			prev->extend(*curr);
+			curr->right *= -1; // Negate to mark consumed...
+			break;
+		}
+	}
 }
 
-void XpLib::blitDirtyRects(ClipRect *rects, int16 count) {
+void XpLib::blitDirtyRects(Common::Rect *rects, int16 count) {
+	for (int16 i = 0; i < count; i++) {
+		Common::Rect *rect = &rects[i];
+
+		if (rect->right < 0) {
+			rect->right = -rect->right;
+		} else {
+			int16 offset = rect->top * g_virtualWidth + rect->left;
+			int16 w = rect->right - rect->left;
+			int16 h = rect->bottom - rect->top;
+
+			blit(g_surfaces[0].mainPic.pixelData + offset, g_surfaces[0].mainPic.width,
+				 g_vgaFramebuffer + offset, g_virtualWidth,
+				 w, h);
+		}
+	}
 }
 
-void XpLib::compositeDirtyRects(ClipRect *rects, int16 count) {
+void XpLib::compositeDirtyRects(Common::Rect *rects, int16 count) {
+	for (int16 i = 0; i < count; i++) {
+		Common::Rect *rect = &rects[i];
+
+		if (rect->right < 0) {
+			rect->right = -rect->right;
+		} else {
+			int16 offset = rect->top * g_virtualWidth + rect->left;
+			int16 w = rect->right - rect->left;
+			int16 h = rect->bottom - rect->top;
+
+			compositeBlit(
+				g_surfaces[0].mainPic.pixelData + offset,
+				g_surfaces[1].mainPic.pixelData + offset,
+				g_vgaFramebuffer + offset,
+				g_virtualWidth, w, h);
+		}
+	}
 }
 
-void XpLib::applyCursorPalette(bool enable) {
+void XpLib::applyCursorPalette() {
+	setPalette(127, 129, &g_paletteBuffer[3]);
+	markCursorPixels(g_vgaFramebuffer, (uint32)g_virtualWidth * (uint32)g_virtualHeight);
 }
 
 void XpLib::prepareBackSurface() {
+	XPPicDesc *src;
+	if (g_spriteOverlayEnabled & 1) {
+		src = &g_surfaces[0].overlayPic;
+	} else {
+		src = &g_surfaces[0].mainPic;
+	}
+
+	clipAndBlit(src, &g_surfaces[1], 0, 0, nullptr);
+
+	markCursorPixels(g_surfaces[1].mainPic.pixelData, (uint32)g_surfaces[1].mainPic.width * (uint32)g_surfaces[1].mainPic.height);
+
+	memcpy(g_vgaFramebuffer, g_surfaces[1].mainPic.pixelData, (uint32)g_surfaces[1].mainPic.width * (uint32)g_surfaces[1].mainPic.height);
 }
 
 void XpLib::setTransparency(bool toggle) {
+	if (toggle) {
+		g_spriteOverlayEnabled |= 2; // Enables double-buffer compositing
+	} else {
+		g_spriteOverlayEnabled &= ~2;
+	}
 }
 
 void XpLib::fillDisplay(byte color, int16 page) {
+
+	XPSurface *surf = &g_surfaces[page];
+	uint32 size = (uint32)surf->mainPic.width * (uint32)surf->mainPic.height;
+
+	// Back surface (page 1) pixels have bit 7 set for cursor palette mapping...
+	byte fillVal = (page == 1) ? (color + 0x80) : color;
+
+	memset(surf->mainPic.pixelData, fillVal, size);
+
+	// Mark page as pixel-dirty...
+	g_spriteOverlayEnabled |= (page != 0) ? 0x10 : 0x08;
+
+	// Clear overlay flag, signal full redraw needed...
+	g_spriteOverlayEnabled &= ~1;
+	g_prevDirtyValid = 1;
 }
 
 } // End of namespace Bolt
diff --git a/engines/bolt/xplib/events.cpp b/engines/bolt/xplib/events.cpp
index 77650c75153..4528c50253a 100644
--- a/engines/bolt/xplib/events.cpp
+++ b/engines/bolt/xplib/events.cpp
@@ -22,98 +22,456 @@
 #include "bolt/bolt.h"
 #include "bolt/xplib/xplib.h"
 
+#include "common/events.h"
+
 namespace Bolt {
 
-bool XpLib::initWindow() {
-	return false;
+bool XpLib::initEvents() {
+	for (int16 i = 0; i < 40; i++) {
+		g_events[i].next = g_eventFreeList;
+		g_eventFreeList = &g_events[i];
+	}
+
+	g_inactivityTimerId = 0;
+	g_inactivityTimeout = 0;
+	g_mouseButtonState = 0;
+	g_mouseButtonPrev = 0;
+	g_eventMouseMoved = 0;
+	g_eventKeyStates = 0;
+
+	return true;
 }
 
-void XpLib::shutdownWindow() {
+void XpLib::shutdownEvents() {
+	g_eventFreeList = nullptr;
+	g_eventQueueTail = nullptr;
+	g_eventQueueHead = nullptr;
 
+	if (g_inactivityTimerId != 0) {
+		killTimer(g_inactivityTimerId);
+		g_inactivityTimerId = 0;
+	}
 }
 
 int16 XpLib::getEvent(int16 filter, uint32 *outData) {
-	return 0;
+	pumpMessages();
+
+	// Search event queue for matching event type...
+	XPEvent *node = g_eventQueueHead;
+	if (filter != 0) {
+		while (node) {
+			if (node->type == filter)
+				break;
+
+			node = node->next;
+		}
+	}
+
+	if (!node)
+		return 0;
+
+	// Remove from queue...
+	unlinkEvent(node);
+
+	// Copy event data to caller...
+	*outData = node->payload;
+
+	// Return node to free list...
+	node->next = g_eventFreeList;
+	g_eventFreeList = node;
+
+	return node->type;
 }
 
 int16 XpLib::peekEvent(int16 filter, uint32 *outData) {
-	return 0;
-}
+	pumpMessages();
+
+	XPEvent *node = g_eventQueueHead;
+	if (filter != 0) {
+		while (node) {
+			if (node->type == filter)
+				break;
+
+			node = node->next;
+		}
+	}
 
-void XpLib::unlinkEvent(int16 node) {
+	if (!node)
+		return 0;
 
+	// Copy data but don't remove from queue...
+	*outData = node->payload;
+	return node->type;
 }
 
-void XpLib::enqueueEvent(int16 node) {
+void XpLib::unlinkEvent(XPEvent *node) {
+	// Remove node from doubly-linked event queue...
+	if (node->prev) {
+		XPEvent *prev = node->prev;
+		prev->next = node->next;
+	} else {
+		g_eventQueueHead = node->next;
+	}
+
+	if (node->next) {
+		XPEvent *next = node->next;
+		next->prev = node->prev;
+	} else {
+		g_eventQueueTail = node->prev;
+	}
+}
 
+void XpLib::enqueueEvent(XPEvent *node) {
+	// Append node to tail of event queue...
+	node->next = nullptr;
+
+	if (g_eventQueueTail) {
+		XPEvent *tail = g_eventQueueTail;
+		tail->next = node;
+		node->prev = g_eventQueueTail;
+		g_eventQueueTail = node;
+	} else {
+		// Queue was empty...
+		g_eventQueueTail = node;
+		g_eventQueueHead = node;
+		node->prev = nullptr;
+	}
 }
 
 void XpLib::pumpMessages() {
-
+	Common::Event event;
+	long timerData;
+	bool mouseMoved = false;
+
+	// Drain any pending sound events...
+	while (pollSound(&timerData) && !_bolt->shouldQuit()) {
+		updateTimers();
+		postEvent(etSound, timerData);
+	}
+
+	// Check inactivity timeout...
+	if (g_inactivityDeadline != 0) {
+		uint32 now = _bolt->_system->getMillis();
+		if (now > g_inactivityDeadline) {
+			g_inactivityDeadline = 0;
+			setCursorPos(160, 100);
+			postJoystickEvent(0, 0, 0);
+		}
+	}
+
+	// Process all pending events...
+	while (_bolt->_eventMan->pollEvent(event) && !_bolt->shouldQuit()) {
+		switch (event.type) {
+		case Bolt::CustomEventType::EVENT_TIMER:
+			handleTimer(event.customType);
+			break;
+
+		case Common::EVENT_MOUSEMOVE:
+			g_lastRegisteredMousePos.x = event.mouse.x;
+			g_lastRegisteredMousePos.y = event.mouse.y;
+			handleMouseMove(&mouseMoved);
+			break;
+
+		case Common::EVENT_LBUTTONDOWN:
+			handleMouseButton(1, 0);
+			break;
+
+		case Common::EVENT_LBUTTONUP:
+			handleMouseButton(0, 0);
+			break;
+
+		case Common::EVENT_RBUTTONDOWN:
+			handleMouseButton(1, 1);
+			break;
+
+		case Common::EVENT_RBUTTONUP:
+			handleMouseButton(0, 1);
+			break;
+
+		case Common::EVENT_MBUTTONDOWN:
+			handleMouseButton(1, 2);
+			break;
+
+		case Common::EVENT_MBUTTONUP:
+			handleMouseButton(0, 2);
+			break;
+
+		case Common::EVENT_KEYDOWN:
+			handleKey(event.kbd.keycode, 1);
+			break;
+
+		case Common::EVENT_KEYUP:
+			handleKey(event.kbd.keycode, 0);
+			break;
+
+		default:
+			break;
+		}
+	}
+
+	if (mouseMoved)
+		updateCursorPosition();
+
+	updateTimers();
+	_bolt->_system->delayMillis(5);
 }
 
 void XpLib::handleTimer(uint32 timerId) {
-
+	if (timerId != g_inactivityTimerId) {
+		postEvent(etTimer, timerId);
+		return;
+	}
+
+	if (g_inactivityCountdown != 0) {
+		g_inactivityCountdown--;
+		if (g_inactivityCountdown == 0)
+			postEvent(etInactivity, 0);
+	}
+
+	if (g_cursorBlinkCountdown != 0) {
+		g_cursorBlinkCountdown--;
+		if (g_cursorBlinkCountdown == 0)
+			activateScreenSaver();
+	}
+
+	if (g_inactivityCountdown != 0 || g_cursorBlinkCountdown != 0) {
+		g_inactivityTimerId = startTimer(1000);
+	} else {
+		g_inactivityTimerId = 0;
+	}
 }
 
 void XpLib::handleMouseMove(bool *mouseMoved) {
-
+	resetInactivity();
+
+	if (g_eventKeyStates == eksMouseMode) {
+		*mouseMoved = true;
+		int16 x, y;
+		readCursor(nullptr, &x, &y);
+		postEvent(etMouseMove, ((uint32)x << 16) | (int16)y);
+		g_eventMouseMoved = 0;
+	} else if (g_eventKeyStates == eksJoystickMode) {
+		int16 joyX, joyY;
+		readJoystick(&joyX, &joyY);
+
+		int16 dx = (joyX < 155) ? -1 : (joyX > 165) ? 1 : 0;
+		int16 dy = (joyY < 96) ? -1 :  (joyY > 104) ? 1 : 0;
+
+		if (dx != 0 || dy != 0) {
+			setCursorPos(160, 100);
+			postJoystickEvent(0, dx, dy);
+		}
+
+		// Set inactivity deadline 100ms from now...
+		g_inactivityDeadline = _bolt->_system->getMillis() + 100;
+	}
 }
 
 void XpLib::handleMouseButton(int16 down, int16 button) {
-
+	resetInactivity();
+
+	if (g_eventKeyStates == eksMouseMode) {
+		int16 x, y;
+		readCursor(nullptr, &x, &y);
+		postEvent(etMouseMove, ((uint32)x << 16) | (int16)y);
+	}
+
+	if (down) {
+		g_mouseButtonPrev |= (1 << button);
+		postEvent(etMouseDown, (uint32)button);
+	} else {
+		g_mouseButtonPrev &= ~(1 << button);
+		postEvent(etMouseUp, (uint32)button);
+	}
 }
 
-void XpLib::handleKey(int16 vkey, int16 down) {
-
+void XpLib::handleKey(Common::KeyCode vkey, int16 down) {
+	resetInactivity();
+
+	if (vkey == Common::KEYCODE_ESCAPE) {
+		if (g_mouseButtonState != down) {
+			if (g_eventKeyStates == eksMouseMode) {
+				int16 x, y;
+				readCursor(nullptr, &x, &y);
+				postEvent(etMouseMove, ((uint32)x << 16) | (int16)y);
+			}
+
+			g_mouseButtonState = down;
+			postEvent(down ? etMouseDown : etMouseUp, 0);
+		}
+
+		return;
+	}
+
+	int8 *dirPtr = nullptr;
+	switch (vkey) {
+	case Common::KEYCODE_LEFT:
+		dirPtr = &g_keyStateLeft;
+		break; // VK_LEFT
+	case Common::KEYCODE_UP:
+		dirPtr = &g_keyStateUp;
+		break; // VK_UP
+	case Common::KEYCODE_RIGHT:
+		dirPtr = &g_keyStateRight;
+		break; // VK_RIGHT
+	case Common::KEYCODE_DOWN:
+		dirPtr = &g_keyStateDown;
+		break; // VK_DOWN
+	}
+
+	if (dirPtr == nullptr)
+		return;
+
+	if (*dirPtr == (int8)down)
+		return;
+
+	*dirPtr = (int8)down;
+
+	if (g_eventKeyStates == eksJoystickMode) {
+		int16 dx = g_keyStateRight - g_keyStateLeft;
+		int16 dy = g_keyStateDown - g_keyStateUp;
+		postJoystickEvent(1, dx, dy);
+	}
 }
 
 void XpLib::postJoystickEvent(int16 source, int16 dx, int16 dy) {
+	g_eventMouseMoved = source;
 
+	if (dx != g_lastJoystickX || dy != g_lastJoystickY) {
+		postEvent(etJoystick, ((uint32)dx << 16) | (uint16)dy);
+		g_lastJoystickX = dx;
+		g_lastJoystickY = dy;
+	}
 }
 
-void XpLib::postEvent(int16 type, uint32 data) {
-
+void XpLib::postEvent(XPEventTypes type, uint32 data) {
+	if (type == etMouseMove) {
+		if (g_lastMouseEventData == data)
+			return;
+
+		g_lastMouseEventData = data;
+		if (g_eventQueueTail->type == etMouseMove) {
+			g_eventQueueTail->payload = data;
+			return;
+		}
+	}
+
+	XPEvent *node;
+	if (g_eventFreeList) {
+		// Allocate from free list...
+		node = g_eventFreeList;
+		g_eventFreeList = g_eventFreeList->next;
+	} else {
+		// Queue full: try to evict a droppable event...
+		if (!canDropEvent(type))
+			return;
+
+		// Walk queue from tail looking for droppable event...
+		node = g_eventQueueTail;
+		while (node) {
+			if (canDropEvent(node->type))
+				break;
+
+			node = node->prev;
+		}
+
+		if (!node)
+			node = g_eventQueueHead;
+
+		unlinkEvent(node);
+	}
+
+	node->type = type;
+	node->payload = data;
+	node->next = nullptr;
+	enqueueEvent(node);
 }
 
 bool XpLib::canDropEvent(int16 type) {
-	return false;
+	switch (type) {
+	case etMouseMove:
+	case etMouseDown:
+	case etMouseUp:
+	case etJoystick:
+		return false;
+	default:
+		return true;
+	}
 }
 
 int16 XpLib::setInactivityTimer(int16 seconds) {
-	return 0;
+	int16 prev = g_inactivityTimerValue;
+
+	g_inactivityTimerValue = seconds;
+	g_inactivityCountdown = seconds;
+
+	if (seconds != 0 && g_inactivityTimerId == 0)
+		g_inactivityTimerId = startTimer(1000);
+
+	// Drain any pending inactivity events from the queue...
+	uint32 payloadDummy;
+
+	// getEvent calls pumpMessages which calls pollEvent and delayMillis...
+	while (getEvent(etInactivity, &payloadDummy) == etInactivity && !_bolt->shouldQuit());
+
+	return prev;
 }
 
-int16 XpLib::setScreenSaverTimer(int16 time) {
-	return 0;
+int16 XpLib::setScreenSaverTimer(int16 seconds) {
+	int16 prev = g_screenSaverTimerValue;
+
+	g_screenSaverTimerValue = seconds;
+	g_cursorBlinkCountdown = seconds;
+
+	if (seconds != 0 && g_inactivityTimerId == 0)
+		g_inactivityTimerId = startTimer(1000);
+
+	return prev;
 }
 
 void XpLib::activateScreenSaver() {
-
+	g_inactivityTimeout = 1;
+	setScreenBrightness(25); // Dim to 25%...
 }
 
 void XpLib::resetInactivity() {
+	if (g_inactivityTimeout != 0) {
+		setScreenBrightness(100); // Restore full brightness...
+		g_inactivityTimeout = 0;
+	}
+
+	// Reset screensaver countdown
+	g_cursorBlinkCountdown = g_screenSaverTimerValue;
 
+	if (g_screenSaverTimerValue != 0 && g_inactivityTimerId == 0)
+		g_inactivityTimerId = startTimer(1000);
 }
 
 bool XpLib::enableController() {
-	return false;
+	// Don't switch if already in mouse mode...
+	if (g_eventKeyStates == eksMouseMode)
+		return false;
+
+	g_eventKeyStates = eksJoystickMode;
+	setCursorPos(160, 100);
+	return true;
 }
 
 void XpLib::disableController() {
-
+	g_eventKeyStates = eksInputOff;
+	g_inactivityDeadline = 0;
 }
 
 void XpLib::enableMouse() {
-
+	g_eventKeyStates = eksMouseMode;
 }
 
 void XpLib::disableMouse() {
-
+	g_eventKeyStates = eksInputOff;
 }
 
 int16 XpLib::getButtonState() {
-	return 0;
+	return g_mouseButtonPrev | g_mouseButtonState;
 }
 
 } // End of namespace Bolt
diff --git a/engines/bolt/xplib/file.cpp b/engines/bolt/xplib/file.cpp
index 6a06ad897a6..f478a0178df 100644
--- a/engines/bolt/xplib/file.cpp
+++ b/engines/bolt/xplib/file.cpp
@@ -22,31 +22,53 @@
 #include "bolt/bolt.h"
 #include "bolt/xplib/xplib.h"
 
+#include "common/file.h"
+
 namespace Bolt {
 
 void XpLib::fileError(const char *message) {
+	stopCycle();
+	error(message);
 }
 
-int32 XpLib::createFile(const char *fileName) {
-	return int32();
-}
+Common::File *XpLib::openFile(const char *fileName, short flags) {
+	// Read-write (mode 3) and write (mode 2) are unsupported and not needed by Cartoon Carnival...
+	assert(flags == 1);
 
-void XpLib::deleteFile(const char *fileName) {
-}
+	Common::File *file = new Common::File();
+	if (!file->exists(fileName)) {
+		fileError("File does not exist.");
+	}
 
-int32 XpLib::openFile(const char *fileName, short flags) {
-	return int32();
+	file->open(fileName);
+	if (!file->isOpen()) {
+		fileError("Disc error.  The Disc may be dirty.");
+		return nullptr;
+	}
+
+	return file;
 }
 
-void XpLib::closeFile(int32 handle) {
+void XpLib::closeFile(Common::File *handle) {
+	if (handle->isOpen()) {
+		handle->close();
+	}
 }
 
-bool XpLib::readFile(int32 handle, void *buffer, uint32 *size) {
-	return false;
+bool XpLib::readFile(Common::File *handle, void *buffer, uint32 *size) {
+	uint32 sizeRead = handle->read(buffer, *size);
+	bool readEntireSize = sizeRead == *size;
+	*size = sizeRead;
+
+	if (!readEntireSize) {
+		fileError("Disc error.  The Disc may be dirty.");
+	}
+
+	return readEntireSize;
 }
 
-bool XpLib::setFilePos(int32 handle, uint32 offset, int16 origin) {
-	return false;
+bool XpLib::setFilePos(Common::File *handle, int32 offset, int32 origin) {
+	return handle->seek(offset + origin, SEEK_SET);
 }
 
 void *XpLib::allocMem(uint32 size) {
diff --git a/engines/bolt/xplib/mem.cpp b/engines/bolt/xplib/mem.cpp
deleted file mode 100644
index fe512e1b830..00000000000
--- a/engines/bolt/xplib/mem.cpp
+++ /dev/null
@@ -1,27 +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 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include "bolt/bolt.h"
-#include "bolt/xplib/xplib.h"
-
-namespace Bolt {
-
-} // End of namespace Bolt
diff --git a/engines/bolt/xplib/palette.cpp b/engines/bolt/xplib/palette.cpp
index 81c6b5534f1..fd33eadd3ed 100644
--- a/engines/bolt/xplib/palette.cpp
+++ b/engines/bolt/xplib/palette.cpp
@@ -22,26 +22,155 @@
 #include "bolt/bolt.h"
 #include "bolt/xplib/xplib.h"
 
+#include "common/timer.h"
+
 namespace Bolt {
 
+void XpLib::getPalette(uint16 startIndex, uint16 count, byte *destBuf) {
+	Common::StackLock lock(g_paletteMutex);
+
+	memcpy(destBuf, &g_paletteBuffer[startIndex * 3], count * 3);
+}
+
+void XpLib::setPalette(uint16 count, uint16 startIndex, byte *srcBuf) {
+	Common::StackLock lock(g_paletteMutex);
+
+	if (startIndex == 0 || (startIndex == 128 && count != 1)) {
+		startIndex++;
+		srcBuf += 3;
+		count--;
+	}
+
+	if (count == 0)
+		return;
+
+	memcpy(&g_paletteBuffer[startIndex * 3], srcBuf, count * 3);
+
+	// Apply brightness shift into shifted buffer...
+	const byte *src = &g_paletteBuffer[startIndex * 3];
+	byte *dst = &g_shiftedPaletteBuffer[startIndex * 3];
 
-void XpLib::getPalette(uint16 startIndex, uint16 count, void *destBuf) {
+	for (uint16 i = 0; i < count * 3; i++)
+		dst[i] = src[i] >> g_brightnessShift;
+
+	// Finally set the palette!
+	g_system->getPaletteManager()->setPalette(&g_shiftedPaletteBuffer[startIndex * 3], startIndex, count);
 }
 
-void XpLib::setPalette(uint16 count, uint16 startIndex, void *srcBu) {
+void XpLib::cycleColorsCallback(void *refCon) {
+	XpLib *xp = (XpLib *)refCon;
+	xp->cycleColors();
 }
 
-bool XpLib::startCycle(XPCycleSpec *specs) {
-	return false;
+bool XpLib::startCycle(XPCycleState *specs) {
+	Common::StackLock lock(g_paletteMutex);
+
+	stopCycle();
+
+	bool anyActive = false;
+	uint32 now = g_system->getMillis();
+
+	for (int16 i = 0; i < 4; i++) {
+		if (specs[i].startIndex == specs[i].endIndex) {
+			g_cycleStates[i].active = false;
+			continue;
+		}
+
+		g_cycleStates[i].startIndex = specs[i].startIndex;
+		g_cycleStates[i].endIndex = specs[i].endIndex;
+		g_cycleStates[i].delay = specs[i].delay;
+		g_cycleStates[i].nextFire = now + specs[i].delay;
+		g_cycleStates[i].active = true;
+		anyActive = true;
+	}
+
+	if (anyActive) {
+		_bolt->getTimerManager()->installTimerProc(
+			cycleColorsCallback, 50 * 1000, this, "BoltPalCycle");
+	}
+
+	return true;
 }
 
 void XpLib::cycleColors() {
+	Common::StackLock lock(g_paletteMutex);
+
+	for (int i = 0; i < 4; i++) {
+		if (!g_cycleStates[i].active)
+			continue;
+
+		uint32 now = g_system->getMillis();
+		if (now < g_cycleStates[i].nextFire)
+			continue;
+
+		g_cycleStates[i].nextFire = now + g_cycleStates[i].delay;
+
+		int16 startIdx = g_cycleStates[i].startIndex;
+		int16 endIdx = g_cycleStates[i].endIndex;
+
+		if (startIdx < endIdx) {
+			int16 count = endIdx - startIdx + 1;
+			if (count > 19 || startIdx < 0 || endIdx > 255)
+				continue;
+
+			// Read current palette range...
+			getPalette(startIdx, count, &g_cycleTempPalette[3]);
+
+			// Save last entry into first position...
+			memcpy(g_cycleTempPalette, &g_cycleTempPalette[count * 3], 3);
+
+			// Write back shifted by one (swap buffer starts 3 bytes earlier)...
+			setPalette(count, startIdx, g_cycleTempPalette);
+
+		} else if (startIdx > endIdx) {
+			int16 count = startIdx - endIdx + 1;
+			if (count > 19 || endIdx < 0 || startIdx > 255)
+				continue;
+
+			// Read current palette range...
+			getPalette(endIdx, count, g_cycleTempPalette);
+
+			// Save first entry, append after last...
+			memcpy(&g_cycleTempPalette[count * 3], g_cycleTempPalette, 3);
+
+			// Write back shifted by one (starts one entry later)...
+			setPalette(count, endIdx, &g_cycleTempPalette[3]);
+		}
+	}
 }
 
 void XpLib::stopCycle() {
+	Common::StackLock lock(g_paletteMutex);
+
+	bool wasActive = false;
+	for (int16 i = 0; i < 4; i++) {
+		if (g_cycleStates[i].active) {
+			wasActive = true;
+			g_cycleStates[i].active = false;
+		}
+	}
+
+	if (wasActive)
+		_bolt->getTimerManager()->removeTimerProc(cycleColorsCallback);
 }
 
 void XpLib::setScreenBrightness(uint8 percent) {
+	Common::StackLock lock(g_paletteMutex);
+
+	if (percent >= 100) {
+		g_brightnessShift = 2; // Full brightness (VGA DAC is 6-bit)
+	} else if (percent >= 50) {
+		g_brightnessShift = 3; // 50%
+	} else if (percent >= 25) {
+		g_brightnessShift = 4; // 25%
+	} else if (percent >= 12) {
+		g_brightnessShift = 5; // 12%
+	} else {
+		g_brightnessShift = 6; // Near black
+	}
+
+	// Re-apply entire palette with new brightness...
+	setPalette(256, 0, g_paletteBuffer);
 }
 
 } // End of namespace Bolt
diff --git a/engines/bolt/xplib/sound.cpp b/engines/bolt/xplib/sound.cpp
index b1a26a29671..2bb62117449 100644
--- a/engines/bolt/xplib/sound.cpp
+++ b/engines/bolt/xplib/sound.cpp
@@ -25,11 +25,11 @@
 namespace Bolt {
 
 bool XpLib::pollSound(void *outData) {
-	return false;
+	return true;
 }
 
 bool XpLib::initSound() {
-	return false;
+	return true;
 }
 
 void XpLib::waveCb() {
@@ -40,7 +40,7 @@ void XpLib::shutdownSound() {
 
 }
 
-bool XpLib::playSound(void *data, uint32 size, int16 sampleRate) {
+bool XpLib::playSound(byte *data, uint32 size, int16 sampleRate) {
 	return false;
 }
 
diff --git a/engines/bolt/xplib/timer.cpp b/engines/bolt/xplib/timer.cpp
index 9b8d710f982..16cc3294868 100644
--- a/engines/bolt/xplib/timer.cpp
+++ b/engines/bolt/xplib/timer.cpp
@@ -25,20 +25,63 @@
 namespace Bolt {
 
 bool XpLib::initTimer() {
-	return false;
+	for (int i = 0; i < ARRAYSIZE(g_timers); i++)
+		g_timers[i].active = false;
+
+	g_nextTimerId = 1;
+	g_timerInitialized = true;
+
+	return true;
 }
 
 void XpLib::shutdownTimer() {
+	if (!g_timerInitialized) {
+		g_timerInitialized = false;
+
+		for (int i = 0; i < ARRAYSIZE(g_timers); i++) {
+			g_timers[i].active = false;
+		}
+	}
 }
 
-int32 XpLib::startTimer(int16 delay) {
+uint32 XpLib::startTimer(int16 delay) {
+	uint32 now = _bolt->_system->getMillis();
+
+	for (int i = 0; i < ARRAYSIZE(g_timers); i++) {
+		if (!g_timers[i].active) {
+			g_timers[i].id = g_nextTimerId++;
+
+			if (g_nextTimerId == 0)
+				g_nextTimerId = 1;
+
+			g_timers[i].deadline = now + delay;
+			g_timers[i].active = true;
+			return g_timers[i].id;
+		}
+	}
+
 	return 0;
 }
 
-void XpLib::timeCb() {
+void XpLib::updateTimers() {
+	uint32 now = _bolt->_system->getMillis();
+
+	for (int i = 0; i < ARRAYSIZE(g_timers); i++) {
+		if (g_timers[i].active && now >= g_timers[i].deadline) {
+			g_timers[i].active = false;
+			handleTimer(g_timers[i].id);
+		}
+	}
 }
 
-bool XpLib::killTimer(int32 timerId) {
+bool XpLib::killTimer(uint32 timerId) {
+	for (int i = 0; i < ARRAYSIZE(g_timers); i++) {
+		if (g_timers[i].active && g_timers[i].id == timerId) {
+			g_timers[i].active = false;
+			return true;
+		}
+	}
+
 	return false;
 }
 
diff --git a/engines/bolt/xplib/xplib.cpp b/engines/bolt/xplib/xplib.cpp
index 4cdc6f476bb..6a4057dab61 100644
--- a/engines/bolt/xplib/xplib.cpp
+++ b/engines/bolt/xplib/xplib.cpp
@@ -27,17 +27,65 @@ namespace Bolt {
 XpLib::XpLib(BoltEngine *bolt) {
 	_bolt = bolt;
 
-	initialize();
+	memset(g_cursorBuffer, 0, sizeof(g_cursorBuffer));
+	memset(g_cursorBackgroundSaveBuffer, 0, sizeof(g_cursorBackgroundSaveBuffer));
+
+	memset(g_paletteBuffer, 0, sizeof(g_paletteBuffer));
+	memset(g_shiftedPaletteBuffer, 0, sizeof(g_shiftedPaletteBuffer));
+	memset(g_cycleTimerIds, 0, sizeof(g_cycleTimerIds));
+	memset(g_cycleTempPalette, 0, sizeof(g_cycleTempPalette));
+
+	_screen = new Graphics::Screen();
 }
 
 XpLib::~XpLib() {
 	terminate();
+
+	delete _screen;
 }
 
-void XpLib::initialize() {
+bool XpLib::initialize() {
+	if (g_xpInitialized)
+		return false;
+
+	g_xpInitialized = true;
+
+	if (!initTimer()) {
+		terminate();
+		return false;
+	}
+
+	if (!initEvents()) {
+		terminate();
+		return false;
+	}
+
+	if (!initSound()) {
+		terminate();
+		return false;
+	}
+
+	if (!initDisplay()) {
+		terminate();
+		return false;
+	}
+
+	if (!initCursor()) {
+		terminate();
+		return false;
+	}
+
+	return true;
 }
 
 void XpLib::terminate() {
+	shutdownCursor();
+	shutdownDisplay();
+	shutdownSound();
+	shutdownEvents();
+	shutdownTimer();
+
+	g_xpInitialized = false;
 }
 
 } // End of namespace Bolt
diff --git a/engines/bolt/xplib/xplib.h b/engines/bolt/xplib/xplib.h
index fd4031a5c35..ff9057e379d 100644
--- a/engines/bolt/xplib/xplib.h
+++ b/engines/bolt/xplib/xplib.h
@@ -24,30 +24,31 @@
 
 #include "bolt/bolt.h"
 
+#include "common/events.h"
+#include "common/file.h"
+#include "common/mutex.h"
+#include "graphics/paletteman.h"
+
 namespace Bolt {
 
 struct DisplaySpecs;
 class BoltEngine;
 
-typedef struct XPCycleSpec {
-	short startIndex; // +0x00: first palette index
-	short endIndex;   // +0x02: last palette index
-	short delay;      // +0x04: cycle period in ms
-} XPCycleSpec;        // 6 bytes
+struct XPCycleState {
+	int16 startIndex;
+	int16 endIndex;
+	int16 delay;     // ms between rotations
+	uint32 nextFire; // getMillis() deadline for next rotation
+	bool active;
 
-typedef struct XPSurface {
-	byte *pixelData;     // +0x00: pixel buffer (width * height bytes, 8bpp)
-	int16 width;         // +0x04: surface width
-	int16 height;        // +0x06: surface height
-	byte *palette;       // +0x08: RGB palette data (127 * 3 = 381 bytes)
-	int16 paletteStart;  // +0x0C: first palette index (1)
-	int16 paletteCount;  // +0x0E: number of palette entries (127)
-	int16 flags;         // +0x10
-	long overlayData;    // +0x12
-	char _pad[0x10];     // +0x16..+0x23 (unknown)
-	int16 clipX;         // +0x24
-	int16 clipY;         // +0x26
-} XPSurface;             // total: 0x28 = 40 bytes
+	XPCycleState() {
+		startIndex = 0;
+		endIndex = 0;
+		delay = 0;
+		nextFire = 0;
+		active = false;
+	}
+};
 
 typedef struct XPPicDesc {
 	byte *pixelData;     // +0x00: pixel buffer (8bpp)
@@ -57,33 +58,93 @@ typedef struct XPPicDesc {
 	int16 paletteStart;  // +0x0C: first palette index
 	int16 paletteCount;  // +0x0E: number of palette entries
 	int16 flags;         // +0x10: bit 0 = transparent, bit 1 = RLE
-} XPPicDesc;             // 0x12 = 18 bytes
 
-typedef void (*BlitFunc)(void *src, int16 srcStride, void *dest, int16 destStride, int16 width, int16 height);
+	XPPicDesc() {
+		pixelData = nullptr;
+		width = 0;
+		height = 0;
+		palette = nullptr;
+		paletteStart = 0;
+		paletteCount = 0;
+		flags = 0;
+	}
+} XPPicDesc;
+
+typedef struct XPSurface {
+	XPPicDesc mainPic;
+	XPPicDesc overlayPic;
+	int16 dirtyPalStart; // +0x24
+	int16 dirtyPalEnd;   // +0x26
+
+	XPSurface() {
+		dirtyPalStart = 0;
+		dirtyPalEnd = 0;
+	}
+} XPSurface;             // total: 0x28 = 40 bytes
+
+enum XPEventTypes : int16 {
+	etEmpty      = 0,
+	etTimer      = 1,
+	etMouseMove  = 2,
+	etMouseDown  = 3,
+	etMouseUp    = 4,
+	etJoystick   = 5,
+	etSound      = 6,
+	etInactivity = 7
+};
+
+enum XPEventKeyStates : int16 {
+	eksInputOff     = 0,
+	eksMouseMode    = 1,
+	eksJoystickMode = 2
+};
+
+enum CustomEventType {
+	EVENT_TIMER = Common::EVENT_USER_FIRST_AVAILABLE
+};
+
+typedef struct XPEvent {
+	XPEvent *prev;
+	XPEvent *next;
+	XPEventTypes type;
+	uint32 payload;
+
+	XPEvent() {
+		prev = nullptr;
+		next = nullptr;
+		type = etEmpty;
+		payload = 0;
+	}
+} XPEvent;
+
+typedef struct XPTimer {
+	uint32 id;
+	uint32 deadline;
+	bool active;
 
-typedef struct ClipRect {
-	short left;
-	short top;
-	short right;
-	short bottom;
-} ClipRect;
+	XPTimer() {
+		id = 0;
+		deadline = 0;
+		active = false;
+	}
+} XPTimer;
 
 class XpLib {
 public:
 	XpLib(BoltEngine *bolt);
 	~XpLib();
 
-	void initialize();
+	bool initialize();
 	void terminate();
 
 	// Blit
-	void blit(void *src, uint16 srcStride, void *dst, uint16 dstStride, uint16 width, uint16 height);
-	void maskBlit(void *src, uint16 srcStride, void *dst, uint16 dstStride, uint16 width, uint16 height);
+	void blit(byte *src, uint16 srcStride, byte *dst, uint16 dstStride, uint16 width, uint16 height);
+	void maskBlit(byte *src, uint16 srcStride, byte *dst, uint16 dstStride, uint16 width, uint16 height);
 
 	// Palette
-	void getPalette(uint16 startIndex, uint16 count, void *destBuf);
-	void setPalette(uint16 count, uint16 startIndex, void *srcBuf);
-	bool startCycle(XPCycleSpec *specs);
+	void getPalette(uint16 startIndex, uint16 count, byte *destBuf);
+	void setPalette(uint16 count, uint16 startIndex, byte *srcBuf);
+	bool startCycle(XPCycleState *specs);
 	void cycleColors();
 	void stopCycle();
 	void setScreenBrightness(uint8 percent);
@@ -91,7 +152,7 @@ public:
 	// Cursor
 	bool readCursor(uint16 *outButtons, int16 *outX, int16 *outY);
 	void setCursorPos(int16 x, int16 y);
-	void setCursorImage(void *bitmap, int16 hotspotX, int16 hotspotY);
+	void setCursorImage(byte *bitmap, int16 hotspotX, int16 hotspotY);
 	void setCursorColor(byte r, byte g, byte b);
 	bool showCursor();
 	void hideCursor();
@@ -100,9 +161,9 @@ public:
 	// Events
 	int16 getEvent(int16 filter, uint32 *outData);
 	int16 peekEvent(int16 filter, uint32 *outData);
-	void postEvent(int16 type, uint32 data);
+	void postEvent(XPEventTypes type, uint32 data);
 	int16 setInactivityTimer(int16 seconds);
-	int16 setScreenSaverTimer(int16 time);
+	int16 setScreenSaverTimer(int16 seconds);
 	bool enableController();
 	void disableController();
 
@@ -120,55 +181,77 @@ public:
 	void randomize();
 
 	// File
-	int32 createFile(const char *fileName);
-	void deleteFile(const char *fileName);
-	int32 openFile(const char *fileName, short flags);
-	void closeFile(int32 handle);
-	bool readFile(int32 handle, void *buffer, uint32 *size);
-	bool setFilePos(int32 handle, uint32 offset, int16 origin);
+	Common::File *openFile(const char *fileName, short flags);
+	void closeFile(Common::File *handle);
+	bool readFile(Common::File *handle, void *buffer, uint32 *size);
+	bool setFilePos(Common::File *handle, int32 offset, int32 origin);
 	void *allocMem(uint32 size);
 	void *tryAllocMem(uint32 size);
 	void freeMem(void *mem);
 
 	// Sound
 	void waveCb();
-	bool playSound(void *data, uint32 size, int16 sampleRate);
+	bool playSound(byte *data, uint32 size, int16 sampleRate);
 	bool pauseSound();
 	bool resumeSound();
 	bool stopSound();
 
 	// Timer
-	int32 startTimer(int16 delay);
-	void timeCb();
-	bool killTimer(int32 timerId);
+	uint32 startTimer(int16 delay);
+	void updateTimers();
+	bool killTimer(uint32 timerId);
 
 protected:
-	BoltEngine *_bolt;
+	BoltEngine *_bolt = nullptr;
+	bool g_xpInitialized = false;
 
 	// Blit
-	void dirtyBlit(void *src, void *dst, void *dirtyFlags, uint16 width, uint16 height);
-	uint32 compositeBlit(void *src, void *background, void *dst, uint16 stride, uint16 width, uint16 height);
-	void rleBlit(void *src, void *dst, uint16 dstStride, uint16 width, uint16 height);
-	void rleMaskBlit(void *src, void *dst, uint16 dstStride, uint16 width, uint16 height);
-	uint32 rleCompositeBlit(void *rle, void *background, void *dst, uint16 width, uint16 height, void *dirtyFlags);
-	uint16 rleDataSize(void *rleData, uint16 height);
-	void markCursorPixels(void *buffer, uint32 count);
+	void dirtyBlit(byte *src, byte *dst, uint16 width, uint16 height, byte *dirtyFlags);
+	void compositeBlit(byte *src, byte *background, byte *dst, uint16 stride, uint16 width, uint16 height);
+	void rleBlit(byte *src, uint16 srcStride, byte *dst, uint16 dstStride, uint16 width, uint16 height);
+	void rleMaskBlit(byte *src, uint16 srcStride, byte *dst, uint16 dstStride, uint16 width, uint16 height);
+	void rleCompositeBlit(byte *rle, byte *background, byte *dst, uint16 width, uint16 height, byte *dirtyFlags);
+	uint16 rleDataSize(byte *rleData, uint16 height);
+	void markCursorPixels(byte *buffer, uint32 count);
+
+	// Palette
+	XPCycleState g_cycleStates[4];
+	byte g_paletteBuffer[3 * 256];
+	byte g_shiftedPaletteBuffer[3 * 256];
+	byte g_cycleTempPalette[3 * 20];
+	uint32 g_cycleTimerIds[4];
+	int16 g_brightnessShift = 0;
+	Common::Mutex g_paletteMutex;
+
+	static void cycleColorsCallback(void *refConf);
 
 	// Cursor
-	bool initCoords();
-	void shutdownCoords();
+	bool initCursor();
+	void shutdownCursor();
 	void readJoystick(int16 *outX, int16 *outY);
 
+	byte g_cursorBuffer[16 * 16];
+	int16 g_cursorWidth = 0;
+	int16 g_cursorHeight = 0;
+	int16 g_cursorScale = 0;
+	int16 g_cursorHotspotX = 0;
+	int16 g_cursorHotspotY = 0;
+	int16 g_lastCursorX = 0;
+	int16 g_lastCursorY = 0;
+	int16 g_cursorViewportWidth = 0;
+	int16 g_cursorViewportHeight = 0;
+	int16 g_cursorHidden = 1;
+
 	// Events
-	bool initWindow();
-	void shutdownWindow();
-	void unlinkEvent(int16 node);
-	void enqueueEvent(int16 node);
+	bool initEvents();
+	void shutdownEvents();
+	void unlinkEvent(XPEvent *node);
+	void enqueueEvent(XPEvent *node);
 	void pumpMessages();
 	void handleTimer(uint32 timerId);
 	void handleMouseMove(bool *mouseMoved);
 	void handleMouseButton(int16 down, int16 button);
-	void handleKey(int16 vkey, int16 down);
+	void handleKey(Common::KeyCode vkey, int16 down);
 	void postJoystickEvent(int16 source, int16 dx, int16 dy);
 	bool canDropEvent(int16 type);
 	void activateScreenSaver();
@@ -177,27 +260,81 @@ protected:
 	void disableMouse();
 	int16 getButtonState();
 
+	XPEvent g_events[40];
+
+	int8 g_keyStateLeft = 0;
+	int8 g_keyStateRight = 0;
+	int8 g_keyStateUp = 0;
+	int8 g_keyStateDown = 0;
+	int16 g_lastJoystickX = 0;
+	int16 g_lastJoystickY = 0;
+	int16 g_mouseButtonPrev = 0;
+	int16 g_mouseButtonState = 0;
+	int16 g_eventMouseMoved = 0;
+	int16 g_eventKeyStates = 0;
+	uint32 g_inactivityDeadline = 0;
+
+	XPEvent *g_eventQueueHead = nullptr;
+	XPEvent *g_eventQueueTail = nullptr;
+	XPEvent *g_eventFreeList = nullptr;
+
+	uint32 g_lastMouseEventData = 0;
+	Common::Point g_lastRegisteredMousePos;
+
 	// Display
-	int16 switchDisplayMode(int16 mode);
 	bool initDisplay();
 	void shutdownDisplay();
 	bool createSurface(XPSurface *surf);
 	void freeSurface(XPSurface *surf);
 	void virtualToScreen(int16 *x, int16 *y);
 	void screenToVirtual(int16 *x, int16 *y);
-	bool clipAndBlit(XPPicDesc *src, XPSurface *dest, int16 x, int16 y, ClipRect *outClip);
-	void addDirtyRect(ClipRect *rect);
+	void dispatchBlit(int16 mode, byte *src, uint16 srcStride, byte *dst, uint16 dstStride, uint16 width, uint16 height);
+	bool clipAndBlit(XPPicDesc *src, XPSurface *dest, int16 x, int16 y, Common::Rect *outClip);
+	void addDirtyRect(Common::Rect *rect);
 	void waitForFrameRate();
 	void handlePaletteTransitions();
 	void flushPalette();
 	void overlayComposite();
 	void compositeToScreen();
 	void mergeDirtyRects();
-	void blitDirtyRects(ClipRect *rects, int16 count);
-	void compositeDirtyRects(ClipRect *rects, int16 count);
-	void applyCursorPalette(bool enable);
+	void blitDirtyRects(Common::Rect *rects, int16 count);
+	void compositeDirtyRects(Common::Rect *rects, int16 count);
+	void applyCursorPalette();
 	void prepareBackSurface();
 
+	Graphics::Screen *_screen = nullptr;
+
+	XPSurface g_surfaces[2];
+
+	int16 g_virtualWidth = 0;
+	int16 g_virtualHeight = 0;
+	int16 g_currentDisplayPage = 0;
+	int16 g_spriteOverlayActive = 0;
+	int16 g_spriteOverlayEnabled = 0;
+	int16 g_frameRateFPS = 0;
+	int16 g_overlayCount = 0;
+	int16 g_prevDirtyCount = 0;
+	int16 g_prevDirtyValid = 0;
+	int16 g_surfaceWidth = 0;
+	int16 g_surfaceHeight = 0;
+	int16 g_viewportOffsetX = 0;
+	int16 g_viewportOffsetY = 0;
+	uint32 g_nextFrameTime = 0;
+
+	Common::Rect g_dirtyRects[30];
+	Common::Rect g_prevDirtyRects[30];
+	Common::Rect g_cursorDirtyRect;
+	Common::Rect g_prevCursorRect;
+	Common::Rect g_cursorClipRect2;
+	Common::Rect g_prevCursorRect2;
+
+	byte g_vgaFramebuffer[320 * 200];
+	byte g_cursorBackgroundSaveBuffer[16 * 16];
+	byte g_rowDirtyFlags[200];
+
+	XPPicDesc g_cursorBackgroundSave;
+	XPPicDesc g_cursorSprite;
+
 	// File
 	void fileError(const char *message);
 
@@ -209,6 +346,18 @@ protected:
 	// Timer
 	bool initTimer();
 	void shutdownTimer();
+
+	uint32 g_inactivityTimerId = 0;
+	int16 g_inactivityCountdown = 0;
+	int16 g_inactivityTimerValue = 0;
+	int16 g_cursorBlinkCountdown = 0;
+	int16 g_screenSaverTimerValue = 0;
+	int16 g_inactivityTimeout = 0;
+
+	bool g_timerInitialized = false;
+
+	XPTimer g_timers[128];
+	uint16 g_nextTimerId = 0;
 };
 
 } // End of namespace Bolt


Commit: e358ac0d4615d956b4506f1c55553c37283b5d13
    https://github.com/scummvm/scummvm/commit/e358ac0d4615d956b4506f1c55553c37283b5d13
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-04-07T01:11:14+02:00

Commit Message:
BOLT: CARNIVAL: Implement main part of the game engine

Changed paths:
  A engines/bolt/av.cpp
  A engines/bolt/barker.cpp
  A engines/bolt/booths/fred.cpp
  A engines/bolt/booths/george.cpp
  A engines/bolt/booths/huck.cpp
  A engines/bolt/booths/scooby.cpp
  A engines/bolt/booths/topcat.cpp
  A engines/bolt/booths/yogi.cpp
  A engines/bolt/rtf.cpp
  A engines/bolt/ssprite.cpp
  A engines/bolt/state.cpp
  A engines/bolt/swap.cpp
  A engines/bolt/utils.cpp
  R engines/bolt/gfx.cpp
    engines/bolt/anim.cpp
    engines/bolt/bolt.cpp
    engines/bolt/bolt.h
    engines/bolt/booth.cpp
    engines/bolt/detection.h
    engines/bolt/detection_tables.h
    engines/bolt/metaengine.cpp
    engines/bolt/module.mk
    engines/bolt/resource.cpp
    engines/bolt/xplib/blit.cpp
    engines/bolt/xplib/cursor.cpp
    engines/bolt/xplib/display.cpp
    engines/bolt/xplib/events.cpp
    engines/bolt/xplib/file.cpp
    engines/bolt/xplib/palette.cpp
    engines/bolt/xplib/sound.cpp
    engines/bolt/xplib/xplib.cpp
    engines/bolt/xplib/xplib.h


diff --git a/engines/bolt/anim.cpp b/engines/bolt/anim.cpp
index fb1fd5bdb65..9c869224c5e 100644
--- a/engines/bolt/anim.cpp
+++ b/engines/bolt/anim.cpp
@@ -23,98 +23,70 @@
 
 namespace Bolt {
 
-void *BoltEngine::openRTF(const char *fileName) {
-	return nullptr;
-}
-
-void BoltEngine::closeRTF(void *rtf) {
-}
-
-bool BoltEngine::playRTF(void *rtfFile, int16 animIndex, void *ringBuffer, int32 bufferSize) {
-	return false;
-}
-
-bool BoltEngine::fillRTFBuffer() {
-	return false;
-}
-
-void BoltEngine::flushRTFSoundQueue() {
-}
-
-bool BoltEngine::maintainRTF(int16 mode, void *outFrameData) {
-	return false;
-}
-
-bool BoltEngine::isRTFPlaying() {
-	return false;
-}
-
-void BoltEngine::killRTF() {
-}
-
-void BoltEngine::readPacket() {
-
-}
-
-void BoltEngine::preProcessPacket() {
-}
+bool BoltEngine::startAnimation(RTFResource *rtf, int16 animIndex) {
+	if (!initAnim(rtf, animIndex)) {
+		cleanUpAnim();
+		return false;
+	}
 
-void BoltEngine::queuePacket() {
+	return true;
 }
 
-void BoltEngine::deQueuePacket() {
+void BoltEngine::stopAnimation() {
+	killRTF(nullptr);
+	cleanUpAnim();
 }
 
-void BoltEngine::allocPacket() {
-}
-
-void BoltEngine::freePacket() {
-}
-
-void BoltEngine::resetPlaybackState() {
-}
+bool BoltEngine::maintainAudioPlay(int16 mode) {
+	RTFPacket *frameData;
 
-void BoltEngine::sub_12980() {
-}
-
-void BoltEngine::prepareAV() {
-}
-
-void BoltEngine::maintainAV() {
-}
+	if (!maintainRTF(mode, &frameData)) {
+		cleanUpAnim();
+		return false;
+	}
 
-void BoltEngine::stopAV() {
-}
+	// Check for "TRGR" trigger marker in frame data
+	if (frameData && frameData->tag == MKTAG('T', 'R', 'G', 'R')) {
+		_xp->postEvent(etTrigger, 0);
+	}
 
-bool BoltEngine::playAV(void *rtfHandle, int16 animIndex, int16 width, int16 height, int16 xOff, int16 yOff) {
-	return false;
+	return true;
 }
 
-void BoltEngine::processPacket() {
-}
+bool BoltEngine::initAnim(RTFResource *rtf, int16 animIndex) {
+	int32 bufSize;
 
-void BoltEngine::processRL7() {
-}
+	g_animPrevInactivityTimer = _xp->setInactivityTimer(0);
 
-void BoltEngine::processPLTE() {
-}
+	// Try 80KB ring buffer, fall back to 40KB
+	bufSize = 0x14000;
+	g_animRingBuffer = (byte *)_xp->tryAllocMem(bufSize);
+	if (!g_animRingBuffer) {
+		bufSize = 0xA000;
+		g_animRingBuffer = (byte *)_xp->allocMem(bufSize);
+	}
 
-void BoltEngine::initAV() {
-}
+	if (!g_animRingBuffer)
+		return false;
 
-void BoltEngine::cleanUpAV() {
-}
+	if (!playRTF(rtf, animIndex, g_animRingBuffer, bufSize))
+		return false;
 
-void BoltEngine::startAnimation() {
+	return true;
 }
 
-void BoltEngine::maintainAudioPlay() {
-}
+void BoltEngine::cleanUpAnim() {
+	if (g_animRingBuffer) {
+		_xp->freeMem(g_animRingBuffer);
+		g_animRingBuffer = nullptr;
+	}
 
-void BoltEngine::initAnim() {
-}
+	if (g_animFileHandle) {
+		_xp->closeFile(g_animFileHandle);
+		g_animFileHandle = nullptr;
+	}
 
-void BoltEngine::cleanUpAnim() {
+	_xp->setInactivityTimer(g_animPrevInactivityTimer);
 }
 
 } // End of namespace Bolt
diff --git a/engines/bolt/av.cpp b/engines/bolt/av.cpp
new file mode 100644
index 00000000000..c5233c5b443
--- /dev/null
+++ b/engines/bolt/av.cpp
@@ -0,0 +1,330 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "bolt/bolt.h"
+
+namespace Bolt {
+
+void BoltEngine::setAVBufferSize(uint32 bufSize) {
+	g_avTargetBufSize = bufSize;
+}
+
+bool BoltEngine::prepareAV(RTFResource *rtfHandle, int16 animIndex, int16 width, int16 height, int16 xOff, int16 yOff) {
+	if (!initAV(rtfHandle, animIndex, width, height, xOff, yOff)) {
+		cleanUpAV();
+		return false;
+	}
+
+	return true;
+}
+
+bool BoltEngine::maintainAV(int16 mode) {
+	RTFPacket *frameData;
+
+	if (!maintainRTF(mode, &frameData)) {
+		cleanUpAV();
+		return false;
+	}
+
+	if (frameData)
+		processPacket(frameData);
+
+	return true;
+}
+
+void BoltEngine::stopAV() {
+	killRTF(nullptr);
+	cleanUpAV();
+}
+
+bool BoltEngine::playAV(RTFResource *rtfHandle, int16 animIndex, int16 width, int16 height, int16 xOff, int16 yOff) {
+	RTFPacket *frameData = nullptr;
+	uint32 eventBuf = 0;
+	int16 eventType = 0;
+
+	// Init if not already loaded...
+	if (!g_avRingBuffer) {
+		if (!initAV(rtfHandle, animIndex, width, height, xOff, yOff)) {
+			cleanUpAV();
+			return false;
+		}
+	}
+
+	// Flush pending mouse-down events...
+	while (_xp->getEvent(etMouseDown, &eventBuf) != etEmpty && !shouldQuit()) {
+		// getEvent already calls pollEvent and delayMillis
+	}
+
+	// Main playback loop...
+	while (!shouldQuit()) {
+		eventType = _xp->getEvent(etEmpty, &eventBuf);
+
+		if (eventType == etMouseDown) {
+			// Mouse click, abort playback...
+			killRTF(nullptr);
+			cleanUpAV();
+			return false;
+		}
+
+		if (eventType != etSound && eventType != etEmpty)
+			continue;
+
+		// Advance: mode=1 on sound event, mode=0 on idle
+		if (!maintainRTF(eventType == etSound ? 1 : 0, &frameData)) {
+			cleanUpAV();
+			return true; // finished normally
+		}
+
+		if (frameData)
+			processPacket(frameData);
+	}
+
+	// Not in the original, but it helps cleaning up
+	// if the user wants to quit during a video...
+	killRTF(nullptr);
+	cleanUpAV();
+	return false;
+}
+
+void BoltEngine::processPacket(RTFPacket *packet) {
+	uint32 tag = packet->tag;
+
+	// Adaptive frame rate control...
+	if (tag == MKTAG('R', 'L', '7', ' ') || tag == MKTAG('R', 'L', '7', 'F')) {
+		int16 frameRate = packet->frameRate;
+
+		// Skip this frame if: low framerate, only 1 behind, or haven't accumulated enough...
+		if (frameRate <= 10 || packet->skipCount > 1 || g_avFrameAccum < g_avSkipLevel) {
+			g_avFrameAccum += packet->duration;
+			warning("BoltEngine::processPacket() WANTS TO RETURN BUT IT WILL SKIP FRAMES");
+			return;
+		}
+
+		// Adjust skip level based on how far behind we are...
+		if (g_avFrameAccum > g_avSkipLevel)
+			g_avSkipLevel = (g_avFrameAccum > 4) ? 4 : g_avFrameAccum;
+
+		g_avFrameAccum = 0;
+
+		int16 targetSkip;
+		if (frameRate <= 12)
+			targetSkip = 4;
+		else if (frameRate <= 15)
+			targetSkip = 3;
+		else if (frameRate <= 20)
+			targetSkip = 2;
+		else
+			targetSkip = 1;
+
+		// If keeping up fine, stop skipping entirely...
+		if (g_avSkipLevel <= 1 && targetSkip == 1 && packet->skipCount == 0) {
+			g_avSkipLevel = 0;
+		} else {
+			// Ramp toward target skip level...
+			if (targetSkip > g_avSkipLevel)
+				g_avSkipLevel = targetSkip;
+			else if (targetSkip < g_avSkipLevel)
+				g_avSkipLevel--;
+		}
+	} else if (tag == MKTAG('r', 'l', '7', ' ') || tag == MKTAG('r', 'l', '7', 'f')) {
+		g_avFrameAccum = 0;
+	}
+
+	// Process frame, palette and trigger packets...
+	tag = packet->tag;
+
+	if (tag == MKTAG('R', 'L', '7', ' ') || tag == MKTAG('R', 'L', '7', 'B') || tag == MKTAG('R', 'L', '7', 'F') ||
+		tag == MKTAG('r', 'l', '7', ' ') || tag == MKTAG('r', 'l', '7', 'f')) {
+		processRL7(packet);
+	} else if (tag == MKTAG('P', 'L', 'T', 'B') || tag == MKTAG('P', 'L', 'T', 'E') || tag == MKTAG('P', 'L', 'T', 'F')) {
+		processPLTE(packet);
+	} else if (tag == MKTAG('T', 'R', 'G', 'R')) {
+		_xp->postEvent(etTrigger, 0);
+	}
+}
+
+void BoltEngine::processRL7(RTFPacket *packet) {
+	XPPicDesc *displayDesc;
+	int page;
+	bool transparency;
+	bool updateDisplay;
+
+	uint32 tag = packet->tag;
+
+	if (tag == MKTAG('R', 'L', '7', 'B')) {
+		displayDesc = &g_avBackBufDesc;
+	} else {
+		displayDesc = &g_avFrontBufDesc;
+	}
+
+	switch (tag) {
+	case MKTAG('R', 'L', '7', ' '):
+	case MKTAG('r', 'l', '7', ' '):
+		page = stFront;
+		transparency = false;
+		updateDisplay = true;
+		break;
+	case MKTAG('R', 'L', '7', 'F'):
+	case MKTAG('r', 'l', '7', 'f'):
+		page = stFront;
+		transparency = true;
+		updateDisplay = true;
+		break;
+	case MKTAG('R', 'L', '7', 'B'):
+		page = stBack;
+		transparency = true;
+		updateDisplay = false;
+		break;
+	default:
+		return;
+	}
+
+	_xp->setTransparency(transparency);
+
+	displayDesc->pixelData = packet->dataPtr;
+
+	_xp->displayPic(displayDesc, g_avDisplayX, g_avDisplayY, page);
+
+	if (updateDisplay)
+		_xp->updateDisplay();
+
+	g_avFrontBufDesc.palette = nullptr;
+	g_avFrontBufDesc.paletteStart = 0;
+	g_avFrontBufDesc.paletteCount = 0;
+
+	if (transparency) {
+		g_avBackBufDesc.palette = nullptr;
+		g_avBackBufDesc.paletteStart = 0;
+		g_avBackBufDesc.paletteCount = 0;
+	}
+}
+
+void BoltEngine::processPLTE(RTFPacket *packet) {
+	byte *srcData = packet->dataPtr;
+
+	int16 startIndex = READ_BE_INT16(srcData); srcData += 2;
+	int16 count = READ_BE_INT16(srcData); srcData += 2;
+
+	XPPicDesc *desc;
+	byte *palBuffer;
+
+	if (packet->tag == MKTAG('P', 'L', 'T', 'B')) {
+		desc = &g_avBackBufDesc;
+		palBuffer = g_avFrontPalette;
+	} else {
+		desc = &g_avFrontBufDesc;
+		palBuffer = g_avBackPalette;
+	}
+
+	desc->paletteStart = startIndex;
+	desc->paletteCount = count;
+	desc->palette = palBuffer;
+
+	// Copy RGB triplets into palette buffer...
+	while (count-- > 0) {
+		palBuffer[0] = *srcData++;
+		palBuffer[1] = *srcData++;
+		palBuffer[2] = *srcData++;
+		palBuffer += 3;
+	}
+}
+
+bool BoltEngine::initAV(RTFResource *rtfHandle, int16 animIndex, int16 width, int16 height, int16 xOff, int16 yOff) {
+	uint32 savedAllocSize = g_avTargetBufSize;
+	uint32 allocSize = 0;
+
+	g_avTargetBufSize = 0xFA000; // ~1MB
+
+	g_avDisplayY = yOff;
+	g_avDisplayX = xOff;
+
+	g_avSavedInactivityTimer = _xp->setInactivityTimer(0);
+	g_avSavedScreenSaverTimer = _xp->setScreenSaverTimer(0);
+
+	g_avBackPalette = nullptr;
+	g_avFrontPalette = nullptr;
+	g_avFrameAccum = 0;
+
+	// Allocate two 384-byte palette buffers (128 RGB triplets each)
+	g_avBackPalette = (byte *)_xp->allocMem(128 * 3);
+	if (!g_avBackPalette)
+		return false;
+
+	g_avFrontPalette = (byte *)_xp->allocMem(128 * 3);
+	if (!g_avFrontPalette)
+		return false;
+
+	// Allocate ring buffer: try saved size, shrink by 50KB until 300KB minimum
+	allocSize = savedAllocSize;
+	do {
+		g_avRingBuffer = (byte *)_xp->tryAllocMem(allocSize);
+		if (g_avRingBuffer)
+			break;
+
+		allocSize -= 0xC800;
+	} while (allocSize >= 0x4B000);
+
+	if (!g_avRingBuffer) {
+		error("BoltEngine::initAV(): Not enough memory");
+		return false;
+	}
+
+	g_avBackBufDesc.width = width;
+	g_avFrontBufDesc.width = width;
+	g_avBackBufDesc.height = height;
+	g_avFrontBufDesc.height = height;
+	g_avBackBufDesc.flags = 1;
+	g_avFrontBufDesc.flags = 1;
+	g_avBackBufDesc.palette = nullptr;
+	g_avFrontBufDesc.palette = nullptr;
+	g_avBackBufDesc.paletteStart = 0;
+	g_avFrontBufDesc.paletteStart = 0;
+	g_avBackBufDesc.paletteCount = 0;
+	g_avFrontBufDesc.paletteCount = 0;
+
+	// Open and start streaming!
+	if (!playRTF(rtfHandle, animIndex, g_avRingBuffer, allocSize))
+		return false;
+
+	return true;
+}
+
+void BoltEngine::cleanUpAV() {
+	if (g_avRingBuffer) {
+		_xp->freeMem(g_avRingBuffer);
+		g_avRingBuffer = nullptr;
+	}
+
+	if (g_avFrontPalette) {
+		_xp->freeMem(g_avFrontPalette);
+		g_avFrontPalette = nullptr;
+	}
+
+	if (g_avBackPalette) {
+		_xp->freeMem(g_avBackPalette);
+		g_avBackPalette = nullptr;
+	}
+
+	_xp->setInactivityTimer(g_avSavedInactivityTimer);
+	_xp->setScreenSaverTimer(g_avSavedScreenSaverTimer);
+}
+
+} // End of namespace Bolt
diff --git a/engines/bolt/barker.cpp b/engines/bolt/barker.cpp
new file mode 100644
index 00000000000..91f663a9979
--- /dev/null
+++ b/engines/bolt/barker.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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "bolt/bolt.h"
+
+namespace Bolt {
+
+BarkerTable *BoltEngine::createBarker(int16 minIndex, int16 maxIndex) {
+	bool success = false;
+
+	BarkerTable *table = new BarkerTable();
+	if (table) {
+		table->minIndex = minIndex;
+		table->maxIndex = maxIndex;
+		table->count = maxIndex - minIndex + 1;
+
+		table->handlers = new SideShowHandler[table->count];
+		if (!table->handlers) {
+			freeBarker(table);
+		} else {
+			success = true;
+			for (int i = 0; i < table->count; i++) {
+				table->handlers[i] = nullptr;
+			}
+		}
+	}
+
+	if (success) {
+		g_curErrorCode = 0;
+		return table;
+	} else {
+		g_curErrorCode = 1;
+		return nullptr;
+	}
+}
+
+void BoltEngine::freeBarker(BarkerTable *table) {
+	if (!table)
+		return;
+
+	if (table->handlers)
+		delete[] table->handlers;
+
+	delete table;
+}
+
+bool BoltEngine::registerSideShow(BarkerTable *table, SideShowHandler handler, int16 boothId) {
+	if (table->minIndex > boothId || table->maxIndex < boothId) {
+		g_curErrorCode = 5;
+		return false;
+	}
+
+	table->handlers[boothId - table->minIndex] = handler;
+
+	g_curErrorCode = 0;
+	return true;
+}
+
+int16 BoltEngine::barker(BarkerTable *table, int16 startBooth) {
+	int16 prevBooth = 0;
+	int16 currentBooth = startBooth;
+
+	while (true) {
+		SideShowHandler handler = table->handlers[currentBooth - table->minIndex];
+
+		int16 nextBooth = (this->*handler)(prevBooth);
+
+		if (nextBooth == 0)
+			break;
+
+		prevBooth = currentBooth;
+		currentBooth = nextBooth;
+	}
+
+	return g_currentBoothId;
+}
+
+bool BoltEngine::checkError() {
+	return false;
+}
+
+} // End of namespace Bolt
diff --git a/engines/bolt/bolt.cpp b/engines/bolt/bolt.cpp
index e4683cdf938..4baa8fc5b72 100644
--- a/engines/bolt/bolt.cpp
+++ b/engines/bolt/bolt.cpp
@@ -41,6 +41,8 @@ BoltEngine::BoltEngine(OSystem *syst, const ADGameDescription *gameDesc) : Engin
 	_gameDescription(gameDesc), _randomSource("Bolt") {
 	g_engine = this;
 	_xp = new XpLib(this);
+
+	initCallbacks();
 }
 
 BoltEngine::~BoltEngine() {
@@ -62,7 +64,7 @@ Common::Error BoltEngine::run() {
 	}
 
 	// Initialize paletted graphics mode
-	if (g_extendedViewport) {
+	if (!g_extendedViewport) {
 		initGraphics(SCREEN_WIDTH, SCREEN_HEIGHT);
 	} else {
 		initGraphics(EXTENDED_SCREEN_WIDTH, EXTENDED_SCREEN_HEIGHT);
@@ -71,41 +73,9 @@ Common::Error BoltEngine::run() {
 	// Set the engine's debugger console
 	setDebugger(new Console());
 
-	//
-	//// If a savegame was selected from the launcher, load it
-	//int saveSlot = ConfMan.getInt("save_slot");
-	//if (saveSlot != -1)
-	//	(void)loadGameState(saveSlot);
-	//
-	//// Draw a series of boxes on screen as a sample
-	//for (int i = 0; i < 100; ++i)
-	//	_screen->frameRect(Common::Rect(i, i, 320 - i, 200 - i), i);
-	//_screen->update();
-	//
-	//// Simple event handling loop
-	//byte pal[256 * 3] = { 0 };
-	//Common::Event e;
-	//int offset = 0;
-	//
-	//Graphics::FrameLimiter limiter(g_system, 60);
-	//while (!shouldQuit()) {
-	//	while (g_system->getEventManager()->pollEvent(e)) {
-	//	}
-	//
-	//	// Cycle through a simple palette
-	//	++offset;
-	//	for (int i = 0; i < 256; ++i)
-	//		pal[i * 3 + 1] = (i + offset) % 256;
-	//	g_system->getPaletteManager()->setPalette(pal, 0, 256);
-	//	// Delay for a bit. All events loops should have a delay
-	//	// to prevent the system being unduly loaded
-	//	limiter.delayBeforeSwap();
-	//	_screen->update();
-	//	limiter.startFrame();
-	//}
-
 	_xp->initialize();
 	boltMain();
+	_xp->terminate();
 
 	return Common::kNoError;
 }
@@ -122,13 +92,11 @@ Common::Error BoltEngine::syncGame(Common::Serializer &s) {
 }
 
 void BoltEngine::boltMain() {
-	void *testAlloc;
+	byte *testAlloc;
 	BarkerTable *barkerTable;
-	void *boothSprite;
-
-	// g_callbackPtr = DS:0xCB; 
+	byte *boothSprite;
 
-	testAlloc = _xp->allocMem(0x100000);
+	testAlloc = (byte *)_xp->allocMem(0x100000);
 	if (!testAlloc)
 		return;
 
@@ -136,90 +104,86 @@ void BoltEngine::boltMain() {
 
 	_xp->randomize();
 
-	if (!allocResourceIndex())
-		goto cleanup;
-
-	g_boothsBoltLib = 0;
-
-	if (!openBOLTLib(&g_boothsBoltLib, &g_boothsBoltIndex, AssetPath("booths.blt")))
-		goto cleanup;
-
-	if (!_xp->chooseDisplaySpec(&g_displayMode, 2, g_displaySpecs))
-		goto cleanup;
-
-	g_displayWidth = g_displaySpecs[g_displayMode].width;
-	g_displayHeight = g_displaySpecs[g_displayMode].height;
-
-	// Center within 384x240 virtual coordinate space...
-	g_displayX = (384 - g_displayWidth) / 2;
-	g_displayY = (240 - g_displayHeight) / 2;
-
-	_xp->setCoordSpec(g_displayX, g_displayY, g_displayWidth, g_displayHeight);
-
-	if (g_displayMode != 0)
-		g_rtfHandle = openRTF(AssetPath("booths.pal"));
-	else
-		g_rtfHandle = openRTF(AssetPath("booths4.pal"));
-
-	if (g_rtfHandle == nullptr)
-		goto cleanup;
-
-	playAV(g_rtfHandle, 0, g_displayWidth, g_displayHeight, g_displayX, g_displayY);
-
-	boothSprite = getBOLTMember(g_boothsBoltLib, (g_displayMode != 0) ? 0x1701 : 0x1702);
-
-	_xp->setTransparency(false);
-
-	displayColors(getBOLTMember(g_boothsBoltLib, 0x1700), 0, 0);
-	displayPic(boothSprite, g_displayX, g_displayY, 0);
-
-	_xp->updateDisplay();
-
-	displayColors(getBOLTMember(g_boothsBoltLib, 0x1700), 1, 0);
-	displayPic(boothSprite, g_displayX, g_displayY, 1);
-
-	playAV(g_rtfHandle, 2, g_displayWidth, g_displayHeight, g_displayX, g_displayY);
-
-	freeBOLTGroup(g_boothsBoltLib, 0x1700, 1);
-
-	if (!getBOLTGroup(g_boothsBoltLib, 0, 1))
-		goto cleanup;
-
-	setCursorPict(memberAddr(g_boothsBoltLib, 0));
-
-	if (!initVRam(1500))
-		goto cleanup;
-
-	barkerTable = createBarker(17, 2);
-	if (barkerTable == nullptr)
-		goto cleanup;
-
-	// Register booth handlers...
-	registerSideShow(barkerTable, &BoltEngine::hucksBooth, 3);
-	registerSideShow(barkerTable, &BoltEngine::fredsBooth, 4);
-	registerSideShow(barkerTable, &BoltEngine::scoobysBooth, 5);
-	registerSideShow(barkerTable, &BoltEngine::yogisBooth, 6);
-	registerSideShow(barkerTable, &BoltEngine::georgesBooth, 7);
-	registerSideShow(barkerTable, &BoltEngine::topCatsBooth, 8);
-	registerSideShow(barkerTable, &BoltEngine::mainEntrance, 9);
-	registerSideShow(barkerTable, &BoltEngine::huckGame, 10);
-	registerSideShow(barkerTable, &BoltEngine::fredGame, 11);
-	registerSideShow(barkerTable, &BoltEngine::scoobyGame, 12);
-	registerSideShow(barkerTable, &BoltEngine::yogiGame, 13);
-	registerSideShow(barkerTable, &BoltEngine::georgeGame, 14);
-	registerSideShow(barkerTable, &BoltEngine::topCatGame, 15);
-	registerSideShow(barkerTable, &BoltEngine::winALetter, 16);
-
-	g_lettersWon = 0;
-	_xp->setScreenSaverTimer(1800);
-
-	// The barker function runs the main loop, starting at mainEntrance()...
-	if (!checkError())
-		barker(barkerTable, 9);
-
-	freeBarker(barkerTable);
+	if (allocResourceIndex()) {
+		g_boothsBoltLib = nullptr;
+
+		if (openBOLTLib(&g_boothsBoltLib, &g_resourceDefaultCallbacks, assetPath("booths.blt"))) {
+			int16 chosenSpecId = g_extendedViewport ? 0 : 1;
+
+			if (_xp->setDisplaySpec(&g_displayMode, &g_displaySpecs[chosenSpecId])) {
+				assert(g_displayMode >= 0);
+				g_displayWidth = g_displaySpecs[g_displayMode].width;
+				g_displayHeight = g_displaySpecs[g_displayMode].height;
+
+				// Center within 384x240 virtual coordinate space...
+				g_displayX = (EXTENDED_SCREEN_WIDTH - g_displayWidth) / 2;
+				g_displayY = (EXTENDED_SCREEN_HEIGHT - g_displayHeight) / 2;
+
+				_xp->setCoordSpec(g_displayX, g_displayY, g_displayWidth, g_displayHeight);
+				
+				if (g_displayMode == 0) {
+					g_rtfHandle = openRTF(assetPath("cc_og.av"));
+				} else {
+					g_rtfHandle = openRTF(assetPath("cc_cr.av"));
+				}
+
+				if (g_rtfHandle) {
+					playAV(g_rtfHandle, 0, g_displayWidth, g_displayHeight, g_displayX, g_displayY);
+
+					boothSprite = getBOLTMember(g_boothsBoltLib, (g_displayMode != 0) ? 0x1701 : 0x1702);
+
+					_xp->setTransparency(false);
+
+					displayColors(getBOLTMember(g_boothsBoltLib, 0x1700), stFront, 0);
+					displayPic(boothSprite, g_displayX, g_displayY, stFront);
+
+					_xp->updateDisplay();
+
+					displayColors(getBOLTMember(g_boothsBoltLib, 0x1700), stBack, 0);
+					displayPic(boothSprite, g_displayX, g_displayY, stBack);
+
+					playAV(g_rtfHandle, 2, g_displayWidth, g_displayHeight, g_displayX, g_displayY);
+
+					freeBOLTGroup(g_boothsBoltLib, 0x1700, 1);
+
+					if (getBOLTGroup(g_boothsBoltLib, 0, 1)) {
+						setCursorPict(memberAddr(g_boothsBoltLib, 0));
+
+						if (initVRam(1500)) {
+							barkerTable = createBarker(2, 17);
+							if (barkerTable) {
+								// Register booth handlers...
+								registerSideShow(barkerTable, &BoltEngine::hucksBooth, 3);
+								registerSideShow(barkerTable, &BoltEngine::fredsBooth, 4);
+								registerSideShow(barkerTable, &BoltEngine::scoobysBooth, 5);
+								registerSideShow(barkerTable, &BoltEngine::yogisBooth, 6);
+								registerSideShow(barkerTable, &BoltEngine::georgesBooth, 7);
+								registerSideShow(barkerTable, &BoltEngine::topCatsBooth, 8);
+								registerSideShow(barkerTable, &BoltEngine::mainEntrance, 9);
+								registerSideShow(barkerTable, &BoltEngine::huckGame, 10);
+								registerSideShow(barkerTable, &BoltEngine::fredGame, 11);
+								registerSideShow(barkerTable, &BoltEngine::scoobyGame, 12);
+								registerSideShow(barkerTable, &BoltEngine::yogiGame, 13);
+								registerSideShow(barkerTable, &BoltEngine::georgeGame, 14);
+								registerSideShow(barkerTable, &BoltEngine::topCatGame, 15);
+								registerSideShow(barkerTable, &BoltEngine::winALetter, 16);
+
+								g_lettersWon = 0;
+								_xp->setScreenSaverTimer(1800);
+
+								// The barker function runs the main loop, starting at mainEntrance()...
+								if (!checkError())
+									barker(barkerTable, 9);
+
+								freeBarker(barkerTable);
+							}
+						}
+					}
+				}
+			}
+		}
+	}
 
-cleanup:
 	freeBOLTGroup(g_boothsBoltLib, 0, 1);
 
 	if (g_boothsBoltLib != 0)
@@ -232,21 +196,23 @@ cleanup:
 	freeResourceIndex();
 }
 
-BarkerTable *BoltEngine::createBarker(int16 minIndex, int16 maxIndex) {
-	return nullptr;
-}
-
-void BoltEngine::freeBarker(BarkerTable *table) {
-}
+void BoltEngine::setCursorPict(byte *sprite) {
+	byte cursorBitmap[32]; // 16x16
+	byte *srcPtr = getResolvedPtr(sprite, 0x12);
 
-void BoltEngine::registerSideShow(BarkerTable *table, SideShowHandler handler, short boothId) {
-}
+	// Convert 8bpp pixel data to 1bpp monochrome bitmap
+	for (int i = 0; i < 32; i++) {
+		cursorBitmap[i] = 0x00;
+		int16 mask = 0x80;
+		while (mask != 0x00) {
+			if (*srcPtr++ != 0x00)
+				cursorBitmap[i] |= (byte)mask;
 
-void BoltEngine::barker(BarkerTable *table, int16 startBooth) {
-}
+			mask >>= 1;
+		}
+	}
 
-bool BoltEngine::checkError() {
-	return false;
+	_xp->setCursorImage(cursorBitmap, 7, 7);
 }
 
 } // End of namespace Bolt
diff --git a/engines/bolt/bolt.h b/engines/bolt/bolt.h
index 4042d3f725b..c24dcb6fd5c 100644
--- a/engines/bolt/bolt.h
+++ b/engines/bolt/bolt.h
@@ -47,26 +47,185 @@ namespace Bolt {
 struct BoltGameDescription;
 class XpLib;
 
-struct DisplaySpecs {
+typedef struct DisplaySpecs {
+	int16 id;
 	int16 width;
 	int16 height;
-};
+} DisplaySpecs;
+
+typedef int16 (BoltEngine::*SideShowHandler)(int16 prevBooth);
 
 struct BarkerTable {
-	
+	SideShowHandler *handlers;
+	int16 count;
+	int16 minIndex;
+	int16 maxIndex;
 };
 
-struct BOLTLib {
-	int16 refCount;
-	int16 groupCount;
-	int32 fileHandle;
-	int32 *funcTable1;
-	int32 *funcTable2;
-	int32 *dwordPtr1;
-	int32 *dwordPtr2;
-	int32 *dwordPtr3;
-	int32 *dwordPtr4;
-};
+typedef struct BOLTHeader {
+	byte header[11];
+	byte groupCount;
+	uint32 groupDirOffset;
+
+	BOLTHeader() {
+		memset(header, 0, sizeof(header));
+		groupCount = 0;
+		groupDirOffset = 0;
+	}
+} BOLTHeader;
+
+typedef void (*BOLTCallback)(void);
+
+typedef struct BOLTCallbacks {
+	BOLTCallback *typeLoadCallbacks;   // +0x00: type-based post-load fixup (25 entries)
+	BOLTCallback *typeFreeCallbacks;   // +0x04: type-based post-free cleanup (25 entries)
+	BOLTCallback *memberLoadCallbacks; // +0x08: per-member pre-load callbacks
+	BOLTCallback *memberFreeCallbacks; // +0x0C: per-member pre-free callbacks
+	BOLTCallback *groupLoadCallbacks;  // +0x10: per-group load callbacks
+	BOLTCallback *groupFreeCallbacks;  // +0x14: per-group free callbacks
+
+	BOLTCallbacks() {
+		typeLoadCallbacks = nullptr;
+		typeFreeCallbacks = nullptr;
+		memberLoadCallbacks = nullptr;
+		memberFreeCallbacks = nullptr;
+		groupLoadCallbacks = nullptr;
+		groupFreeCallbacks = nullptr;
+	}
+} BOLTCallbacks;
+
+typedef struct BOLTMemberEntry {
+	byte flags;          // +0x00: bit 3 = uncompressed
+	byte preLoadCbIndex; // +0x01: index into lib->userData1 callback table
+	byte preFreeCbIndex; // +0x02: index into lib->userData2 callback table
+	byte typeCbIndex;    // +0x03: index into lib->loadCallbacks (post-load)
+						 //        and lib->freeCallbacks (post-free)
+	uint32 decompSize;   // +0x04: decompressed data size (big-endian in file)
+	uint32 fileOffset;   // +0x08: file offset of compressed/raw data (big-endian in file)
+	uint32 dataPtrPlaceholder;
+	byte *dataPtr; // +0x0C: pointer to loaded data (NULL if not loaded)
+
+	BOLTMemberEntry() {
+		flags = 0;
+		preLoadCbIndex = 0;
+		preFreeCbIndex = 0;
+		typeCbIndex = 0;
+		decompSize = 0;
+		fileOffset = 0;
+		dataPtrPlaceholder = 0;
+		dataPtr = nullptr;
+	}
+} BOLTMemberEntry; // 0x10 = 16 bytes
+
+typedef struct BOLTGroupEntry {
+	byte flags;                     // +0x00: unknown flags
+	byte loadCbIndex;               // +0x01: group load callback index (into lib+0x18)
+	byte freeCbIndex;               // +0x02: group free callback index (into lib+0x1C)
+	byte memberCount;               // +0x03: number of members (0 = 256)
+	uint32 memberDirOffset;         // +0x04: file offset to member directory (big-endian)
+	uint32 memberDataOffset;        // +0x08: file offset to member data (big-endian)
+	uint32 groupDataPtrPlaceholder; // +0x0C: pointer to loaded group data block (NULL if not loaded)
+	byte *memberData;
+	BOLTMemberEntry *members;
+
+	BOLTGroupEntry() {
+		flags = 0;
+		loadCbIndex = 0;
+		freeCbIndex = 0;
+		memberCount = 0;
+		memberDirOffset = 0;
+		memberDataOffset = 0;
+		groupDataPtrPlaceholder = 0;
+		memberData = nullptr;
+		members = nullptr;
+	}
+
+	~BOLTGroupEntry() {
+		free(memberData);
+		memberData = nullptr;
+
+		delete[] members;
+		members = nullptr;
+	}
+
+	void initMembers(int numMembers) {
+		int actualNumber = numMembers == 0 ? 256 : numMembers;
+		members = new BOLTMemberEntry[actualNumber];
+	}
+
+} BOLTGroupEntry;            // 0x10 = 16 bytes
+
+typedef struct BOLTLib {
+	int16 refCount;           // +0x00
+	int16 groupCount;         // +0x02 (0 = 256)
+	Common::File *fileHandle; // +0x04
+	BOLTCallbacks callbacks;
+	BOLTGroupEntry *groups; // +0x20
+
+	BOLTLib(int inGroupCount) {
+		refCount = 0;
+		groupCount = 0;
+		fileHandle = nullptr;
+		groups = new BOLTGroupEntry[inGroupCount];
+	}
+
+	~BOLTLib() {
+		delete[] groups;
+	}
+} BOLTLib;
+
+typedef struct RTFResource {
+	Common::File *fileHandle;
+	uint32 *indexTablePtr;
+	int16 entryCount;
+	byte *indexTableRawData;
+	uint32 *indexTable;
+
+	RTFResource() {
+		fileHandle = nullptr;
+		indexTablePtr = nullptr;
+		entryCount = 0;
+		indexTableRawData = nullptr;
+		indexTable = nullptr;
+	}
+
+	~RTFResource() {
+		if (indexTable)
+			delete[] indexTable;
+	}
+
+	void reserveTableSize(int numElements) {
+		indexTable = new uint32[numElements];
+	}
+} RTFResource;
+
+struct RTFPacket;
+typedef struct RTFPacket {
+	uint32 tag;       // +0x00: chunk tag (e.g. MKTAG('A','2','2','8'))
+	uint32 allocSize; // +0x04: total allocated size (including header)
+	uint32 dataSize;  // +0x08: raw data size
+	byte *dataPtr;    // +0x0C: pointer to data (= this + 0x1C)
+	int16 skipCount;  // +0x10: how late this frame is
+	int16 frameRate;    // +0x12: frame rate reference (or 50 if no sound)
+	int16 duration;   // +0x14: sound duration in hundredths of a second
+	int16 timestamp;  // +0x16: cumulative timeline position
+	RTFPacket *next;  // +0x18: next packet in linked list
+	// +0x1C: data begins here
+	byte *ringBufPtr;
+
+	RTFPacket() {
+		tag = 0;
+		allocSize = 0;
+		dataSize = 0;
+		dataPtr = nullptr;
+		skipCount = 0;
+		frameRate = 0;
+		duration = 0;
+		timestamp = 0;
+		next = nullptr;
+		ringBufPtr = nullptr;
+	}
+} RTFPacket; // 0x1C header = 28 bytes
 
 class BoltEngine : public Engine {
 friend class XpLib;
@@ -127,151 +286,303 @@ public:
 
 protected:
 	DisplaySpecs g_displaySpecs[2] = {
-		{384, 240},
-		{320, 200}
+		{0, 384, 240},
+		{1, 320, 200}
 	};
 
-	XpLib *_xp;
+	XpLib *_xp = nullptr;
 	bool g_extendedViewport = false;
 
-	// Entry point
+	// xpMain
 	void boltMain();
-
-	// Booths logic
-	typedef void (BoltEngine::*SideShowHandler)(void);
-
-	int g_lettersWon = 0;
-
-	void displayBooth();
-	void playAVOverBooth();
-	void hucksBooth();
-	void fredsBooth();
-	void scoobysBooth();
-	void yogisBooth();
-	void georgesBooth();
-	void topCatsBooth();
-	void mainEntrance();
-	void huckGame();
-	void fredGame();
-	void scoobyGame();
-	void yogiGame();
-	void georgeGame();
-	void topCatGame();
-	void winALetter();
-
-	void loadBooth();
+	void setCursorPict(byte *sprite);
+
+	// Booth
+	void startCycle(byte *cycleResource);
+	void displayBooth(int16 page);
+	void playAVOverBooth(int16 animIndex);
+	int16 hucksBooth(int16 prevBooth);
+	int16 fredsBooth(int16 prevBooth);
+	int16 scoobysBooth(int16 prevBooth);
+	int16 yogisBooth(int16 prevBooth);
+	int16 georgesBooth(int16 prevBooth);
+	int16 topCatsBooth(int16 prevBooth);
+	int16 mainEntrance(int16 prevBooth);
+	bool loadBooth(int16 boothId);
 	void unloadBooth();
-	void openBooth();
+	int16 openBooth(int16 sceneId);
 	void closeBooth();
 	void playTour();
-	void finishPlayingHelp();
-	void hotSpotActive();
+	void finishPlayingHelp(int16 activeHotspot);
+	int16 hotSpotActive(int16 hotspot);
 	void hoverHotSpot();
-	void boothEventLoop();
+	int16 boothEventLoop();
 	void resetInactivityState();
-	void handleButtonPress();
+	bool handleButtonPress(int16 hotspot);
+	void blastColors(byte **paletteTable, int16 index, int16 mode);
+	void setColors(int16 index);
+	void restoreColors(int16 index);
+	void loadColors();
+	void shiftColorMap(byte *colorMap, int16 delta);
 	void playBoothAV();
 	void mapIdleAnimation();
 	void boothIdleAnimation();
 	void screensaverStep();
+	void fadeToBlack(int16 steps);
 	void flushInput();
-	
-	BarkerTable *createBarker(int16 minIndex, int16 maxIndex);
-	void freeBarker(BarkerTable *table);
-	void registerSideShow(BarkerTable *table, SideShowHandler handler, short boothId);
-	void barker(BarkerTable *table, int16 startBooth);
-	bool checkError();
+	int16 winALetter(int16 prevBooth);
 
-	// Graphics
+	int16 g_lettersWon = 0;
+	bool g_allLettersWonFlag = false;
 	int g_displayMode = 0;
 	int32 g_displayX = 0;
 	int32 g_displayY = 0;
 	int32 g_displayWidth = 0;
 	int32 g_displayHeight = 0;
 
-	void setCursorPict(void *sprite);
-	void startCycle(void *cycleResource);
-	void blastColors();
-	void setColors();
-	void restoreColors();
-	void loadColors();
-	void shiftColorMap();
-	void fadeToBlack();
-	void displayColors(void *palette, int16 page, int16 flags);
-	void boltPict2Pict(void *dest, void *boltSprite);
-	void displayPic(void *boltSprite, int16 xOff, int16 yOff, int16 flags);
-	void boltCycleToXPCycle();
-	void unpackColors();
-	void inRect();
-	void rectOverlap();
-
-	// Resource handling
-	BOLTLib *g_boothsBoltLib = nullptr;
-	int g_boothsBoltIndex = 0;
-	int16 g_resourceIndexCount = 1000;
-	uint32 *g_resourceIndex = nullptr;
-
-	#define AssetPath(x) x
+	int16 g_boothLoadedMask = 0;
+	int16 g_currentBoothScene = 0;
+	int16 g_boothNumHotspots = 0;
+	int16 g_boothNumAnimations = 0;
+	byte *g_boothSceneDesc = nullptr;
+	byte *g_boothHotPalDescs[4];
+	Common::Rect g_boothHotspotDescs[8]; 
+	byte *g_boothAnimDescs[7];
+	byte *g_boothAnimPalDescs[4];
+	byte g_savedPalettes[7][30];
+	byte g_savedHotPalettes[4][9];
+	byte *g_boothPalCycleData = nullptr;
+	XPPicDesc g_boothLetterSprite;
+	bool g_needInitCursorPos = true;
+	byte *g_boothExitLeft = nullptr;
+	byte *g_boothExitRight = nullptr;
+	byte g_leftDoorNavTable[3] = {3, 2, 4};
+	byte g_rightDoorNavTable[3] = {1, 0, 5};
+
+	int16 g_cursorX = 0;
+	int16 g_cursorY = 0;
+	int16 g_hoveredHotspot = 0;
+	uint32 g_keyTimer = 0;
+	int16 g_keyReleased = 0;
+	int16 g_keyLeft = 0;
+	int16 g_keyRight = 0;
+	int16 g_keyUp = 0;
+	int16 g_keyDown = 0;
+	int16 g_keyHelp = 0;
+	int16 g_keyEnter = 0;
+
+	int16 g_screensaverStep = 0;
+	int16 g_helpPlaying = 0;
+	int16 g_helpIsIdle = 0;
+	int16 g_idleHelpAvailable = 0;
+
+	// Barker
+	BarkerTable *createBarker(int16 minIndex, int16 maxIndex);
+	void freeBarker(BarkerTable *table);
+	bool registerSideShow(BarkerTable *table, SideShowHandler handler, int16 boothId);
+	int16 barker(BarkerTable *table, int16 startBooth);
+	bool checkError();
 
-	bool openBOLTLib(BOLTLib **outLib, int *outIdx, const char *fileName);
-	void closeBOLTLib(BOLTLib **lib);
+	int32 g_curErrorCode = 0;
+	int16 g_currentBoothId = 0;
+
+	// Utils
+	void displayColors(byte *palette, int16 page, int16 flags);
+	void sub_11035();
+	void boltPict2Pict(XPPicDesc *dest, byte *boltSprite);
+	void displayPic(byte *boltSprite, int16 xOff, int16 yOff, int16 page);
+	void sub_11131();
+	const char *assetPath(const char *fileName);
+	void boltCycleToXPCycle(byte *srcData, XPCycleState *cycleDesc);
+	void unpackColors(int16 count, byte *packedColors);
+
+	// Swap
+	void swapPicHeader();
+	void swapAndResolvePicDesc();
+	void swapFirstWord();
+	void swapFirstTwoWords();
+	void swapFirstFourWords();
+	void swapSpriteHeader();
+	void freeSpriteCleanUp();
+
+	// Resource
+	bool libRead(Common::File *fileHandle, uint32 offset, byte *dest, uint32 size);
+	void decompress(byte *dest, uint32 decompSize, byte *src);
+	void resolveIt(uint32 *ref);
+	void resolvePendingFixups();
+	void resolveFunction(uint32 *ref);
+	void resolveAllRefs();
+	byte *getResolvedPtr(byte *data, int offset);
+	bool openBOLTLib(BOLTLib **outLib, BOLTCallbacks *outIdx, const char *fileName);
+	bool closeBOLTLib(BOLTLib **lib);
 	bool attemptFreeIndex(BOLTLib *lib, int16 groupId);
-	void loadGroupDirectory();
+	bool loadGroupDirectory();
 	bool getBOLTGroup(BOLTLib *lib, int16 groupId, int16 flags);
 	void freeBOLTGroup(BOLTLib *lib, int16 groupId, int16 flags);
-	void *getBOLTMember(BOLTLib *lib, int16 resId);
+	byte *getBOLTMember(BOLTLib *lib, int16 resId);
 	bool freeBOLTMember(BOLTLib *lib, int16 resId);
-
-	void *memberAddr(BOLTLib *lib, int16 resId);
-	void *memberAddrOffset(BOLTLib *lib, uint32 resIdAndOffset);
+	Common::Rect memberToRect(byte *data);
+	byte *memberAddr(BOLTLib *lib, int16 resId);
+	byte *memberAddrOffset(BOLTLib *lib, uint32 resIdAndOffset);
 	uint32 memberSize(BOLTLib *lib, int16 resId);
-	void *groupAddr(BOLTLib *lib, int16 groupId);
-
+	byte *groupAddr(BOLTLib *lib, int16 groupId);
 	bool allocResourceIndex();
 	void freeResourceIndex();
+	void swapAllWords();
+	void swapAllLongs();
+
+	BOLTLib *g_boothsBoltLib = nullptr;
+	BOLTCallbacks g_resourceDefaultCallbacks;
+
+	static BOLTCallback g_defaultTypeLoadCallbacks[25];
+	static BOLTCallback g_defaultTypeFreeCallbacks[25];
+	static BOLTCallback g_defaultMemberLoadCallbacks[25];
+	static BOLTCallback g_defaultMemberFreeCallbacks[25];
+	static BOLTCallback g_defaultGroupLoadCallbacks[25];
+	static BOLTCallback g_defaultGroupFreeCallbacks[25];
 
+	static void noOpCb();
+
+	static void swapAllWordsCb();
+	static void swapPicHeaderCb();
+	static void swapAndResolvePicDescCb();
+	static void swapFirstWordCb();
+	static void swapFirstTwoWordsCb();
+	static void swapFirstFourWordsCb();
+	static void swapSpriteHeaderCb();
+	static void freeSpriteCleanUpCb();
+
+	void initCallbacks();
+
+	int16 g_resourceIndexCount = 1000;
+	uint32 **g_resourceIndex = nullptr;
+	Common::Array<byte *> g_resolvedPtrs;
+	Common::File *g_cachedFileHandle = nullptr;
+	uint32 g_cachedFilePos = 0xFFFFFFFF;
+	BOLTLib *g_boltCurrentLib = nullptr;
+	BOLTHeader g_boltFileHeader;
+	BOLTGroupEntry *g_boltCurrentGroupEntry = nullptr;
+	int16 g_boltLoadDepth = 0;
+	byte *g_boltRawMemberData = nullptr;
+	BOLTMemberEntry *g_boltCurrentMemberEntry = nullptr;
+	int16 g_pendingFixupCount = 0;
+
+	// Game state
 	bool initVRam(int16 poolSize);
 	void freeVRam();
 	bool vLoad(void *dest, const char *name);
 	bool vSave(void *src, uint16 srcSize, const char *name);
 	bool vDelete(const char *name);
-	void memMove(void *dest, void *src, uint16 count);
-	void *dataAddress(int16 recordOffset);
+	byte *dataAddress(int16 recordOffset);
 	uint16 dataSize(int16 recordOffset);
 	bool findRecord(const char *name, int16 *outOffset);
 
-	// Videos
-	void *g_rtfHandle = nullptr;
+	int32 g_vramRecordCount = 0;
+	int32 g_vramUsedBytes = 0;
+	byte *g_allocatedMemPool = nullptr;
+	uint32 g_allocatedMemPoolSize = 0;
 
-	void *openRTF(const char *fileName);
-	void closeRTF(void *rtf);
-	bool playRTF(void *rtfFile, int16 animIndex, void *ringBuffer, int32 bufferSize);
+	// RTF
+	RTFResource *openRTF(const char *fileName);
+	void closeRTF(RTFResource *rtf);
+	bool playRTF(RTFResource *rtfFile, int16 animIndex, byte *ringBuffer, int32 bufferSize);
 	bool fillRTFBuffer();
 	void flushRTFSoundQueue();
-	bool maintainRTF(int16 mode, void *outFrameData);
+	bool maintainRTF(int16 mode, RTFPacket **outFrameData);
 	bool isRTFPlaying();
-	void killRTF();
-	void readPacket();
-	void preProcessPacket();
-	void queuePacket();
-	void deQueuePacket();
-	void allocPacket();
-	void freePacket();
+	bool killRTF(uint32 *outFilePos);
+	bool readPacket();
+	void preProcessPacket(RTFPacket *packet);
+	void queuePacket(RTFPacket *packet);
+	RTFPacket *deQueuePacket();
+	RTFPacket *allocPacket(uint32 dataSize);
+	void freePacket(RTFPacket *packet);
 	void resetPlaybackState();
-	void sub_12980();
-	void prepareAV();
-	void maintainAV();
+	void setAVBufferSize(uint32 bufSize);
+
+	RTFResource *g_rtfHandle = nullptr;
+	Common::File *g_rtfFileHandle = nullptr;
+	uint32 g_rtfChunkRemaining = 0;
+	bool g_rtfMidChunk = false;
+	RTFPacket *g_rtfCurrentPacket = nullptr;
+	byte *g_ringBufBase = nullptr;
+	byte *g_ringBufWritePtr = nullptr;
+	uint32 g_ringBufSize = 0;
+	uint32 g_ringBufFreeSpace = 0;
+	uint32 g_ringBufLowWater = 0;
+	uint32 g_ringBufHighWater = 0;
+	uint32 g_ringBufUsed = 0;
+	bool g_rtfSoundActive = false;
+	int16 g_rtfPlaybackTime = 0;
+	int16 g_rtfCumulativeTime = 0;
+	RTFPacket *g_rtfPendingFrame = nullptr;
+	RTFPacket *g_rtfSoundQueueHead = nullptr;
+	RTFPacket *g_rtfSoundPlayHead = nullptr;
+	RTFPacket *g_rtfChunkListTail = nullptr;
+	RTFPacket *g_rtfChunkListHead = nullptr;
+	int16 g_rtfChunkCount = 0;
+	int16 g_rtfQueuedSoundCount = 0;
+	int16 g_rtfSoundTiming = 0;
+	uint32 g_rtfAnimStartOffset = 0;
+	bool g_rtfNeedInitialFill = false;
+	uint32 g_rtfChunkTag = 0;
+	uint32 g_rtfChunkSize = 0;
+
+	// AV
+	bool prepareAV(RTFResource *rtfHandle, int16 animIndex, int16 width, int16 height, int16 xOff, int16 yOff);
+	bool maintainAV(int16 mode);
 	void stopAV();
-	bool playAV(void *rtfHandle, int16 animIndex, int16 width, int16 height, int16 xOff, int16 yOff);
-	void processPacket();
-	void processRL7();
-	void processPLTE();
-	void initAV();
+	bool playAV(RTFResource *rtfHandle, int16 animIndex, int16 width, int16 height, int16 xOff, int16 yOff);
+	void processPacket(RTFPacket *packet);
+	void processRL7(RTFPacket *packet);
+	void processPLTE(RTFPacket *packet);
+	bool initAV(RTFResource *rtfHandle, int16 animIndex, int16 width, int16 height, int16 xOff, int16 yOff);
 	void cleanUpAV();
-	void startAnimation();
-	void maintainAudioPlay();
-	void initAnim();
+
+	byte *g_avRingBuffer = nullptr;
+	uint32 g_avTargetBufSize = 0x0FA000;
+	int16 g_avSkipLevel = 0;
+	byte *g_avFrontPalette = nullptr;
+	byte *g_avBackPalette = nullptr;
+	uint32 g_avSavedInactivityTimer = 0;
+	uint32 g_avSavedScreenSaverTimer = 0;
+	int16 g_avFrameAccum = 0;
+	int16 g_avDisplayX = 0;
+	int16 g_avDisplayY = 0;
+
+	XPPicDesc g_avFrontBufDesc;
+	XPPicDesc g_avBackBufDesc;
+
+	// Anim
+	bool startAnimation(RTFResource *rtf, int16 animIndex);
+	void stopAnimation();
+	bool maintainAudioPlay(int16 mode);
+	bool initAnim(RTFResource *rtf, int16 animIndex);
 	void cleanUpAnim();
+
+	int16 g_animPrevInactivityTimer = 0;
+	byte *g_animRingBuffer = nullptr;
+	Common::File *g_animFileHandle = nullptr;
+
+	// --- MINIGAMES ---
+
+	// Fred
+	int16 fredGame(int16 prevBooth) { return 0; }
+
+	// George
+	int16 georgeGame(int16 prevBooth) { return 0; }
+
+	// Huck
+	int16 huckGame(int16 prevBooth) { return 0; }
+
+	// Scooby
+	int16 scoobyGame(int16 prevBooth) { return 0; }
+
+	// TopCat
+	int16 topCatGame(int16 prevBooth) { return 0; }
+
+	// Yogi
+	int16 yogiGame(int16 prevBooth) { return 0; }
 };
 
 extern BoltEngine *g_engine;
diff --git a/engines/bolt/booth.cpp b/engines/bolt/booth.cpp
index 2af00442565..acfe6f2457c 100644
--- a/engines/bolt/booth.cpp
+++ b/engines/bolt/booth.cpp
@@ -23,100 +23,1222 @@
 
 namespace Bolt {
 
-void BoltEngine::displayBooth() {
-}
+void BoltEngine::startCycle(byte *cycleResource) {
+	if (!cycleResource)
+		return;
 
-void BoltEngine::playAVOverBooth() {
+	XPCycleState specs[4];
+	boltCycleToXPCycle(cycleResource, specs);
+	_xp->startCycle(specs);
 }
 
-void BoltEngine::hucksBooth() {
-}
+void BoltEngine::displayBooth(int16 page) {
+	_xp->setTransparency(page);
+	_xp->displayPic(&g_boothLetterSprite, g_displayX, g_displayY, page);
+	displayColors(g_boothPalCycleData, 0, page);
 
-void BoltEngine::fredsBooth() {
-}
+	if (page != stFront)
+		_xp->fillDisplay(0, stFront);
 
-void BoltEngine::scoobysBooth() {
+	_xp->updateDisplay();
 }
 
-void BoltEngine::yogisBooth() {
+void BoltEngine::playAVOverBooth(int16 animIndex) {
+	_xp->hideCursor();
+	displayBooth(stBack);
+	playAV(g_rtfHandle, animIndex, g_displayWidth, g_displayHeight, g_displayX, g_displayY);
+	displayBooth(stFront);
+	_xp->showCursor();
 }
 
-void BoltEngine::georgesBooth() {
-}
+int16 BoltEngine::hucksBooth(int16 prevBooth) {
+	int16 result = openBooth(3);
+	closeBooth();
 
-void BoltEngine::topCatsBooth() {
+	switch (result) {
+	case -1:
+		return 0; // exit
+	case 0:
+		return 9; // main entrance
+	case 1:
+		return 4; // right neighbor (Fred)
+	case 2:
+		return 10; // default (play game)
+	case 3:
+		return 10; // play game
+	default:
+		return 10;
+	}
 }
 
-void BoltEngine::mainEntrance() {
-}
+int16 BoltEngine::fredsBooth(int16 prevBooth) {
+	int16 result = openBooth(4);
+	closeBooth();
 
-void BoltEngine::huckGame() {
+	switch (result) {
+	case -1:
+		return 0;
+	case 0:
+		return 3; // left neighbor (Hucks)
+	case 1:
+		return 5; // right neighbor (Scooby)
+	case 2:
+		return 11;
+	case 3:
+		return 11;
+	default:
+		return 11;
+	}
 }
 
-void BoltEngine::fredGame() {
-}
+int16 BoltEngine::scoobysBooth(int16 prevBooth) {
+	int16 result = openBooth(5);
+	closeBooth();
 
-void BoltEngine::scoobyGame() {
+	switch (result) {
+	case -1:
+		return 0;
+	case 0:
+		return 4; // left (Fred)
+	case 1:
+		return 6; // right (Yogi)
+	case 2:
+		return 12;
+	case 3:
+		return 12;
+	default:
+		return 12;
+	}
 }
 
-void BoltEngine::yogiGame() {
+int16 BoltEngine::yogisBooth(int16 prevBooth) {
+	int16 result = openBooth(6);
+	closeBooth();
+
+	switch (result) {
+	case -1:
+		return 0;
+	case 0:
+		return 5; // left (Scooby)
+	case 1:
+		return 7; // right (George)
+	case 2:
+		return 13;
+	case 3:
+		return 13;
+	default:
+		return 13;
+	}
 }
 
-void BoltEngine::georgeGame() {
+int16 BoltEngine::georgesBooth(int16 prevBooth) {
+	int16 result = openBooth(7);
+	closeBooth();
+
+	switch (result) {
+	case -1:
+		return 0;
+	case 0:
+		return 6; // left (Yogi)
+	case 1:
+		return 8; // right (TopCat)
+	case 2:
+		return 14;
+	case 3:
+		return 14;
+	default:
+		return 14;
+	}
 }
 
-void BoltEngine::topCatGame() {
+int16 BoltEngine::topCatsBooth(int16 prevBooth) {
+	int16 result = openBooth(8);
+	closeBooth();
+
+	switch (result) {
+	case -1:
+		return 0;
+	case 0:
+		return 7; // left (George)
+	case 1:
+		return 9; // right (main entrance)
+	case 2:
+		return 15;
+	case 3:
+		return 15;
+	default:
+		return 15;
+	}
 }
 
-void BoltEngine::winALetter() {
+int16 BoltEngine::mainEntrance(int16 prevBooth) {
+	int16 result = openBooth(9);
+	closeBooth();
+
+	switch (result) {
+	case -1:
+		return 0; // exit
+	case 0:
+		return 8; // left (TopCat)
+	case 1:
+		return 3; // right (Hucks)
+	case 2:
+		return 9; // self (stay)
+	case 3:
+		return 9; // self
+	case 4:
+		return 9; // self
+	case 5:
+		return 0; // exit
+	default:
+		return 9;
+	}
 }
 
-void BoltEngine::loadBooth() {
+bool BoltEngine::loadBooth(int16 boothId) {
+	switch (boothId) {
+	case 3:
+		if (g_boothLoadedMask & 0x01)
+			break;
+		if (!getBOLTGroup(g_boothsBoltLib, 0x100, 1))
+			return false;
+		g_boothLoadedMask |= 0x01;
+		break;
+
+	case 4:
+		if (g_boothLoadedMask & 0x02)
+			break;
+		if (!getBOLTGroup(g_boothsBoltLib, 0x200, 1))
+			return false;
+		g_boothLoadedMask |= 0x02;
+		break;
+
+	case 5:
+		if (g_boothLoadedMask & 0x04)
+			break;
+		if (!getBOLTGroup(g_boothsBoltLib, 0x300, 1))
+			return false;
+		g_boothLoadedMask |= 0x04;
+		break;
+
+	case 6:
+		if (g_boothLoadedMask & 0x08)
+			break;
+		if (!getBOLTGroup(g_boothsBoltLib, 0x400, 1))
+			return false;
+		g_boothLoadedMask |= 0x08;
+		break;
+
+	case 7:
+		if (g_boothLoadedMask & 0x10)
+			break;
+		if (!getBOLTGroup(g_boothsBoltLib, 0x500, 1))
+			return false;
+		g_boothLoadedMask |= 0x10;
+		break;
+
+	case 8:
+		if (g_boothLoadedMask & 0x20)
+			break;
+		if (!getBOLTGroup(g_boothsBoltLib, 0x600, 1))
+			return false;
+		g_boothLoadedMask |= 0x20;
+		break;
+
+	case 9:
+		if (g_boothLoadedMask & 0x40)
+			break;
+		if (!getBOLTGroup(g_boothsBoltLib, 0x700, 1))
+			return false;
+		if (!getBOLTGroup(g_boothsBoltLib, 0x800 + (g_lettersWon << 8), 1))
+			return false;
+		g_boothLoadedMask |= 0x40;
+		break;
+	}
+
+	return true;
 }
 
 void BoltEngine::unloadBooth() {
+	if (g_boothLoadedMask & 0x01)
+		freeBOLTGroup(g_boothsBoltLib, 0x100, 1);
+
+	if (g_boothLoadedMask & 0x02)
+		freeBOLTGroup(g_boothsBoltLib, 0x200, 1);
+
+	if (g_boothLoadedMask & 0x04)
+		freeBOLTGroup(g_boothsBoltLib, 0x300, 1);
+
+	if (g_boothLoadedMask & 0x08)
+		freeBOLTGroup(g_boothsBoltLib, 0x400, 1);
+
+	if (g_boothLoadedMask & 0x10)
+		freeBOLTGroup(g_boothsBoltLib, 0x500, 1);
+
+	if (g_boothLoadedMask & 0x20)
+		freeBOLTGroup(g_boothsBoltLib, 0x600, 1);
+
+	if (g_boothLoadedMask & 0x40) {
+		freeBOLTGroup(g_boothsBoltLib, 0x800 + (g_lettersWon << 8), 1);
+		freeBOLTGroup(g_boothsBoltLib, 0x700, 1);
+	}
+
+	g_boothLoadedMask = 0;
 }
 
-void BoltEngine::openBooth() {
+int16 BoltEngine::openBooth(int16 sceneId) {
+	int16 baseResId;
+	int16 resId;
+
+	g_currentBoothScene = sceneId;
+
+	if (!loadBooth(sceneId))
+		return -1;
+
+	baseResId = (sceneId - 3) << 8;
+
+	// Main entrance has more hotspots/animations than regular booths
+	if (sceneId == 9) {
+		g_boothNumHotspots = 8;
+		g_boothNumAnimations = 7;
+	} else {
+		g_boothNumHotspots = 5;
+		g_boothNumAnimations = 4;
+	}
+
+	// Background scene descriptor
+	g_boothSceneDesc = memberAddr(g_boothsBoltLib, baseResId + 0x100);
+
+	// Load 4 palette descriptors (at DS:0x13AC, members +0x105..+0x108)
+	resId = baseResId + 0x105;
+	for (int16 i = 0; i < 4; i++) {
+		g_boothHotPalDescs[i] = memberAddr(g_boothsBoltLib, resId);
+		resId++;
+	}
+
+	// Load animation descriptors
+	if (sceneId == 9) {
+		for (int16 i = 0; i < g_boothNumAnimations; i++)
+			g_boothAnimDescs[i] = memberAddr(g_boothsBoltLib, 0x709 + i);
+	} else {
+		resId = baseResId + 0x109;
+		for (int16 i = 0; i < g_boothNumAnimations; i++) {
+			g_boothAnimDescs[i] = memberAddr(g_boothsBoltLib, resId);
+			resId++;
+		}
+	}
+
+	// Load hotspot descriptors
+	if (sceneId == 9) {
+		for (int16 i = 0; i < g_boothNumHotspots; i++)
+			g_boothHotspotDescs[i] = memberToRect(memberAddr(g_boothsBoltLib, 0x710 + i));
+	} else {
+		resId = baseResId + 0x10D;
+		for (int16 i = 0; i < g_boothNumHotspots; i++) {
+			g_boothHotspotDescs[i] = memberToRect(memberAddr(g_boothsBoltLib, resId));
+			resId++;
+		}
+	}
+
+	if (sceneId == 9) {
+		// Main entrance palette cycling data
+		g_boothPalCycleData = memberAddr(g_boothsBoltLib, 0x718);
+
+		// Letter marquee sprite (display mode selects variant)
+		byte *sprite = memberAddr(g_boothsBoltLib, (g_displayMode != 0 ? 0x801 : 0x800) + (g_lettersWon << 8));
+		boltPict2Pict(&g_boothLetterSprite, sprite);
+
+		// Exit sign sprites
+		g_boothExitLeft = memberAddr(g_boothsBoltLib, 0x747);
+		g_boothExitRight = memberAddr(g_boothsBoltLib, 0x748);
+	} else {
+		g_boothPalCycleData = memberAddr(g_boothsBoltLib, baseResId + 0x112);
+
+		byte *sprite = memberAddr(g_boothsBoltLib, baseResId + (g_displayMode != 0 ? 0x114 : 0x113));
+		boltPict2Pict(&g_boothLetterSprite, sprite);
+	}
+
+	displayBooth(stFront);
+	loadColors();
+	_xp->setCursorColor(255, 255, 255);
+
+	// First-time cursor position from resource data
+	if (g_needInitCursorPos) {
+		g_cursorX = (int16)READ_UINT16(memberAddr(g_boothsBoltLib, 1));
+		g_cursorY = (int16)READ_UINT16(memberAddr(g_boothsBoltLib, 2));
+		g_needInitCursorPos = false;
+	}
+
+	_xp->setCursorPos(g_cursorX, g_cursorY);
+
+	flushInput();
+	return boothEventLoop();
 }
 
 void BoltEngine::closeBooth() {
+	uint16 buttonState;
+
+	_xp->readCursor(&buttonState, &g_cursorY, &g_cursorX);
+	flushInput();
+	_xp->fillDisplay(0, 0);
+	unloadBooth();
 }
 
 void BoltEngine::playTour() {
+	int16 playing = 1;
+
+	_xp->hideCursor();
+	g_screensaverStep = 0;
+
+	if (prepareAV(g_rtfHandle, 4, g_displayWidth, g_displayHeight, g_displayX, g_displayY)) {
+		while (playing) {
+			playing = maintainAV(0);
+			if (!playing)
+				break;
+
+			uint32 eventData;
+			int16 event = _xp->getEvent(etEmpty, &eventData);
+
+			switch (event) {
+			case etMouseDown: // Abort tour
+				stopAV();
+				playing = 0;
+				break;
+
+			case etSound: // Sound marker
+				playing = maintainAV(1);
+				break;
+
+			case etTrigger: // Trigger marker from RTF stream
+				screensaverStep();
+				break;
+			}
+		}
+
+		_xp->stopCycle();
+		displayBooth(stFront);
+	}
+
+	_xp->showCursor();
 }
 
-void BoltEngine::finishPlayingHelp() {
+void BoltEngine::finishPlayingHelp(int16 activeHotspot) {
+	g_helpPlaying = 0;
+
+	if (g_helpIsIdle != 0) {
+		g_helpIsIdle = 0;
+	} else {
+		resetInactivityState();
+	}
+
+	if (activeHotspot != 0)
+		restoreColors(0);
+
+	if (activeHotspot != 1)
+		restoreColors(1);
+
+	if (activeHotspot != 2 && g_keyHelp == 0)
+		restoreColors(2);
+
+	g_keyLeft = 0;
+
+	if (g_currentBoothScene == 9) {
+		if (activeHotspot != 7)
+			restoreColors(7);
+
+		if (activeHotspot != 5)
+			restoreColors(5);
+
+		if (activeHotspot != 6)
+			restoreColors(6);
+
+		g_keyRight = 0;
+		g_keyUp = 0;
+		g_keyDown = 0;
+	} else {
+		if (activeHotspot != 3)
+			restoreColors(3);
+
+		g_keyEnter = 0;
+	}
 }
 
-void BoltEngine::hotSpotActive() {
+int16 BoltEngine::hotSpotActive(int16 hotspot) {
+	switch (hotspot) {
+	case 0:
+	case 1:
+		return g_keyLeft;
+
+	case 2:
+		return (g_helpPlaying || g_keyHelp) ? 1 : 0;
+
+	case 3:
+		return g_keyEnter;
+
+	case 4:
+		return 0;
+
+	case 5:
+		return g_keyDown;
+
+	case 6:
+		return g_keyUp;
+
+	case 7:
+		return g_keyRight;
+
+	default:
+		return 0;
+	}
 }
 
 void BoltEngine::hoverHotSpot() {
+	int16 i = 0;
+
+	while (i < g_boothNumHotspots) {
+		if (g_boothHotspotDescs[i].contains(g_cursorX, g_cursorY))
+			break;
+
+		i++;
+	}
+
+	if (i == g_hoveredHotspot)
+		return;
+
+	if (g_hoveredHotspot < g_boothNumHotspots) {
+		if (!hotSpotActive(g_hoveredHotspot))
+			restoreColors(g_hoveredHotspot);
+	}
+
+	g_hoveredHotspot = i;
+
+	if (i < g_boothNumHotspots) {
+		if (!hotSpotActive(i))
+			setColors(g_hoveredHotspot);
+	}
 }
 
-void BoltEngine::boothEventLoop() {
+int16 BoltEngine::boothEventLoop() {
+	uint32 eventData;
+	int16 exitCode = 0;
+
+	_xp->showCursor();
+	g_hoveredHotspot = -1;
+	hoverHotSpot();
+
+	while (exitCode == 0 && !shouldQuit()) {
+		_xp->setInactivityTimer(30);
+
+		if (g_helpPlaying != 0) {
+			g_helpPlaying = maintainAudioPlay(0);
+			if (g_helpPlaying == 0)
+				finishPlayingHelp(g_hoveredHotspot);
+		}
+
+		eventData = 0;
+		switch (_xp->getEvent(etEmpty, &eventData)) {
+		case etTimer:
+			if (g_keyLeft == 0 && g_keyRight == 0 &&
+				g_keyUp == 0 && g_keyDown == 0 &&
+				g_keyEnter == 0 && g_keyHelp == 0)
+				break;
+
+			if (g_keyReleased != 0) {
+				// Key release: restore hotspot colors
+				g_keyReleased = 0;
+				if (g_keyLeft != 0) {
+					restoreColors(0);
+					restoreColors(1);
+				} else if (g_keyRight != 0) {
+					restoreColors(7);
+				} else if (g_keyUp != 0) {
+					restoreColors(6);
+				} else if (g_keyDown != 0) {
+					restoreColors(5);
+				} else if (g_keyEnter != 0) {
+					restoreColors(3);
+				} else if (g_keyHelp != 0) {
+					restoreColors(2);
+
+					if (g_helpPlaying != 0) {
+						if (g_helpIsIdle == 0 || g_currentBoothScene != 9)
+							break;
+					}
+
+					restoreColors(8);
+				}
+			} else {
+				// Key press: highlight hotspot colors
+				g_keyReleased = 1;
+				if (g_keyLeft != 0) {
+					setColors(0);
+					setColors(1);
+				} else if (g_keyRight != 0) {
+					setColors(7);
+				} else if (g_keyUp != 0) {
+					setColors(6);
+				} else if (g_keyDown != 0) {
+					setColors(5);
+				} else if (g_keyEnter != 0) {
+					setColors(3);
+				} else if (g_keyHelp != 0) {
+					setColors(2);
+
+					if (g_helpPlaying != 0) {
+						if (g_helpIsIdle == 0 || g_currentBoothScene != 9)
+							break;
+					}
+
+					setColors(8);
+				}
+			}
+
+			g_keyTimer = _xp->startTimer(500);
+			break;
+
+		case etMouseMove:
+			g_cursorX = (int16)(eventData >> 16);
+			g_cursorY = (int16)eventData;
+			hoverHotSpot();
+			break;
+
+		case etMouseDown:
+		{
+			int16 prevHelp = g_helpPlaying;
+			if (g_helpPlaying != 0) {
+				stopAnimation();
+				finishPlayingHelp(g_hoveredHotspot);
+			}
+
+			if (prevHelp != 0 && g_hoveredHotspot == 2)
+				break;
+
+			if (g_hoveredHotspot == -1)
+				break;
+
+			exitCode = handleButtonPress(g_hoveredHotspot);
+			if (exitCode == 0) {
+				g_hoveredHotspot = -1;
+				hoverHotSpot();
+			}
+
+			break;
+		}
+
+		case etSound:
+			if (g_helpPlaying != 0) {
+				g_helpPlaying = maintainAudioPlay(1);
+				if (g_helpPlaying == 0)
+					finishPlayingHelp(g_hoveredHotspot);
+			}
+
+			break;
+
+		case etInactivity:
+			if (g_helpPlaying != 0)
+				break;
+
+			if (g_keyHelp == 0) {
+				g_keyHelp = 1;
+				g_keyTimer = _xp->startTimer(500);
+				if (g_idleHelpAvailable != 0)
+					continue;
+			} else {
+				if (g_idleHelpAvailable == 0)
+					break;
+
+				if (startAnimation(g_rtfHandle, 0x19)) {
+					g_idleHelpAvailable = 0;
+					g_helpPlaying = 1;
+					g_helpIsIdle = 1;
+				}
+			}
+
+			break;
+
+		case etTrigger:
+			if (g_helpPlaying == 0)
+				break;
+
+			if (g_currentBoothScene == 9)
+				mapIdleAnimation();
+			else
+				boothIdleAnimation();
+
+			break;
+		}
+	}
+
+	if (shouldQuit()) {
+		return -1;
+	}
+
+	_xp->hideCursor();
+	return g_hoveredHotspot;
 }
 
 void BoltEngine::resetInactivityState() {
+	if (g_keyTimer != 0) {
+		_xp->killTimer(g_keyTimer);
+		g_keyTimer = 0;
+	}
+
+	g_idleHelpAvailable = 0;
+	_xp->setInactivityTimer(30);
+
+	if (g_keyHelp != 0) {
+		restoreColors(2);
+		if (g_currentBoothScene == 9)
+			restoreColors(8);
+	}
+
+	g_keyHelp = 0;
+}
+
+bool BoltEngine::handleButtonPress(int16 hotspot) {
+	byte savedLeftDoor[3];
+	byte savedRightDoor[3];
+
+	savedLeftDoor[0] = g_leftDoorNavTable[0];
+	savedLeftDoor[1] = g_leftDoorNavTable[1];
+	savedLeftDoor[2] = g_leftDoorNavTable[2];
+	savedRightDoor[0] = g_rightDoorNavTable[0];
+	savedRightDoor[1] = g_rightDoorNavTable[1];
+	savedRightDoor[2] = g_rightDoorNavTable[2];
+
+	if (hotspot != -1)
+		resetInactivityState();
+
+	switch (hotspot) {
+	case 0:
+	case 1:
+		return true;
+
+	case 2: {
+		int16 animId;
+		switch (g_currentBoothScene) {
+		case 3:
+			animId = 0x18;
+			break;
+		case 4:
+			animId = 0x12;
+			break;
+		case 5:
+			animId = 0x13;
+			break;
+		case 6:
+			animId = 0x14;
+			break;
+		case 7:
+			animId = 0x15;
+			break;
+		case 8:
+			animId = 0x16;
+			break;
+		case 9:
+			animId = 0x17;
+			break;
+		default:
+			animId = -1;
+			break;
+		}
+
+		bool ok = false;
+		if (animId != -1)
+			ok = startAnimation(g_rtfHandle, animId);
+
+		if (ok) {
+			g_helpPlaying = 1;
+			setColors(2);
+			g_keyHelp = 0;
+			g_screensaverStep = 0;
+		}
+
+		return false;
+	}
+
+	default:
+		if (g_currentBoothScene == 9) {
+			switch (hotspot) {
+			case 3:
+			case 4:
+			{
+				byte *navTable = (hotspot == 3) ? savedLeftDoor : savedRightDoor;
+				int8 boothOffset = (int8)navTable[g_lettersWon % 3];
+				playAVOverBooth(boothOffset + 12);
+				return false;
+			}
+
+			case 5:
+				_xp->hideCursor();
+				displayBooth(stBack);
+
+				if (playAV(g_rtfHandle, 3, g_displayWidth, g_displayHeight, g_displayX, g_displayY) == 0) {
+					fadeToBlack(1);
+					_xp->setTransparency(0);
+					displayPic(getBOLTMember(g_boothsBoltLib, g_displayMode ? 0x1802 : 0x1801), g_displayX, g_displayY, 0);
+					_xp->updateDisplay();
+					displayColors(getBOLTMember(g_boothsBoltLib, 0x1800), 0, 0);
+					_xp->updateDisplay();
+
+					uint32 timer = _xp->startTimer(5000);
+					if (timer != 0) {
+						uint32 evtData;
+						int16 evtType;
+						do {
+							evtType = _xp->getEvent(etEmpty, &evtData);
+						} while ((evtType != etTimer || evtData != timer) && !shouldQuit());
+					}
+
+					fadeToBlack(32);
+				}
+				return true;
+
+			case 6:
+				_xp->hideCursor();
+
+				vDelete("FredBC");
+				vDelete("GeorgeBE");
+				vDelete("Huck");
+				vDelete("Scooby");
+				vDelete("TopCatBF");
+				vDelete("Yogi");
+
+				displayBooth(stBack);
+				playAV(g_rtfHandle, 5,
+					   g_displayWidth, g_displayHeight,
+					   g_displayX, g_displayY);
+
+				if (g_lettersWon != 0) {
+					freeBOLTGroup(g_boothsBoltLib, 0x800 + (g_lettersWon << 8), 1);
+					if (!getBOLTGroup(g_boothsBoltLib, 0x800, 1)) {
+						g_hoveredHotspot = -1;
+						return true;
+					}
+
+					boltPict2Pict(&g_boothLetterSprite, memberAddr(g_boothsBoltLib, g_displayMode ? 0x801 : 0x800));
+				}
+
+				displayBooth(stFront);
+				g_lettersWon = 0;
+				_xp->showCursor();
+				return false;
+
+			case 7:
+				playTour();
+				return false;
+			}
+		} else {
+			switch (hotspot) {
+			case 3:
+				startCycle(g_boothPalCycleData);
+				_xp->startTimer(2000);
+				return true;
+
+			case 4:
+				restoreColors(hotspot);
+				playBoothAV();
+				return false;
+			}
+		}
+
+		return false;
+	}
+}
+
+void BoltEngine::blastColors(byte **paletteTable, int16 index, int16 mode) {
+	byte localPalette[384];
+	int16 si = 0;
+
+	byte *entry = paletteTable[index];
+	int16 startIndex = (int16)READ_UINT16(entry);
+	int16 count = (int16)READ_UINT16(entry + 2);
+
+	for (int16 i = 0; i < count; i++) {
+		int16 off = i * 6;
+		localPalette[si] = entry[4 + off];
+		localPalette[si + 1] = entry[6 + off];
+		localPalette[si + 2] = entry[8 + off];
+		si += 3;
+	}
+
+	if (mode == 1)
+		_xp->setPalette(count, startIndex, localPalette);
+}
+
+void BoltEngine::setColors(int16 index) {
+	if (g_currentBoothScene == 9) {
+		switch (index) {
+		case 0:
+		case 1:
+		case 2:
+			blastColors(g_boothAnimDescs, index, 1);
+			break;
+
+		case 3:
+		case 4:
+			return;
+
+		case 5:
+		case 6:
+		case 7:
+			blastColors(g_boothAnimDescs, index - 2, 1);
+			break;
+
+		case 8:
+			displayPic(g_boothExitLeft, 0, 0, 0);
+			_xp->updateDisplay();
+			break;
+		}
+	} else {
+		switch (index) {
+		case 0:
+		case 1:
+		case 2:
+			blastColors(g_boothAnimDescs, index, 1);
+			break;
+
+		case 3:
+			if (g_currentBoothScene == 9)
+				break;
+
+			for (int16 i = 0; i < 4; i++)
+				blastColors(g_boothHotPalDescs, i, 1);
+
+			break;
+
+		case 4:
+			blastColors(g_boothAnimDescs, index - 1, 1);
+			break;
+		}
+	}
+}
+
+void BoltEngine::restoreColors(int16 index) {
+	if (g_currentBoothScene == 9) {
+		switch (index) {
+		case 0:
+		case 1:
+		case 2: {
+			byte *palData = g_savedPalettes[index];
+			byte *animDesc = g_boothAnimDescs[index];
+			_xp->setPalette((int16)READ_UINT16(animDesc + 2), (int16)READ_UINT16(animDesc), palData);
+			break;
+		}
+
+		case 3:
+		case 4:
+			return;
+
+		case 5:
+		case 6:
+		case 7: {
+			byte *palData = g_savedPalettes[index - 2];
+			byte *animDesc = g_boothAnimDescs[index - 2];
+			_xp->setPalette((int16)READ_UINT16(animDesc + 2), (int16)READ_UINT16(animDesc), palData);
+			break;
+		}
+
+		case 8:
+			displayPic(g_boothExitRight, 0, 0, 0);
+			_xp->updateDisplay();
+			break;
+		}
+	} else {
+		switch (index) {
+		case 0:
+		case 1:
+		case 2: {
+			byte *palData = g_savedPalettes[index];
+			byte *animDesc = g_boothAnimDescs[index];
+			_xp->setPalette((int16)READ_UINT16(animDesc + 2), (int16)READ_UINT16(animDesc), palData);
+			break;
+		}
+
+		case 3:
+			for (int16 i = 0; i < 4; i++) {
+				byte *palData = g_savedHotPalettes[i];
+				byte *palDesc = g_boothHotPalDescs[i];
+				_xp->setPalette((int16)READ_UINT16(palDesc + 2), (int16)READ_UINT16(palDesc), palData);
+			}
+			break;
+
+		case 4:
+		{
+			byte *palData = g_savedPalettes[index - 1];
+			byte *animDesc = g_boothAnimDescs[index - 1];
+			_xp->setPalette((int16)READ_UINT16(animDesc + 2), (int16)READ_UINT16(animDesc), palData);
+			break;
+		}
+		}
+	}
+}
+
+void BoltEngine::loadColors() {
+	for (int16 i = 0; i < g_boothNumAnimations; i++) {
+		byte *animDesc = g_boothAnimDescs[i];
+		_xp->getPalette((int16)READ_UINT16(animDesc), (int16)READ_UINT16(animDesc + 2), g_savedPalettes[i]);
+	}
+
+	if (g_currentBoothScene == 9) {
+		int16 last = g_boothNumAnimations - 1;
+		byte *animDesc = g_boothAnimDescs[last];
+		_xp->getPalette((int16)READ_UINT16(animDesc), (int16)READ_UINT16(animDesc + 2), g_savedPalettes[last]);
+	}
+
+	for (int16 i = 0; i < 4; i++) {
+		byte *palDesc = g_boothHotPalDescs[i];
+		_xp->getPalette((int16)READ_UINT16(palDesc), (int16)READ_UINT16(palDesc + 2), g_savedHotPalettes[i]);
+	}
 }
 
-void BoltEngine::handleButtonPress() {
+void BoltEngine::shiftColorMap(byte *colorMap, int16 delta) {
+	WRITE_BE_INT16(colorMap + 2, READ_BE_INT16(colorMap + 2) + delta);
+	WRITE_BE_INT16(colorMap + 4, READ_BE_INT16(colorMap + 4) + delta);
 }
 
 void BoltEngine::playBoothAV() {
+	switch (g_currentBoothScene) {
+	case 3:
+		playAVOverBooth(6);
+		break;
+	case 4:
+		playAVOverBooth(7);
+		break;
+	case 5:
+		playAVOverBooth(8);
+		break;
+	case 6:
+		playAVOverBooth(9);
+		break;
+	case 7:
+		playAVOverBooth(10);
+		break;
+	case 8:
+		playAVOverBooth(11);
+		break;
+	}
 }
 
 void BoltEngine::mapIdleAnimation() {
+	g_screensaverStep++;
+
+	switch (g_screensaverStep) {
+	case 1:
+		setColors(0);
+		setColors(1);
+		g_keyLeft = 1;
+		g_keyReleased = 1;
+		g_keyTimer = _xp->startTimer(500);
+		break;
+
+	case 2:
+		g_keyLeft = 0;
+		g_keyReleased = 0;
+
+		if (!g_boothHotspotDescs[0].contains(g_cursorX, g_cursorY))
+			restoreColors(0);
+
+		if (!g_boothHotspotDescs[1].contains(g_cursorX, g_cursorY))
+			restoreColors(1);
+
+		return;
+
+	case 3:
+		setColors(7);
+		g_keyRight = 1;
+		g_keyReleased = 1;
+		g_keyTimer = _xp->startTimer(500);
+		break;
+
+	case 4:
+		g_keyRight = 0;
+		g_keyReleased = 0;
+
+		if (!g_boothHotspotDescs[7] .contains(g_cursorX, g_cursorY))
+			restoreColors(7);
+		return;
+
+	case 5:
+		setColors(6);
+		g_keyUp = 1;
+		g_keyReleased = 1;
+		g_keyTimer = _xp->startTimer(500);
+		break;
+
+	case 6:
+		g_keyUp = 0;
+		g_keyReleased = 0;
+
+		if (!g_boothHotspotDescs[6].contains(g_cursorX, g_cursorY))
+			restoreColors(6);
+		return;
+
+	case 7:
+		setColors(5);
+		g_keyDown = 1;
+		g_keyReleased = 1;
+		g_keyTimer = _xp->startTimer(500);
+		break;
+
+	case 8:
+		g_keyDown = 0;
+		g_keyReleased = 0;
+		g_keyTimer = 0;
+
+		if (!g_boothHotspotDescs[5].contains(g_cursorX, g_cursorY))
+			restoreColors(5);
+		return;
+	}
 }
 
 void BoltEngine::boothIdleAnimation() {
+	g_screensaverStep++;
+
+	switch (g_screensaverStep) {
+	case 1:
+		setColors(3);
+		g_keyEnter = 1;
+		g_keyReleased = 1;
+		g_keyTimer = _xp->startTimer(500);
+		break;
+
+	case 2:
+		g_keyEnter = 0;
+		g_keyReleased = 0;
+
+		if (!g_boothHotspotDescs[3].contains(g_cursorX, g_cursorY))
+			restoreColors(3);
+
+		return;
+
+	case 3:
+		setColors(0);
+		setColors(1);
+		g_keyLeft = 1;
+		g_keyReleased = 1;
+		g_keyTimer = _xp->startTimer(500);
+		break;
+
+	case 4:
+		g_keyLeft = 0;
+		g_keyReleased = 0;
+		g_keyTimer = 0;
+
+		if (!g_boothHotspotDescs[0].contains(g_cursorX, g_cursorY))
+			restoreColors(0);
+
+		if (!g_boothHotspotDescs[1].contains(g_cursorX, g_cursorY))
+			restoreColors(1);
+
+		return;
+	}
 }
 
 void BoltEngine::screensaverStep() {
+	static const int16 cycleResIds[] = {
+		0x700, 0x719, 0x71E, 0x720, 0x725, 0x72A, 0x72F, 0x731,
+		-1,    0x736, 0x73B, 0x73F, 0x743, -1
+	};
+
+	g_screensaverStep++;
+
+	if (!(g_screensaverStep & 1)) {
+		_xp->stopCycle();
+		return;
+	}
+
+	int16 index = g_screensaverStep >> 1;
+	if (index > 13)
+		return;
+
+	int16 resId = cycleResIds[index];
+	if (resId == -1)
+		return;
+
+	startCycle(memberAddr(g_boothsBoltLib, resId));
+}
+
+void BoltEngine::fadeToBlack(int16 steps) {
+	byte palette[768];
+	XPPicDesc desc;
+
+	desc.paletteStart = 1;
+	desc.paletteCount = 0x7F;
+
+	_xp->getPalette(0, 256, palette);
+	_xp->setFrameRate(32);
+
+	int16 scale = steps;
+	int16 factor = steps - 1;
+
+	while (scale > 0) {
+		byte *ptr = palette;
+		for (int16 i = 0; i < 256; i++) {
+			ptr[0] = (byte)((uint16)ptr[0] * factor / scale);
+			ptr[1] = (byte)((uint16)ptr[1] * factor / scale);
+			ptr[2] = (byte)((uint16)ptr[2] * factor / scale);
+			ptr += 3;
+		}
+
+		desc.palette = &palette[3];
+		_xp->displayPic(&desc, 0, 0, stFront);
+
+		desc.palette = &palette[384];
+		_xp->displayPic(&desc, 0, 0, stBack);
+
+		_xp->updateDisplay();
+		factor--;
+		scale--;
+	}
+
+	_xp->setFrameRate(0);
 }
 
 void BoltEngine::flushInput() {
+	uint32 eventBuf;
+
+	// Drain all mouse-down events
+	while (_xp->getEvent(etMouseDown, &eventBuf) == etMouseDown);
+
+	// Drain all mouse-up events
+	while (_xp->getEvent(etMouseUp, &eventBuf) == etMouseUp);
+}
+
+int16 BoltEngine::winALetter(int16 prevBooth) {
+	g_lettersWon++;
+
+	if (g_lettersWon == 15) {
+		if (g_allLettersWonFlag) {
+			g_allLettersWonFlag = false;
+			g_lettersWon = 16;
+		} else {
+			g_allLettersWonFlag = true;
+		}
+	}
+
+	playAV(g_rtfHandle, g_lettersWon + 31, g_displayWidth, g_displayHeight, g_displayX, g_displayY);
+
+	if (g_lettersWon >= 15)
+		g_lettersWon = 0;
+
+	// Return the booth front ID for the game just played
+	switch (prevBooth) {
+	case 10:
+		return 3; // HuckGame    -> HucksBooth
+	case 11:
+		return 4; // FredGame    -> FredsBooth
+	case 12:
+		return 5; // ScoobyGame  -> ScoobysBooth
+	case 13:
+		return 6; // YogiGame    -> YogisBooth
+	case 14:
+		return 7; // GeorgeGame  -> GeorgesBooth
+	case 15:
+		return 8; // TopCatGame  -> TopCatsBooth
+	default:
+		return 9; // MainEntrance
+	}
 }
 
 } // End of namespace Bolt
diff --git a/engines/bolt/gfx.cpp b/engines/bolt/booths/fred.cpp
similarity index 57%
rename from engines/bolt/gfx.cpp
rename to engines/bolt/booths/fred.cpp
index d9fb5e8f211..d0e5b38e0bf 100644
--- a/engines/bolt/gfx.cpp
+++ b/engines/bolt/booths/fred.cpp
@@ -23,49 +23,6 @@
 
 namespace Bolt {
 
-void BoltEngine::setCursorPict(void *sprite) {
-}
 
-void BoltEngine::startCycle(void *cycleResource) {
-}
-
-void BoltEngine::blastColors() {
-}
-
-void BoltEngine::setColors() {
-}
-
-void BoltEngine::restoreColors() {
-}
-
-void BoltEngine::loadColors() {
-}
-
-void BoltEngine::shiftColorMap() {
-}
-
-void BoltEngine::fadeToBlack() {
-}
-
-void BoltEngine::displayColors(void *palette, int16 page, int16 flags) {
-}
-
-void BoltEngine::boltPict2Pict(void *dest, void *boltSprite) {
-}
-
-void BoltEngine::displayPic(void *boltSprite, int16 xOff, int16 yOff, int16 flags) {
-}
-
-void BoltEngine::boltCycleToXPCycle() {
-}
-
-void BoltEngine::unpackColors() {
-}
-
-void BoltEngine::inRect() {
-}
-
-void BoltEngine::rectOverlap() {
-}
 
 } // End of namespace Bolt
diff --git a/engines/bolt/booths/george.cpp b/engines/bolt/booths/george.cpp
new file mode 100644
index 00000000000..d0e5b38e0bf
--- /dev/null
+++ b/engines/bolt/booths/george.cpp
@@ -0,0 +1,28 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "bolt/bolt.h"
+
+namespace Bolt {
+
+
+
+} // End of namespace Bolt
diff --git a/engines/bolt/booths/huck.cpp b/engines/bolt/booths/huck.cpp
new file mode 100644
index 00000000000..d0e5b38e0bf
--- /dev/null
+++ b/engines/bolt/booths/huck.cpp
@@ -0,0 +1,28 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "bolt/bolt.h"
+
+namespace Bolt {
+
+
+
+} // End of namespace Bolt
diff --git a/engines/bolt/booths/scooby.cpp b/engines/bolt/booths/scooby.cpp
new file mode 100644
index 00000000000..d0e5b38e0bf
--- /dev/null
+++ b/engines/bolt/booths/scooby.cpp
@@ -0,0 +1,28 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "bolt/bolt.h"
+
+namespace Bolt {
+
+
+
+} // End of namespace Bolt
diff --git a/engines/bolt/booths/topcat.cpp b/engines/bolt/booths/topcat.cpp
new file mode 100644
index 00000000000..d0e5b38e0bf
--- /dev/null
+++ b/engines/bolt/booths/topcat.cpp
@@ -0,0 +1,28 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "bolt/bolt.h"
+
+namespace Bolt {
+
+
+
+} // End of namespace Bolt
diff --git a/engines/bolt/booths/yogi.cpp b/engines/bolt/booths/yogi.cpp
new file mode 100644
index 00000000000..d0e5b38e0bf
--- /dev/null
+++ b/engines/bolt/booths/yogi.cpp
@@ -0,0 +1,28 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "bolt/bolt.h"
+
+namespace Bolt {
+
+
+
+} // End of namespace Bolt
diff --git a/engines/bolt/detection.h b/engines/bolt/detection.h
index ead614b2ab2..e36027700d0 100644
--- a/engines/bolt/detection.h
+++ b/engines/bolt/detection.h
@@ -38,7 +38,7 @@ extern const PlainGameDescriptor boltGames[];
 
 extern const ADGameDescription gameDescriptions[];
 
-#define GAMEOPTION_ORIGINAL_SAVELOAD GUIO_GAMEOPTIONS1
+#define GAMEOPTION_EXTEND_SCREEN GUIO_GAMEOPTIONS1
 
 } // End of namespace Bolt
 
diff --git a/engines/bolt/detection_tables.h b/engines/bolt/detection_tables.h
index f3785c88ad5..7453d983699 100644
--- a/engines/bolt/detection_tables.h
+++ b/engines/bolt/detection_tables.h
@@ -43,7 +43,7 @@ const ADGameDescription gameDescriptions[] = {
 		Common::EN_USA,
 		Common::kPlatformWindows,
 		ADGF_UNSTABLE,
-		GUIO1(GUIO_NONE)
+		GUIO1(GAMEOPTION_EXTEND_SCREEN)
 	},
 	{
 		"carnival",
@@ -61,7 +61,7 @@ const ADGameDescription gameDescriptions[] = {
 		Common::EN_USA,
 		Common::kPlatformWindows,
 		ADGF_UNSTABLE,
-		GUIO1(GUIO_NONE)
+		GUIO1(GAMEOPTION_EXTEND_SCREEN)
 	},
 	{
 		"carnival",
@@ -76,7 +76,7 @@ const ADGameDescription gameDescriptions[] = {
 		Common::IT_ITA,
 		Common::kPlatformWindows,
 		ADGF_UNSTABLE | ADGF_DEMO,
-		GUIO1(GUIO_NONE)
+		GUIO1(GAMEOPTION_EXTEND_SCREEN)
 	},
 	{
 		"carnival",
@@ -91,7 +91,7 @@ const ADGameDescription gameDescriptions[] = {
 		Common::IT_ITA,
 		Common::kPlatformWindows,
 		ADGF_UNSTABLE | ADGF_DEMO,
-		GUIO1(GUIO_NONE)
+		GUIO1(GAMEOPTION_EXTEND_SCREEN)
 	},
 
 	AD_TABLE_END_MARKER
diff --git a/engines/bolt/metaengine.cpp b/engines/bolt/metaengine.cpp
index 9beb569542f..a7ae1e71de2 100644
--- a/engines/bolt/metaengine.cpp
+++ b/engines/bolt/metaengine.cpp
@@ -29,11 +29,11 @@ namespace Bolt {
 
 static const ADExtraGuiOptionsMap optionsList[] = {
 	{
-		GAMEOPTION_ORIGINAL_SAVELOAD,
+		GAMEOPTION_EXTEND_SCREEN,
 		{
-			_s("Use original save/load screens"),
-			_s("Use the original save/load screens instead of the ScummVM ones"),
-			"original_menus",
+			_s("Extended game resolution"),
+			_s("This option allows the game to play at a slightly extended resolution. This is what the Mac and CDi versions are supposed to play like."),
+			"extended_viewport",
 			false,
 			0,
 			0
diff --git a/engines/bolt/module.mk b/engines/bolt/module.mk
index 4f9c5b5ac04..5574c117a4e 100644
--- a/engines/bolt/module.mk
+++ b/engines/bolt/module.mk
@@ -2,12 +2,24 @@ MODULE := engines/bolt
 
 MODULE_OBJS = \
 	anim.o \
+	av.o \
+	barker.o \
 	bolt.o \
 	booth.o \
+	booths\fred.o \
+	booths\george.o \
+	booths\huck.o \
+	booths\scooby.o \
+	booths\topcat.o \
+	booths\yogi.o \
 	console.o \
-	gfx.o \
 	metaengine.o \
 	resource.o \
+	rtf.o \
+	ssprite.o \
+	state.o \
+	swap.o \
+	utils.o \
 	xplib\blit.o \
 	xplib\cursor.o \
 	xplib\display.o \
diff --git a/engines/bolt/resource.cpp b/engines/bolt/resource.cpp
index e1204a58c00..4d8ae6f4c1c 100644
--- a/engines/bolt/resource.cpp
+++ b/engines/bolt/resource.cpp
@@ -21,92 +21,679 @@
 
 #include "bolt/bolt.h"
 
+#include "common/memstream.h"
+
 namespace Bolt {
 
-bool BoltEngine::openBOLTLib(BOLTLib **outLib, int *outIdx, const char *fileName) {
+void BoltEngine::decompress(byte *dest, uint32 decompSize, byte *src) {
+	byte cmd;
+	byte fillByte;
+	uint16 count;
+	uint16 offset;
+	byte *backRef;
+
+	for (;;) {
+		cmd = *src++;
+
+		switch (cmd & 0xC0) {
+		case 0x00:
+			count = 0x1F - (cmd & 0x1F);
+			decompSize -= count;
+			count++;
+			while (--count)
+				*dest++ = *src++;
+			break;
+
+		case 0x40:
+			count = 0x23 - (cmd & 0x1F);
+			decompSize -= count;
+			offset = ((cmd & 0x20) << 3) + *src++;
+			backRef = dest - offset;
+			count++;
+			while (--count)
+				*dest++ = *backRef++;
+			break;
+
+		case 0x80:
+			count = (0x20 - (cmd & 0x1F)) << 2;
+			if (cmd & 0x20)
+				count += 2;
+			decompSize -= count;
+			offset = *src++ * 2;
+			backRef = dest - offset;
+			count++;
+			while (--count)
+				*dest++ = *backRef++;
+			break;
+
+		case 0xC0:
+			if (cmd & 0x20) {
+				if ((int32)decompSize <= 0)
+					return;
+			} else {
+				count = (0x20 - (cmd & 0x1F) + (*src++ << 5)) << 2;
+				decompSize -= count;
+				src++;
+				fillByte = *src++;
+				count++;
+				while (--count)
+					*dest++ = fillByte;
+			}
+			break;
+		}
+	}
+}
+
+bool BoltEngine::libRead(Common::File *fileHandle, uint32 offset, byte *dest, uint32 size) {
+	uint32 bytesRead = size;
+
+	if (g_cachedFileHandle != fileHandle || g_cachedFilePos != offset) {
+		if (!_xp->setFilePos(fileHandle, offset, 0))
+			return false;
+	}
+
+	if (_xp->readFile(fileHandle, dest, &bytesRead)) {
+		if (bytesRead == size) {
+			g_cachedFileHandle = fileHandle;
+			g_cachedFilePos = offset + bytesRead;
+			return true;
+		}
+	}
+
+	g_cachedFileHandle = nullptr;
 	return false;
 }
 
-void BoltEngine::closeBOLTLib(BOLTLib **lib) {
+void BoltEngine::resolveIt(uint32 *ref) {
+	if (*ref == 0xFFFFFFFF) {
+		*ref = 0;
+		return;
+	}
+
+	uint32 swapped = FROM_BE_32(*ref);
+
+	byte *resolved = memberAddrOffset(g_boltCurrentLib, swapped);
+
+	if (resolved != nullptr) {
+		uint32 idx = g_resolvedPtrs.size();
+		g_resolvedPtrs.push_back(resolved);
+		*ref = idx | 0x80000000; // High bit marks as resolved index
+	} else {
+		// Target not loaded yet, queue for later...
+		*ref = swapped;
+		if (g_pendingFixupCount < g_resourceIndexCount) {
+			g_resourceIndex[g_pendingFixupCount] = ref;
+			g_pendingFixupCount++;
+		}
+	}
 }
 
-bool BoltEngine::attemptFreeIndex(BOLTLib *lib, int16 groupId) {
+void BoltEngine::resolvePendingFixups() {
+	while (g_pendingFixupCount > 0) {
+		g_pendingFixupCount--;
+		uint32 *ref = (uint32 *)g_resourceIndex[g_pendingFixupCount];
+
+		byte *resolved = memberAddrOffset(g_boltCurrentLib, *ref);
+
+		uint32 idx = g_resolvedPtrs.size();
+		g_resolvedPtrs.push_back(resolved);
+		*ref = idx | 0x80000000;
+	}
+}
+
+void BoltEngine::resolveFunction(uint32 *ref) {
+	if (*ref == 0xFFFFFFFF) {
+		*ref = 0;
+		return;
+	}
+
+	// TODO!
+	warning("BoltEngine::resolveFunction(): TODO!");
+	uint16 index = (uint16)*ref;
+	*ref = index;
+}
+
+void BoltEngine::resolveAllRefs() {
+	byte *data = g_boltCurrentMemberEntry->dataPtr;
+	if (data == nullptr)
+		return;
+
+	uint32 count = g_boltCurrentMemberEntry->decompSize / 4;
+	uint32 *ptr = (uint32 *)data;
+	for (uint32 i = 0; i < count; i++) {
+		resolveIt(&ptr[i]);
+	}
+}
+
+byte *BoltEngine::getResolvedPtr(byte *data, int offset) {
+	uint32 val = READ_UINT32(data + offset);
+
+	if (val == 0)
+		return nullptr;
+
+	if (val & 0x80000000)
+		return g_resolvedPtrs[val & 0x7FFFFFFF];
+
+	return nullptr;
+}
+
+bool BoltEngine::openBOLTLib(BOLTLib **libPtr, BOLTCallbacks *callbacks, const char *fileName) {
+	Common::File *fileHandle = nullptr;
+	int16 groupCount = 0;
+
+	// If already open, bump reference count...
+	if (*libPtr) {
+		(*libPtr)->refCount++;
+		g_boltCurrentLib = *libPtr;
+		return true;
+	}
+
+	g_boltCurrentLib = nullptr;
+
+	fileHandle = _xp->openFile(fileName, 1);
+	if (fileHandle) {
+		// Read 16-byte BOLT file header...
+		byte headerData[16];
+
+		if (libRead(fileHandle, 0, headerData, 16)) {
+			Common::SeekableReadStream *headerDataStream = new Common::MemoryReadStream(headerData, 16, DisposeAfterUse::NO);
+
+			headerDataStream->read(g_boltFileHeader.header, sizeof(g_boltFileHeader.header));
+			g_boltFileHeader.groupCount = headerDataStream->readByte();
+			g_boltFileHeader.groupDirOffset = headerDataStream->readUint32BE();
+
+			groupCount = g_boltFileHeader.groupCount;
+			if (groupCount == 0)
+				groupCount = 256;
+
+			delete headerDataStream;
+
+			*libPtr = new BOLTLib(groupCount);
+
+			g_boltCurrentLib = *libPtr;
+			if (g_boltCurrentLib) {
+				(*libPtr)->refCount = 0;
+				(*libPtr)->groupCount = g_boltFileHeader.groupCount;
+				(*libPtr)->fileHandle = fileHandle;
+
+				(*libPtr)->callbacks.typeLoadCallbacks = callbacks->typeLoadCallbacks;
+				(*libPtr)->callbacks.typeFreeCallbacks = callbacks->typeFreeCallbacks;
+				(*libPtr)->callbacks.memberLoadCallbacks = callbacks->memberLoadCallbacks;
+				(*libPtr)->callbacks.memberFreeCallbacks = callbacks->memberFreeCallbacks;
+				(*libPtr)->callbacks.groupLoadCallbacks = callbacks->groupLoadCallbacks;
+				(*libPtr)->callbacks.groupFreeCallbacks = callbacks->groupFreeCallbacks;
+
+				byte *groupEntryRawData = (byte *)_xp->allocMem(groupCount * 16);
+
+				if (libRead(fileHandle, 0x10, groupEntryRawData, groupCount * 16)) {
+					Common::SeekableReadStream *groupEntryDataStream = new Common::MemoryReadStream(groupEntryRawData, groupCount * 16, DisposeAfterUse::NO);
+
+					for (int16 i = 0; i < groupCount; i++) {
+						(*libPtr)->groups[i].flags = groupEntryDataStream->readByte();
+						(*libPtr)->groups[i].loadCbIndex = groupEntryDataStream->readByte();
+						(*libPtr)->groups[i].freeCbIndex = groupEntryDataStream->readByte();
+						(*libPtr)->groups[i].memberCount = groupEntryDataStream->readByte();
+						(*libPtr)->groups[i].memberDirOffset = groupEntryDataStream->readUint32BE();
+						(*libPtr)->groups[i].memberDataOffset = groupEntryDataStream->readUint32BE();
+						(*libPtr)->groups[i].groupDataPtrPlaceholder = groupEntryDataStream->readUint32BE();
+						(*libPtr)->groups[i].memberData = nullptr;
+
+						(*libPtr)->groups[i].initMembers((*libPtr)->groups[i].memberCount);
+					}
+
+					delete groupEntryDataStream;
+
+					return true;
+				}
+			}
+		}
+	}
+
+	if (fileHandle != 0)
+		_xp->closeFile(fileHandle);
+
+	if (g_boltCurrentLib != nullptr) {
+		_xp->freeMem(g_boltCurrentLib);
+		g_boltCurrentLib = nullptr;
+	}
+
 	return false;
 }
 
-void BoltEngine::loadGroupDirectory() {
+bool BoltEngine::closeBOLTLib(BOLTLib **libPtr) {
+	if (!*libPtr)
+		return true;
+
+	if ((*libPtr)->refCount > 0) {
+		(*libPtr)->refCount--;
+		return true;
+	}
+
+	int16 groupCount = (*libPtr)->groupCount;
+	if (groupCount == 0) {
+		groupCount = 256;
+		warning("DEBUG, closeBOLTLib(): CHECK FOR OVERFLOW ");
+	}
+
+	// Free all groups in reverse order...
+	int16 groupId = groupCount << 8;
+	while (groupCount-- > 0) {
+		groupId -= (1 << 8);
+		freeBOLTGroup(*libPtr, groupId, 0);
+	}
+
+	_xp->closeFile((*libPtr)->fileHandle);
+	_xp->freeMem(*libPtr);
+	*libPtr = nullptr;
+
+	return true;
+}
+
+bool BoltEngine::attemptFreeIndex(BOLTLib *lib, int16 groupId) {
+	BOLTGroupEntry *groupEntry = &lib->groups[groupId >> 8];
+
+	if (!groupEntry->memberData)
+		return true;
+
+	int16 memberCount = g_boltCurrentGroupEntry->memberCount;
+	if (memberCount == 0)
+		memberCount = 256;
+
+	for (int16 i = memberCount - 1; i >= 0; i--) {
+		BOLTMemberEntry *member = &groupEntry->members[i];
+		if (member->dataPtr)
+			return false; // Still in use
+	}
+
+	_xp->freeMem(groupEntry->memberData);
+	groupEntry->memberData = nullptr;
+
+	return true;
+}
+
+bool BoltEngine::loadGroupDirectory() {
+	int16 memberCount = g_boltCurrentGroupEntry->memberCount;
+	if (memberCount == 0)
+		memberCount = 256;
+
+	if (g_boltCurrentGroupEntry->memberData) {
+		g_boltRawMemberData = g_boltCurrentGroupEntry->memberData;
+		return true;
+	}
+
+	byte *rawMemberData = (byte *)_xp->allocMem((uint32)memberCount * 16 + 4);
+	g_boltRawMemberData = rawMemberData;
+	g_boltCurrentGroupEntry->memberData = g_boltRawMemberData;
+
+	if (rawMemberData) {
+		if (libRead(g_boltCurrentLib->fileHandle, g_boltCurrentGroupEntry->memberDataOffset, rawMemberData + 4, (uint32)memberCount * 16)) {
+			Common::SeekableReadStream *memberEntryStream = new Common::MemoryReadStream(rawMemberData + 4, (uint32)memberCount * 16, DisposeAfterUse::NO);
+
+			for (int16 i = 0; i < memberCount; i++) {
+				g_boltCurrentGroupEntry->members[i].flags = memberEntryStream->readByte();
+				g_boltCurrentGroupEntry->members[i].preLoadCbIndex = memberEntryStream->readByte();
+				g_boltCurrentGroupEntry->members[i].preFreeCbIndex = memberEntryStream->readByte();
+				g_boltCurrentGroupEntry->members[i].typeCbIndex = memberEntryStream->readByte();
+				g_boltCurrentGroupEntry->members[i].decompSize = memberEntryStream->readUint32BE();
+				g_boltCurrentGroupEntry->members[i].fileOffset = memberEntryStream->readUint32BE();
+				g_boltCurrentGroupEntry->members[i].dataPtrPlaceholder = memberEntryStream->readUint32BE();
+				g_boltCurrentGroupEntry->members[i].dataPtr = nullptr;
+			}
+
+			delete memberEntryStream;
+
+			g_boltRawMemberData = g_boltCurrentGroupEntry->memberData;
+			return true;
+		}
+	}
+
+	if (g_boltRawMemberData)
+		_xp->freeMem(g_boltRawMemberData);
+
+	g_boltRawMemberData = nullptr;
+	g_boltCurrentGroupEntry->memberData = nullptr;
+	return false;
 }
 
 bool BoltEngine::getBOLTGroup(BOLTLib *lib, int16 groupId, int16 flags) {
+	g_boltLoadDepth++;
+	int16 resId = groupId & 0xFF00;
+
+	if (lib) {
+		g_boltCurrentLib = lib;
+		g_boltCurrentGroupEntry = &lib->groups[groupId >> 8];
+
+		int16 memberCount = g_boltCurrentGroupEntry->memberCount;
+		if (memberCount == 0)
+			memberCount = 256;
+
+		if (loadGroupDirectory()) {
+			if (g_boltCurrentGroupEntry->loadCbIndex != 0) {
+				lib->callbacks.groupLoadCallbacks[g_boltCurrentGroupEntry->loadCbIndex]();
+			}
+
+			for (int16 i = 0; i < memberCount; i++) {
+				if (!getBOLTMember(lib, resId)) {
+					freeBOLTGroup(lib, resId, 0);
+					g_boltLoadDepth--;
+					return false;
+				}
+
+				resId++;
+			}
+
+			resolvePendingFixups();
+			g_boltLoadDepth--;
+			return true;
+		}
+	}
+
+	freeBOLTGroup(lib, resId, 0);
+	g_boltLoadDepth--;
 	return false;
 }
 
 void BoltEngine::freeBOLTGroup(BOLTLib *lib, int16 groupId, int16 flags) {
+	if (!lib)
+		return;
+
+	g_boltCurrentGroupEntry = &lib->groups[groupId >> 8];
+	g_boltRawMemberData = g_boltCurrentGroupEntry->memberData;
+
+	if (g_boltCurrentGroupEntry->freeCbIndex != 0) {
+		lib->callbacks.groupFreeCallbacks[g_boltCurrentGroupEntry->freeCbIndex]();
+	}
+
+	if (!g_boltRawMemberData)
+		return;
+
+	int16 memberCount = g_boltCurrentGroupEntry->memberCount;
+	if (memberCount == 0)
+		memberCount = 256;
+
+	int16 resId = groupId & 0xFF00;
+	for (int16 i = 0; i < memberCount; i++) {
+		freeBOLTMember(lib, resId);
+		resId++;
+	}
+
+	attemptFreeIndex(lib, resId);
 }
 
-void *BoltEngine::getBOLTMember(BOLTLib *lib, int16 resId) {
+byte *BoltEngine::getBOLTMember(BOLTLib *lib, int16 resId) {
+	byte memberIndex = (byte)resId;
+	uint32 compressedSize;
+	byte *tempBuf;
+
+	if (lib) {
+		g_boltCurrentLib = lib;
+		g_boltCurrentGroupEntry = &lib->groups[resId >> 8];
+
+		int16 memberCount = g_boltCurrentGroupEntry->memberCount;
+		if (memberCount == 0)
+			memberCount = 256;
+
+		if (loadGroupDirectory()) {
+			g_boltCurrentMemberEntry = &g_boltCurrentGroupEntry->members[memberIndex];
+
+			if (g_boltCurrentMemberEntry->preLoadCbIndex != 0) {
+				lib->callbacks.memberLoadCallbacks[g_boltCurrentMemberEntry->preLoadCbIndex]();
+			}
+
+			if (g_boltCurrentMemberEntry->dataPtr != nullptr)
+				return g_boltCurrentMemberEntry->dataPtr;
+
+			g_boltCurrentMemberEntry->dataPtr = (byte *)_xp->allocMem(g_boltCurrentMemberEntry->decompSize);
+			if (g_boltCurrentMemberEntry->dataPtr) {
+				// Bit 3 in flags = uncompressed
+				if (g_boltCurrentMemberEntry->flags & 0x08) {
+					// Read directly into destination
+					if (!libRead(lib->fileHandle, g_boltCurrentMemberEntry->fileOffset, g_boltCurrentMemberEntry->dataPtr, g_boltCurrentMemberEntry->decompSize)) {
+						if (g_boltCurrentMemberEntry->dataPtr != nullptr) {
+							_xp->freeMem(g_boltCurrentMemberEntry->dataPtr);
+							g_boltCurrentMemberEntry->dataPtr = nullptr;
+						}
+
+						return nullptr;
+					}
+				} else {
+					// Compressed member
+					if (memberIndex + 1 < memberCount) {
+						BOLTMemberEntry *nextEntry = &g_boltCurrentGroupEntry->members[memberIndex + 1];
+						compressedSize = nextEntry->fileOffset;
+					} else {
+						// Last entry: total = memberDirSize + memberDirOffset + memberDataOffset
+						warning("is this even working?");
+						compressedSize = (uint32)(memberCount * 16) + g_boltCurrentGroupEntry->memberDataOffset + g_boltCurrentGroupEntry->memberDirOffset;
+					}
+
+					compressedSize -= g_boltCurrentMemberEntry->fileOffset;
+
+					tempBuf = (byte *)_xp->allocMem(compressedSize);
+					if (!tempBuf) {
+						if (g_boltCurrentMemberEntry->dataPtr != nullptr) {
+							_xp->freeMem(g_boltCurrentMemberEntry->dataPtr);
+							g_boltCurrentMemberEntry->dataPtr = nullptr;
+						}
+
+						return nullptr;
+					}
+
+					if (!libRead(lib->fileHandle, g_boltCurrentMemberEntry->fileOffset, tempBuf, compressedSize)) {
+						_xp->freeMem(tempBuf);
+
+						if (g_boltCurrentMemberEntry->dataPtr != nullptr) {
+							_xp->freeMem(g_boltCurrentMemberEntry->dataPtr);
+							g_boltCurrentMemberEntry->dataPtr = nullptr;
+						}
+
+						return nullptr;
+					}
+
+					decompress(g_boltCurrentMemberEntry->dataPtr, g_boltCurrentMemberEntry->decompSize, tempBuf);
+
+					_xp->freeMem(tempBuf);
+				}
+
+				lib->callbacks.typeLoadCallbacks[g_boltCurrentMemberEntry->typeCbIndex]();
+
+				if (g_boltLoadDepth == 0)
+					resolvePendingFixups();
+
+				return g_boltCurrentMemberEntry->dataPtr;
+			}
+		}
+	}
+
+	if (g_boltCurrentMemberEntry->dataPtr != nullptr) {
+		_xp->freeMem(g_boltCurrentMemberEntry->dataPtr);
+		g_boltCurrentMemberEntry->dataPtr = nullptr;
+	}
+
 	return nullptr;
 }
 
 bool BoltEngine::freeBOLTMember(BOLTLib *lib, int16 resId) {
-	return false;
+	if (!lib)
+		return true;
+
+	g_boltCurrentGroupEntry = &lib->groups[resId >> 8];
+	g_boltRawMemberData = g_boltCurrentGroupEntry->memberData;
+
+	if (g_boltRawMemberData == nullptr)
+		return true;
+
+	g_boltCurrentMemberEntry = &g_boltCurrentGroupEntry->members[(resId & 0xFF)];
+
+	if (g_boltCurrentMemberEntry->preFreeCbIndex != 0) {
+		lib->callbacks.memberFreeCallbacks[g_boltCurrentMemberEntry->preFreeCbIndex]();
+	}
+
+	if (g_boltCurrentMemberEntry->dataPtr == nullptr)
+		return true;
+
+	lib->callbacks.typeFreeCallbacks[g_boltCurrentMemberEntry->typeCbIndex]();
+
+	_xp->freeMem(g_boltCurrentMemberEntry->dataPtr);
+	g_boltCurrentMemberEntry->dataPtr = nullptr;
+
+	return true;
 }
 
-void *BoltEngine::memberAddr(BOLTLib *lib, int16 resId) {
-	return nullptr;
+Common::Rect BoltEngine::memberToRect(byte *data) {
+	int16 x = (int16)READ_UINT16(data);
+	int16 y = (int16)READ_UINT16(data + 2);
+	int16 w = (int16)READ_UINT16(data + 4);
+	int16 h = (int16)READ_UINT16(data + 6);
+	return Common::Rect(x, y, x + w, y + h);
 }
 
-void *BoltEngine::memberAddrOffset(BOLTLib *lib, uint32 resIdAndOffset) {
-	return nullptr;
+byte *BoltEngine::memberAddr(BOLTLib *lib, int16 resId) {
+	if (!lib)
+		return nullptr;
+
+	BOLTGroupEntry *groupEntry = &lib->groups[resId >> 8];
+
+	if (!groupEntry->memberData)
+		return nullptr;
+
+	BOLTMemberEntry *member = &groupEntry->members[(resId & 0xFF)];
+
+	return member->dataPtr;
+}
+
+byte *BoltEngine::memberAddrOffset(BOLTLib *lib, uint32 resIdAndOffset) {
+	if (!lib)
+		return nullptr;
+
+	int16 resId = (int16)(resIdAndOffset >> 16);
+	int16 offset = (int16)(resIdAndOffset & 0xFFFF);
+
+	BOLTGroupEntry *groupEntry = &lib->groups[resId >> 8];
+
+	if (!groupEntry->memberData)
+		return nullptr;
+
+	BOLTMemberEntry *member = &groupEntry->members[(resId & 0xFF)];
+	return (byte *)&member->dataPtr[offset];
 }
 
 uint32 BoltEngine::memberSize(BOLTLib *lib, int16 resId) {
-	return uint32();
+	if (!lib)
+		return 0;
+
+	BOLTGroupEntry *groupEntry = &lib->groups[resId >> 8];
+
+	if (!groupEntry->memberData)
+		return 0;
+
+	BOLTMemberEntry *member = &groupEntry->members[(resId & 0xFF)];
+	return member->decompSize;
 }
 
-void *BoltEngine::groupAddr(BOLTLib *lib, int16 groupId) {
-	return nullptr;
+byte *BoltEngine::groupAddr(BOLTLib *lib, int16 groupId) {
+	if (!lib)
+		return nullptr;
+
+	return lib->groups[groupId >> 8].memberData;
 }
 
 bool BoltEngine::allocResourceIndex() {
-	return false;
+	g_resourceIndex = (uint32 **)_xp->allocMem(g_resourceIndexCount * sizeof(uintptr));
+	if (!g_resourceIndex)
+		return false;
+
+	return true;
 }
 
 void BoltEngine::freeResourceIndex() {
+	if (g_resourceIndex) {
+		_xp->freeMem(g_resourceIndex);
+		g_resourceIndex = nullptr;
+	}
 }
 
-bool BoltEngine::initVRam(int16 poolSize) {
-	return false;
-}
+void BoltEngine::swapAllWords() {
+	byte *data = g_boltCurrentMemberEntry->dataPtr;
+	if (!data)
+		return;
 
-void BoltEngine::freeVRam() {
+	int16 count = int16(g_boltCurrentMemberEntry->decompSize / 2);
+	for (int16 i = 0; i < count; i++) {
+		WRITE_UINT16(data, READ_BE_INT16(data));
+		data += 2;
+	}
 }
 
-bool BoltEngine::vLoad(void *dest, const char *name) {
-	return false;
-}
+void BoltEngine::swapAllLongs() {
+	byte *data = g_boltCurrentMemberEntry->dataPtr;
+	if (!data)
+		return;
 
-bool BoltEngine::vSave(void *src, uint16 srcSize, const char *name) {
-	return false;
+	int16 count = (int16)(g_boltCurrentMemberEntry->decompSize / 4);
+	for (int16 i = 0; i < count; i++) {
+		WRITE_UINT32(data, READ_BE_UINT32(data));
+		data += 4;
+	}
 }
 
-bool BoltEngine::vDelete(const char *name) {
-	return false;
-}
+BOLTCallback BoltEngine::g_defaultTypeLoadCallbacks[25];
+BOLTCallback BoltEngine::g_defaultTypeFreeCallbacks[25];
+BOLTCallback BoltEngine::g_defaultMemberLoadCallbacks[25];
+BOLTCallback BoltEngine::g_defaultMemberFreeCallbacks[25];
+BOLTCallback BoltEngine::g_defaultGroupLoadCallbacks[25];
+BOLTCallback BoltEngine::g_defaultGroupFreeCallbacks[25];
 
-void BoltEngine::memMove(void *dest, void *src, uint16 count) {
-}
+void BoltEngine::noOpCb() {}
+void BoltEngine::swapAllWordsCb() { ((BoltEngine *)g_engine)->swapAllWords(); }
+void BoltEngine::swapPicHeaderCb() { ((BoltEngine *)g_engine)->swapPicHeader(); }
+void BoltEngine::swapAndResolvePicDescCb() { ((BoltEngine *)g_engine)->swapAndResolvePicDesc(); }
+void BoltEngine::swapFirstWordCb() { ((BoltEngine *)g_engine)->swapFirstWord(); }
+void BoltEngine::swapFirstTwoWordsCb() { ((BoltEngine *)g_engine)->swapFirstTwoWords(); }
+void BoltEngine::swapFirstFourWordsCb() { ((BoltEngine *)g_engine)->swapFirstFourWords(); }
+void BoltEngine::swapSpriteHeaderCb() { ((BoltEngine *)g_engine)->swapSpriteHeader(); }
+void BoltEngine::freeSpriteCleanUpCb() { ((BoltEngine *)g_engine)->freeSpriteCleanUp(); }
 
-void *BoltEngine::dataAddress(int16 recordOffset) {
-	return nullptr;
-}
+void BoltEngine::initCallbacks() {
+	for (int i = 0; i < ARRAYSIZE(g_defaultTypeLoadCallbacks); i++) {
+		g_defaultTypeLoadCallbacks[i] = noOpCb;
+	}
 
-uint16 BoltEngine::dataSize(int16 recordOffset) {
-	return uint16();
-}
+	g_defaultTypeLoadCallbacks[2] = swapAllWordsCb;
+	g_defaultTypeLoadCallbacks[8] = swapSpriteHeaderCb;
+	g_defaultTypeLoadCallbacks[10] = swapPicHeaderCb;
+	g_defaultTypeLoadCallbacks[11] = swapAndResolvePicDescCb;
+	g_defaultTypeLoadCallbacks[12] = swapFirstTwoWordsCb;
+	g_defaultTypeLoadCallbacks[14] = swapFirstFourWordsCb;
 
-bool BoltEngine::findRecord(const char *name, int16 *outOffset) {
-	return false;
+	for (int i = 0; i < ARRAYSIZE(g_defaultTypeFreeCallbacks); i++) {
+		g_defaultTypeFreeCallbacks[i] = noOpCb;
+	}
+
+	g_defaultTypeFreeCallbacks[8] = freeSpriteCleanUpCb;
+
+	for (int i = 0; i < ARRAYSIZE(g_defaultMemberLoadCallbacks); i++) {
+		g_defaultMemberLoadCallbacks[i] = noOpCb;
+	}
+
+	for (int i = 0; i < ARRAYSIZE(g_defaultMemberFreeCallbacks); i++) {
+		g_defaultMemberFreeCallbacks[i] = noOpCb;
+	}
+
+	for (int i = 0; i < ARRAYSIZE(g_defaultGroupLoadCallbacks); i++) {
+		g_defaultGroupLoadCallbacks[i] = noOpCb;
+	}
+
+	for (int i = 0; i < ARRAYSIZE(g_defaultGroupFreeCallbacks); i++) {
+		g_defaultGroupFreeCallbacks[i] = noOpCb;
+	}
+
+	g_resourceDefaultCallbacks.typeLoadCallbacks = g_defaultTypeLoadCallbacks;
+	g_resourceDefaultCallbacks.typeFreeCallbacks = g_defaultTypeFreeCallbacks;
+	g_resourceDefaultCallbacks.memberLoadCallbacks = g_defaultMemberLoadCallbacks;
+	g_resourceDefaultCallbacks.memberFreeCallbacks = g_defaultMemberFreeCallbacks;
+	g_resourceDefaultCallbacks.groupLoadCallbacks = g_defaultGroupLoadCallbacks;
+	g_resourceDefaultCallbacks.groupFreeCallbacks = g_defaultGroupFreeCallbacks;
 }
 
 } // End of namespace Bolt
diff --git a/engines/bolt/rtf.cpp b/engines/bolt/rtf.cpp
new file mode 100644
index 00000000000..e6c15d95a09
--- /dev/null
+++ b/engines/bolt/rtf.cpp
@@ -0,0 +1,526 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "bolt/bolt.h"
+
+#include "common/memstream.h"
+
+namespace Bolt {
+
+RTFResource *BoltEngine::openRTF(const char *fileName) {
+	uint32 headerTag = 0;
+	uint32 dataSize = 0;
+	uint32 bytesRead = 0;
+	RTFResource *rtf = nullptr;
+
+	Common::File *fileHandle = _xp->openFile(fileName, 1);
+	if (!fileHandle)
+		return nullptr;
+
+	// Read 8-byte header: tag + size
+	bytesRead = 4;
+	if (_xp->readFile(fileHandle, &headerTag, &bytesRead) && bytesRead == 4) {
+		bytesRead = 4;
+		if (_xp->readFile(fileHandle, &dataSize, &bytesRead) && bytesRead == 4) {
+			headerTag = FROM_BE_32(headerTag);
+			dataSize = FROM_BE_32(dataSize);
+
+			if (headerTag == MKTAG('M', 'A', 'P', ' ')) {
+				if ((dataSize & 3) == 0) {
+					// Allocate: 10-byte header + index data
+					rtf = new RTFResource();
+					rtf->indexTableRawData = (byte *)_xp->allocMem(dataSize);
+					if (rtf && rtf->indexTableRawData) {
+						// RTF layout:
+						//   +0x00: file handle (uint32)
+						//   +0x04: pointer to index table (far ptr)
+						//   +0x08: entry count (word)
+						//   +0x0A: index table data begins here
+						rtf->fileHandle = fileHandle;
+						rtf->indexTablePtr = rtf->indexTable;
+						rtf->entryCount = (int16)(dataSize / sizeof(uint32));
+						rtf->reserveTableSize(rtf->entryCount);
+
+						// Read offset table of available animations...
+						bytesRead = dataSize;
+						if (_xp->readFile(fileHandle, rtf->indexTableRawData, &bytesRead)) {
+							Common::SeekableReadStream *indexDataStream = new Common::MemoryReadStream((byte *)rtf->indexTableRawData, bytesRead, DisposeAfterUse::NO);
+
+							for (int16 i = 0; i < rtf->entryCount; i++) {
+								rtf->indexTable[i] = indexDataStream->readUint32BE();
+							}
+
+							return rtf;
+						}
+					}
+				}
+			}
+		}
+	}
+
+	if (rtf && rtf->indexTableRawData)
+		_xp->freeMem(rtf->indexTableRawData);
+
+	if (rtf)
+		delete rtf;
+
+	if (fileHandle)
+		_xp->closeFile(fileHandle);
+
+	return nullptr;
+}
+
+void BoltEngine::closeRTF(RTFResource *rtf) {
+	_xp->closeFile(rtf->fileHandle);
+	_xp->freeMem(rtf->indexTableRawData);
+
+	delete rtf;
+	rtf = nullptr;
+}
+
+bool BoltEngine::playRTF(RTFResource *rtfFile, int16 animIndex, byte *ringBuffer, int32 bufferSize) {
+	g_rtfFileHandle = rtfFile->fileHandle;
+
+	if (animIndex >= rtfFile->entryCount)
+		return false;
+
+	uint32 *indexTable = rtfFile->indexTable;
+	if (!_xp->setFilePos(g_rtfFileHandle, indexTable[animIndex], 0))
+		return false;
+
+	g_rtfChunkRemaining = 0;
+	g_rtfMidChunk = false;
+
+	g_ringBufBase = ringBuffer;
+	g_ringBufWritePtr = ringBuffer;
+	g_ringBufSize = bufferSize;
+	g_ringBufFreeSpace = bufferSize;
+
+	if (bufferSize >= 0x96000) {
+		g_ringBufLowWater = bufferSize - 0x4B000;
+		g_ringBufHighWater = bufferSize - 0x19000;
+	} else {
+		g_ringBufLowWater = bufferSize - bufferSize / 2;
+		g_ringBufHighWater = bufferSize - bufferSize / 4;
+	}
+
+	g_ringBufUsed = 0;
+	g_rtfSoundActive = true;
+	g_rtfPlaybackTime = 0;
+	g_rtfCumulativeTime = 0;
+	g_rtfPendingFrame = nullptr;
+	g_rtfSoundQueueHead = nullptr;
+	g_rtfSoundPlayHead = nullptr;
+	g_rtfChunkListTail = nullptr;
+	g_rtfChunkListHead = nullptr;
+	g_rtfChunkCount = 0;
+	g_rtfQueuedSoundCount = 0;
+	g_rtfSoundTiming = 0;
+
+	g_rtfAnimStartOffset = indexTable[animIndex];
+	g_rtfNeedInitialFill = true;
+
+	return true;
+}
+
+bool BoltEngine::fillRTFBuffer() {
+	if (!g_rtfNeedInitialFill)
+		return false;
+
+	if (g_ringBufFreeSpace > g_ringBufLowWater && g_rtfSoundActive) {
+		g_rtfSoundActive = readPacket();
+		return true;
+	}
+
+	g_rtfNeedInitialFill = false;
+	return false;
+}
+
+void BoltEngine::flushRTFSoundQueue() {
+	while (g_rtfQueuedSoundCount < 50 && g_rtfSoundQueueHead) {
+		RTFPacket *packet = g_rtfSoundQueueHead;
+
+		if (packet->tag == MKTAG('A', '2', '2', '8')) {
+			g_rtfQueuedSoundCount++;
+			_xp->playSound(packet->dataPtr, packet->dataSize, 22050);
+
+			if (!g_rtfSoundPlayHead)
+				g_rtfSoundPlayHead = g_rtfSoundQueueHead;
+		}
+
+		g_rtfSoundQueueHead = packet->next;
+	}
+}
+
+bool BoltEngine::maintainRTF(int16 mode, RTFPacket **outFrameData) {
+	if (!g_rtfFileHandle)
+		return false;
+
+	if (!g_ringBufBase)
+		return false;
+
+	if (outFrameData)
+		*outFrameData = nullptr;
+
+	// Initial buffer fill phase...
+	if (g_rtfNeedInitialFill) {
+		if (g_ringBufFreeSpace > g_ringBufLowWater && g_rtfSoundActive) {
+			g_rtfSoundActive = readPacket();
+			return true;
+		}
+
+		g_rtfNeedInitialFill = false;
+	}
+
+	// Advance sound play head when a sound chunk finishes (mode != 0)
+	if (mode != 0) {
+		g_rtfQueuedSoundCount--;
+		g_rtfSoundTiming -= g_rtfSoundPlayHead->duration;
+		g_rtfPlaybackTime += g_rtfSoundPlayHead->duration;
+
+		// Walk to next A228 sound chunk
+		do {
+			g_rtfSoundPlayHead = g_rtfSoundPlayHead->next;
+			if (!g_rtfSoundPlayHead)
+				break;
+		} while (g_rtfSoundPlayHead->tag != MKTAG('A', '2', '2', '8'));
+	}
+
+	// Queue up to 50 pending sound chunks...
+	while (g_rtfQueuedSoundCount < 50 && g_rtfSoundQueueHead) {
+		if (g_rtfSoundQueueHead->tag == MKTAG('A', '2', '2', '8')) {
+			g_rtfQueuedSoundCount++;
+			_xp->playSound(g_rtfSoundQueueHead->dataPtr, g_rtfSoundQueueHead->dataSize, 22050);
+
+			if (!g_rtfSoundPlayHead)
+				g_rtfSoundPlayHead = g_rtfSoundQueueHead;
+		}
+
+		g_rtfSoundQueueHead = g_rtfSoundQueueHead->next;
+	}
+
+	if (g_rtfPendingFrame) {
+		freePacket(g_rtfPendingFrame);
+		g_rtfPendingFrame = nullptr;
+	}
+
+	// Playback complete?
+	if (!g_rtfSoundActive && g_rtfChunkCount == 0) {
+		resetPlaybackState();
+		return false;
+	}
+
+	// Fill ring buffer if needed...
+	if (g_rtfSoundActive && g_ringBufFreeSpace > g_ringBufLowWater) {
+		g_rtfSoundActive = readPacket();
+		if (g_ringBufFreeSpace > g_ringBufHighWater)
+			return true;
+	}
+
+	// Check if next video frame is due...
+	if (g_rtfChunkCount != 0 && (uint16)g_rtfChunkListHead->timestamp <= (uint16)g_rtfPlaybackTime) {
+		g_rtfPendingFrame = deQueuePacket();
+
+		// If it's a sound chunk, just return...
+		if (g_rtfPendingFrame->tag == MKTAG('A', '2', '2', '8'))
+			return true;
+
+		// Calculate frame timing: how late are we?
+		g_rtfPendingFrame->skipCount = g_rtfPlaybackTime - g_rtfPendingFrame->timestamp;
+
+		// Bump skip count if buffer is low...
+		if (g_rtfSoundActive && g_ringBufFreeSpace > g_ringBufLowWater)
+			g_rtfPendingFrame->skipCount += 1;
+
+		if (!g_rtfSoundActive)
+			g_rtfPendingFrame->frameRate = 50; // fixed rate, no sound
+		else
+			g_rtfPendingFrame->frameRate = g_rtfSoundTiming;
+
+		// Find next sound chunk in queue for timing info...
+		RTFPacket *chunk = g_rtfChunkListHead;
+		while (chunk) {
+			if (chunk->tag == MKTAG('A', '2', '2', '8'))
+				break;
+
+			chunk = chunk->next;
+		}
+
+		if (chunk)
+			g_rtfPendingFrame->duration = chunk->duration;
+		else
+			g_rtfPendingFrame->duration = 0;
+
+		if (outFrameData)
+			*outFrameData = g_rtfPendingFrame;
+
+		return true;
+	}
+
+	if (g_rtfSoundActive)
+		g_rtfSoundActive = readPacket();
+
+	return true;
+}
+
+bool BoltEngine::isRTFPlaying() {
+	if (g_rtfSoundActive)
+		return true;
+
+	if (g_rtfSoundQueueHead)
+		return true;
+
+	if (g_rtfQueuedSoundCount > 10)
+		return true;
+
+	return false;
+}
+
+bool BoltEngine::killRTF(uint32 *outFilePos) {
+	if (!g_rtfFileHandle)
+		return false;
+
+	if (!g_ringBufBase)
+		return false;
+
+	_xp->stopSound();
+	
+	if (outFilePos)
+		*outFilePos = g_rtfAnimStartOffset;
+
+	return true;
+}
+
+bool BoltEngine::readPacket() {
+	uint32 readSize;
+	uint32 bytesRead;
+	byte padByte;
+	byte *destPtr;
+
+	if (!g_rtfMidChunk) {
+		// Read 8-byte chunk header...
+		bytesRead = 4;
+		if (!_xp->readFile(g_rtfFileHandle, &g_rtfChunkTag, &bytesRead) || bytesRead != 4)
+			return false;
+
+		bytesRead = 4;
+		if (!_xp->readFile(g_rtfFileHandle, &g_rtfChunkSize, &bytesRead) || bytesRead != 4)
+			return false;
+
+		g_rtfChunkTag = FROM_BE_32(g_rtfChunkTag);
+		g_rtfChunkSize = FROM_BE_32(g_rtfChunkSize);
+
+		// End-of-file marker: 'EOR '
+		if (g_rtfChunkTag == MKTAG('E', 'O', 'R',' '))
+			return false;
+
+		g_rtfChunkRemaining = g_rtfChunkSize;
+		g_rtfMidChunk = true;
+		g_rtfCurrentPacket = nullptr;
+	}
+
+	if (!g_rtfCurrentPacket) {
+		g_rtfCurrentPacket = allocPacket(g_rtfChunkSize);
+		if (!g_rtfCurrentPacket)
+			return true;
+
+		g_rtfCurrentPacket->tag = g_rtfChunkTag;
+	}
+
+	// Read up to 4096 bytes at a time...
+	readSize = (g_rtfChunkRemaining > 0x1000) ? 0x1000 : g_rtfChunkRemaining;
+
+	if (readSize != 0) {
+		destPtr = &g_rtfCurrentPacket->dataPtr[g_rtfChunkSize - g_rtfChunkRemaining];
+
+		bytesRead = readSize;
+		if (!_xp->readFile(g_rtfFileHandle, destPtr, &bytesRead))
+			return false;
+
+		g_rtfChunkRemaining -= bytesRead;
+	}
+
+	if (g_rtfChunkRemaining != 0)
+		return true; // More data to read...
+
+	// Chunk fully read!
+	g_rtfMidChunk = false;
+
+	// Skip padding byte for odd-sized chunks...
+	if (g_rtfChunkSize & 1) {
+		bytesRead = 1;
+		if (!_xp->readFile(g_rtfFileHandle, &padByte, &bytesRead))
+			return false;
+	}
+
+	// Finally update file position tracker...
+	g_rtfAnimStartOffset += g_rtfChunkSize + (g_rtfChunkSize & 1) + 8;
+
+	preProcessPacket(g_rtfCurrentPacket);
+
+	return true;
+}
+
+void BoltEngine::preProcessPacket(RTFPacket *packet) {
+	if (packet->tag == MKTAG('A', '2', '2', '8')) {
+		// Sound chunk: attempt to play it immediately!
+		if (g_rtfQueuedSoundCount < 50 && !g_rtfNeedInitialFill) {
+			g_rtfQueuedSoundCount++;
+			_xp->playSound(packet->dataPtr, packet->dataSize, 22050);
+
+			if (!g_rtfSoundPlayHead)
+				g_rtfSoundPlayHead = packet;
+		} else {
+			// Can't play yet, mark queue head...
+			if (!g_rtfSoundQueueHead)
+				g_rtfSoundQueueHead = packet;
+		}
+
+		// Duration in hundredths of a second: dataSize / 2205 (22050 Hz / 10)
+		packet->duration = packet->dataSize / 2205;
+		if (packet->duration == 0)
+			packet->duration = 1;
+
+		g_rtfCumulativeTime += packet->duration;
+		g_rtfSoundTiming += packet->duration;
+	}
+
+	// Stamp packet with current timeline position...
+	packet->timestamp = g_rtfCumulativeTime;
+
+	queuePacket(packet);
+}
+
+void BoltEngine::queuePacket(RTFPacket *packet) {
+	debug(5, "BoltEngine::queuePacket(): Queueing packet of type: %c%c%c%c",
+		  (packet->tag >> 24) & 0xFF,
+		  (packet->tag >> 16) & 0xFF,
+		  (packet->tag >> 8) & 0xFF,
+		  packet->tag & 0xFF);
+
+	if (g_rtfChunkListTail) {
+		g_rtfChunkListTail->next = packet;
+	} else {
+		g_rtfChunkListHead = packet;
+	}
+
+	g_rtfChunkListTail = packet;
+	packet->next = nullptr;
+	g_rtfChunkCount++;
+}
+
+RTFPacket *BoltEngine::deQueuePacket() {
+	if (g_rtfChunkListHead)
+		debug(5, "BoltEngine::deQueuePacket(): Dequeuing packet of type: %c%c%c%c",
+			  (g_rtfChunkListHead->tag >> 24) & 0xFF,
+			  (g_rtfChunkListHead->tag >> 16) & 0xFF,
+			  (g_rtfChunkListHead->tag >> 8) & 0xFF,
+			  g_rtfChunkListHead->tag & 0xFF);
+
+	RTFPacket *packet = g_rtfChunkListHead;
+
+	if (g_rtfChunkListHead) {
+		g_rtfChunkListHead = packet->next;
+
+		if (!g_rtfChunkListHead)
+			g_rtfChunkListTail = nullptr;
+
+		packet->next = nullptr;
+	}
+
+	g_rtfChunkCount--;
+	return packet;
+}
+
+RTFPacket *BoltEngine::allocPacket(uint32 dataSize) {
+	uint32 totalSize;
+	uint32 available;
+	uint32 freeAfterWrap;
+	byte *allocPtr = nullptr;
+
+	// Header (0x1C) + data, 4-byte aligned
+	totalSize = dataSize + 0x1C;
+	if (totalSize & 3)
+		totalSize = (totalSize + 4) & ~3;
+
+	// How much contiguous space from write pointer to end of buffer?
+	available = (uint32)(g_ringBufBase - g_ringBufWritePtr) + g_ringBufSize;
+
+	if (available >= g_ringBufFreeSpace) {
+		available = g_ringBufFreeSpace;
+		freeAfterWrap = 0;
+	} else {
+		freeAfterWrap = g_ringBufFreeSpace - available;
+	}
+
+	if (available >= totalSize) {
+		allocPtr = g_ringBufWritePtr;
+	} else {
+		// Not enough space before the end: try wrapping to buffer start...
+		if (freeAfterWrap < totalSize)
+			return nullptr;
+
+		allocPtr = g_ringBufBase;
+		g_ringBufWritePtr = g_ringBufBase;
+		g_ringBufUsed = available;
+		g_ringBufFreeSpace -= available;
+	}
+
+	g_ringBufWritePtr += totalSize;
+	g_ringBufFreeSpace -= totalSize;
+
+	// Init packet header and return...
+	RTFPacket *outPacket = new RTFPacket();
+	outPacket->dataPtr = &allocPtr[0x1C];
+	outPacket->dataSize = dataSize;
+	outPacket->allocSize = totalSize;
+	outPacket->ringBufPtr = allocPtr;
+
+	return outPacket;
+}
+
+void BoltEngine::freePacket(RTFPacket *packet) {
+	// Return allocated size to free space...
+	g_ringBufFreeSpace += packet->allocSize;
+
+	// Calculate distance from packet to buffer end...
+	uint32 distToEnd = g_ringBufSize - (packet->ringBufPtr - g_ringBufBase) - (packet->allocSize);
+
+	// If this was the last packet before the wrap point, reclaim the gap!
+	if (distToEnd == g_ringBufUsed) {
+		g_ringBufFreeSpace += g_ringBufUsed;
+		g_ringBufUsed = 0;
+	}
+
+	// If buffer is completely empty, reset write pointer!
+	if (g_ringBufFreeSpace == g_ringBufSize)
+		g_ringBufWritePtr = g_ringBufBase;
+
+	delete packet;
+}
+
+void BoltEngine::resetPlaybackState() {
+	g_rtfFileHandle = nullptr;
+	g_ringBufBase = nullptr;
+	g_rtfSoundActive = false;
+}
+
+} // End of namespace Bolt
diff --git a/engines/bolt/ssprite.cpp b/engines/bolt/ssprite.cpp
new file mode 100644
index 00000000000..d0e5b38e0bf
--- /dev/null
+++ b/engines/bolt/ssprite.cpp
@@ -0,0 +1,28 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "bolt/bolt.h"
+
+namespace Bolt {
+
+
+
+} // End of namespace Bolt
diff --git a/engines/bolt/state.cpp b/engines/bolt/state.cpp
new file mode 100644
index 00000000000..1ac67080cdb
--- /dev/null
+++ b/engines/bolt/state.cpp
@@ -0,0 +1,179 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "bolt/bolt.h"
+
+namespace Bolt {
+
+bool BoltEngine::initVRam(int16 poolSize) {
+	g_vramRecordCount = 0;
+	g_vramUsedBytes = 0;
+
+	g_allocatedMemPool = (byte *)_xp->allocMem(poolSize);
+	if (!g_allocatedMemPool) {
+		g_curErrorCode = 1;
+		return false;
+	}
+
+	g_allocatedMemPoolSize = poolSize;
+	g_curErrorCode = 0;
+	return true;
+}
+
+void BoltEngine::freeVRam() {
+	if (g_allocatedMemPool) {
+		_xp->freeMem(g_allocatedMemPool);
+		g_allocatedMemPool = 0;
+	}
+}
+
+bool BoltEngine::vLoad(void *dest, const char *name) {
+	char localName[82];
+	int16 recordOffset;
+
+	strcpy_s(localName, name);
+	uint32 nameLen = strlen(name);
+	if (!(nameLen & 1)) {
+		localName[nameLen] = ' ';
+		nameLen++;
+		localName[nameLen] = '\0';
+	}
+
+	if (!findRecord(localName, &recordOffset)) {
+		g_curErrorCode = 3;
+		return false;
+	}
+
+	int16 size = dataSize(recordOffset);
+	byte *addr = dataAddress(recordOffset);
+
+	memcpy(dest, addr, size);
+
+	g_curErrorCode = 0;
+	return true;
+}
+
+bool BoltEngine::vSave(void *src, uint16 srcSize, const char *name) {
+	char localName[82];
+
+	strcpy_s(localName, name);
+	uint32 nameLen = strlen(name);
+	if (!(nameLen & 1)) {
+		localName[nameLen] = ' ';
+		nameLen++;
+		localName[nameLen] = '\0';
+	}
+
+	uint32 nameStorageLen = nameLen + 1;
+	uint32 entrySize = nameStorageLen + srcSize + 4;
+
+	vDelete(localName);
+
+	if (entrySize + g_vramUsedBytes > g_allocatedMemPoolSize) {
+		g_curErrorCode = 4;
+		return 0;
+	}
+
+	byte *writePtr = &g_allocatedMemPool[g_vramUsedBytes];
+
+	WRITE_UINT16(writePtr, (uint16)entrySize);
+	WRITE_UINT16(writePtr + 2, (uint16)nameStorageLen);
+	writePtr += 4;
+
+	memcpy(writePtr, localName, nameStorageLen);
+	writePtr += nameStorageLen;
+
+	memcpy(writePtr, src, srcSize);
+
+	g_vramUsedBytes += entrySize;
+	g_vramRecordCount++;
+	g_curErrorCode = 0;
+	return true;
+}
+
+bool BoltEngine::vDelete(const char *name) {
+	char localName[82];
+	int16 recordOffset;
+
+	strcpy_s(localName, name);
+	uint32 nameLen = strlen(name);
+	if (!(nameLen & 1)) {
+		localName[nameLen] = ' ';
+		nameLen++;
+		localName[nameLen] = '\0';
+	}
+
+	g_curErrorCode = 3;
+
+	if (g_vramRecordCount == 0)
+		return 0;
+
+	if (!findRecord(localName, &recordOffset))
+		return 0;
+
+	int16 entrySize = (int16)READ_UINT16(&g_allocatedMemPool[recordOffset]);
+
+	if (recordOffset + entrySize < g_vramUsedBytes) {
+		memmove(g_allocatedMemPool + recordOffset,
+				g_allocatedMemPool + recordOffset + entrySize,
+				g_vramUsedBytes - (recordOffset + entrySize));
+	}
+
+	g_vramUsedBytes -= entrySize;
+	g_vramRecordCount--;
+	g_curErrorCode = 0;
+	return true;
+}
+
+byte *BoltEngine::dataAddress(int16 recordOffset) {
+	byte *entry = &g_allocatedMemPool[recordOffset];
+
+	int16 nameStorageLen = (int16)READ_UINT16(entry + 2);
+	return &g_allocatedMemPool[recordOffset + 4 + nameStorageLen];
+}
+
+uint16 BoltEngine::dataSize(int16 recordOffset) {
+	byte *entry = g_allocatedMemPool + recordOffset;
+
+	int16 entrySize = (int16)READ_UINT16(entry);
+	int16 nameStorageLen = (int16)READ_UINT16(entry + 2);
+	return entrySize - 4 - nameStorageLen;
+}
+
+bool BoltEngine::findRecord(const char *name, int16 *outOffset) {
+	int16 offset = 0;
+	byte *ptr = g_allocatedMemPool;
+
+	for (int16 i = 0; i < g_vramRecordCount; i++) {
+		char *entryName = (char *)&ptr[4];
+		if (strcmp(name, entryName) == 0) {
+			*outOffset = offset;
+			return true;
+		}
+
+		offset += (int16)READ_UINT16(ptr);
+		ptr = &g_allocatedMemPool[offset];
+	}
+
+	return false;
+}
+
+} // End of namespace Bolt
diff --git a/engines/bolt/swap.cpp b/engines/bolt/swap.cpp
new file mode 100644
index 00000000000..c907ec506d3
--- /dev/null
+++ b/engines/bolt/swap.cpp
@@ -0,0 +1,97 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "bolt/bolt.h"
+
+namespace Bolt {
+
+void BoltEngine::swapPicHeader() {
+	byte *data = g_boltCurrentMemberEntry->dataPtr;
+	if (!data)
+		return;
+
+	WRITE_UINT16(data + 0x02, READ_BE_INT16(data + 0x02));
+	WRITE_UINT16(data + 0x04, READ_BE_INT16(data + 0x04));
+}
+
+void BoltEngine::swapAndResolvePicDesc() {
+	byte *data = g_boltCurrentMemberEntry->dataPtr;
+	if (!data)
+		return;
+
+	for (int16 i = 0; i < 4; i++) {
+		resolveIt((uint32 *)(data + 8 + i * 4));
+		WRITE_UINT16(data + i * 2, READ_BE_INT16(data + i * 2));
+	}
+}
+
+void BoltEngine::swapFirstWord() {
+	byte *data = g_boltCurrentMemberEntry->dataPtr;
+	if (!data)
+		return;
+
+	WRITE_UINT16(data, READ_BE_INT16(data));
+}
+
+void BoltEngine::swapFirstTwoWords() {
+	byte *data = g_boltCurrentMemberEntry->dataPtr;
+	if (!data)
+		return;
+
+	WRITE_UINT16(data, READ_BE_INT16(data));
+	WRITE_UINT16(data + 2, READ_BE_INT16(data + 2));
+}
+
+void BoltEngine::swapFirstFourWords() {
+	byte *data = g_boltCurrentMemberEntry->dataPtr;
+	if (!data)
+		return;
+
+	WRITE_UINT16(data + 0x00, READ_BE_INT16(data + 0x00));
+	WRITE_UINT16(data + 0x02, READ_BE_INT16(data + 0x02));
+	WRITE_UINT16(data + 0x04, READ_BE_INT16(data + 0x04));
+	WRITE_UINT16(data + 0x06, READ_BE_INT16(data + 0x06));
+}
+
+void BoltEngine::swapSpriteHeader() {
+	byte *data = g_boltCurrentMemberEntry->dataPtr;
+	if (!data)
+		return;
+
+	WRITE_UINT16(data + 0x00, READ_BE_INT16(data + 0x00));
+	WRITE_UINT16(data + 0x06, READ_BE_INT16(data + 0x06));
+	WRITE_UINT16(data + 0x08, READ_BE_INT16(data + 0x08));
+	WRITE_UINT16(data + 0x0A, READ_BE_INT16(data + 0x0A));
+	WRITE_UINT16(data + 0x0C, READ_BE_INT16(data + 0x0C));
+	WRITE_UINT16(data + 0x16, READ_BE_INT16(data + 0x16));
+
+	if (!(data[0] & 0x10)) {
+		uint32 idx = g_resolvedPtrs.size();
+		g_resolvedPtrs.push_back(data + 0x18);
+		WRITE_UINT32(data + 0x12, idx | 0x80000000);
+	}
+}
+
+void BoltEngine::freeSpriteCleanUp() {
+	// No-op
+}
+
+} // End of namespace Bolt
diff --git a/engines/bolt/utils.cpp b/engines/bolt/utils.cpp
new file mode 100644
index 00000000000..67df914881a
--- /dev/null
+++ b/engines/bolt/utils.cpp
@@ -0,0 +1,156 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "bolt/bolt.h"
+
+#include "common/config-manager.h"
+
+namespace Bolt {
+
+void BoltEngine::displayColors(byte *palette, int16 page, int16 flags) {
+	byte localPalette[384];
+	int16 startIndex = 0, endIndex = 0;
+
+	if (!palette)
+		return;
+
+	if (flags == 0) {
+		// Indices 0-127
+		startIndex = (int16)READ_UINT16(palette + 2);
+		endIndex = MIN<int16>((int16)READ_UINT16(palette + 4), 127);
+	} else if (flags == 1) {
+		// Indices 128-255
+		startIndex = MAX<int16>((int16)READ_UINT16(palette + 2), 128);
+		endIndex = (int16)READ_UINT16(palette + 4);
+	}
+
+	// Copy RGB triplets from palette resource...
+	byte *src = palette + startIndex * 3 + 6;
+	byte *dst = localPalette;
+
+	for (int16 i = startIndex; i <= endIndex; i++) {
+		*dst++ = *src++;
+		*dst++ = *src++;
+		*dst++ = *src++;
+	}
+
+	XPPicDesc picDesc;
+
+	picDesc.pixelData = nullptr;
+	picDesc.paletteStart = (flags != 0) ? startIndex - 128 : startIndex;
+	picDesc.paletteCount = endIndex - startIndex + 1;
+	picDesc.palette = localPalette;
+
+	_xp->displayPic(&picDesc, 0, 0, page);
+}
+
+void BoltEngine::sub_11035() {
+}
+
+void BoltEngine::boltPict2Pict(XPPicDesc *dest, byte *boltSprite) {
+	dest->pixelData = getResolvedPtr(boltSprite, 0x12);
+	dest->width = (int16)READ_UINT16(boltSprite + 0x0A);
+	dest->height = (int16)READ_UINT16(boltSprite + 0x0C);
+	dest->palette = nullptr;
+	dest->paletteStart = 0;
+	dest->paletteCount = 0;
+	dest->flags = 0;
+
+	if (boltSprite[1] & 0x01)
+		dest->flags |= 1;
+
+	if (boltSprite[0] & 0x02)
+		dest->flags |= 2;
+}
+
+void BoltEngine::displayPic(byte *boltSprite, int16 xOff, int16 yOff, int16 page) {
+	XPPicDesc localDesc;
+
+	if (!boltSprite)
+		return;
+
+	boltPict2Pict(&localDesc, boltSprite);
+
+	_xp->displayPic(&localDesc,
+		(int16)READ_UINT16(boltSprite + 0x06) + xOff,
+		(int16)READ_UINT16(boltSprite + 0x08) + yOff,
+		page);
+}
+
+void BoltEngine::sub_11131() {
+}
+
+const char *BoltEngine::assetPath(const char *fileName) {
+	if (Common::File::exists(fileName)) {
+		return fileName;
+	} else if (Common::File::exists(Common::Path(Common::String::format("ASSETS/%s", fileName)))) {
+		return Common::Path(Common::String::format("ASSETS/%s", fileName)).toString().c_str();
+	} else {
+		error("BoltEngine::assetPath(): Invalid game path");
+	}
+}
+
+void BoltEngine::boltCycleToXPCycle(byte *srcData, XPCycleState *cycleDesc) {
+	int16 cycleIdx = 0;
+
+	for (int16 i = 0; i < 4 && cycleIdx < 4; i++) {
+		int16 type = (int16)READ_UINT16(srcData + i * 2);
+		if (type != 1)
+			continue;
+
+		byte *cycleData = getResolvedPtr(srcData, 8 + i * 4);
+		if (!cycleData)
+			continue;
+
+		if (cycleData[5] == 0) {
+			cycleDesc[cycleIdx].startIndex = (int16)READ_UINT16(cycleData);
+			cycleDesc[cycleIdx].endIndex = (int16)READ_UINT16(cycleData + 2);
+		} else {
+			cycleDesc[cycleIdx].startIndex = (int16)READ_UINT16(cycleData + 2);
+			cycleDesc[cycleIdx].endIndex = (int16)READ_UINT16(cycleData);
+		}
+
+		cycleDesc[cycleIdx].delay = (int16)(cycleData[4] * (int32)1000 / 60);
+		cycleIdx++;
+	}
+
+	while (cycleIdx < 4) {
+		cycleDesc[cycleIdx].startIndex = 0;
+		cycleDesc[cycleIdx].endIndex = 0;
+		cycleDesc[cycleIdx].delay = 0;
+		cycleIdx++;
+	}
+}
+
+void BoltEngine::unpackColors(int16 count, byte *packedColors) {
+	byte *src = packedColors;
+	byte *dst = packedColors;
+
+	while (count-- > 0) {
+		dst[0] = src[1];
+		dst[1] = src[2];
+		dst[2] = src[3];
+		dst += 3;
+		src += 4;
+	}
+}
+
+} // End of namespace Bolt
diff --git a/engines/bolt/xplib/blit.cpp b/engines/bolt/xplib/blit.cpp
index d7aae972728..e8ae7c92a35 100644
--- a/engines/bolt/xplib/blit.cpp
+++ b/engines/bolt/xplib/blit.cpp
@@ -50,6 +50,8 @@ void XpLib::dirtyBlit(byte *src, byte *dst, uint16 width, uint16 height, byte *d
 		dirtyFlags[row] = (flag == 2) ? 3 : 0;
 
 		memcpy(dst, src, width);
+		src += width;
+		dst += width;
 	}
 }
 
@@ -173,8 +175,7 @@ void XpLib::rleCompositeBlit(byte *rle, byte *background, byte *dst, uint16 widt
 		// Check if row is all transparent...
 		if (rlePtr[0] == 0x80 && rlePtr[1] == 0x00) {
 			if (dirtyFlags[row] & 2) {
-				// Row was already transparent: mark clean and skip...
-				dirtyFlags[row] = 3;
+				// Row was already transparent: skip
 				rlePtr += 2;
 				dstPtr = rowEnd;
 				bgPtr += width;
diff --git a/engines/bolt/xplib/cursor.cpp b/engines/bolt/xplib/cursor.cpp
index cf1f53a3dcb..cf1fef61d97 100644
--- a/engines/bolt/xplib/cursor.cpp
+++ b/engines/bolt/xplib/cursor.cpp
@@ -36,10 +36,9 @@ bool XpLib::initCursor() {
 	}
 
 	g_cursorSprite.pixelData = g_cursorBuffer;
-
-	g_cursorWidth = 16;
-	g_cursorHeight = 16;
-	g_cursorScale = 2;
+	g_cursorSprite.width = 16;
+	g_cursorSprite.height = 16;
+	g_cursorSprite.flags = 2;
 
 	return true;
 }
@@ -48,7 +47,7 @@ void XpLib::shutdownCursor() {
 }
 
 bool XpLib::readCursor(uint16 *outButtons, int16 *outX, int16 *outY) {
-	if (g_cursorHidden == 0)
+	if (g_cursorHidden != 0)
 		return false;
 
 	*outX = g_lastCursorX;
diff --git a/engines/bolt/xplib/display.cpp b/engines/bolt/xplib/display.cpp
index 27b2ee82b16..78ef3391987 100644
--- a/engines/bolt/xplib/display.cpp
+++ b/engines/bolt/xplib/display.cpp
@@ -25,22 +25,22 @@
 namespace Bolt {
 
 bool XpLib::initDisplay() {
-	g_virtualWidth = 320;
-	g_virtualHeight = 200;
-	g_currentDisplayPage = 0;
+	g_virtualWidth = (_bolt->g_extendedViewport) ? EXTENDED_SCREEN_WIDTH : SCREEN_WIDTH;
+	g_virtualHeight = (_bolt->g_extendedViewport) ? EXTENDED_SCREEN_HEIGHT : SCREEN_HEIGHT;
+	g_currentDisplayPage = stFront;
 
 	// Create front and back display surfaces
-	if (!createSurface(&g_surfaces[0]))
+	if (!createSurface(&g_surfaces[stFront]))
 		return false;
 
-	if (!createSurface(&g_surfaces[1]))
+	if (!createSurface(&g_surfaces[stBack]))
 		return false;
 
-	fillDisplay(0, 0);
-	fillDisplay(1, 0);
+	fillDisplay(0, stFront);
+	fillDisplay(0, stBack);
 
-	g_spriteOverlayActive = 0;
-	g_spriteOverlayEnabled = 0;
+	g_prevRenderFlags = 0;
+	g_renderFlags = 0;
 	g_frameRateFPS = 0;
 	g_overlayCount = 0;
 	g_prevDirtyCount = 0;
@@ -54,8 +54,8 @@ bool XpLib::initDisplay() {
 }
 
 void XpLib::shutdownDisplay() {
-	freeSurface(&g_surfaces[0]);
-	freeSurface(&g_surfaces[1]);
+	freeSurface(&g_surfaces[stFront]);
+	freeSurface(&g_surfaces[stBack]);
 	stopCycle();
 }
 
@@ -93,36 +93,29 @@ bool XpLib::createSurface(XPSurface *surf) {
 }
 
 void XpLib::freeSurface(XPSurface *surf) {
-	if (surf->mainPic.palette != nullptr) {
+	if (surf->mainPic.palette) {
 		freeMem(surf->mainPic.palette);
 		surf->mainPic.palette = nullptr;
 	}
 
-	if (surf->mainPic.pixelData != nullptr) {
+	if (surf->mainPic.pixelData) {
 		freeMem(surf->mainPic.pixelData);
 		surf->mainPic.pixelData = nullptr;
 	}
 }
 
-bool XpLib::chooseDisplaySpec(int *outMode, int numSpecs, DisplaySpecs *specs) {
-	for (int16 i = 0; i < numSpecs; i++) {
+bool XpLib::setDisplaySpec(int *outMode, DisplaySpecs *spec) {
+	*outMode = spec->id;
+	g_surfaceWidth = spec->width;
+	g_surfaceHeight = spec->height;
 
-		if (specs[i].width == 320 && specs[i].height == 200) {
-			*outMode = i;
-			g_surfaceWidth = specs[i].width;
-			g_surfaceHeight = specs[i].height;
-
-			if (!createSurface(&g_surfaces[0]))
-				return false;
-
-			if (!createSurface(&g_surfaces[1]))
-				return false;
+	if (!createSurface(&g_surfaces[stFront]))
+		return false;
 
-			return true;
-		}
-	}
+	if (!createSurface(&g_surfaces[stBack]))
+		return false;
 
-	return false;
+	return true;
 }
 
 void XpLib::setCoordSpec(int16 x, int16 y, int16 width, int16 height) {
@@ -152,33 +145,40 @@ void XpLib::displayPic(XPPicDesc *pic, int16 x, int16 y, int16 page) {
 		bool isFullScreen = (pic->width == g_surfaceWidth &&  pic->height == g_surfaceHeight);
 
 		if (isFullScreen)
-			g_spriteOverlayEnabled |= 4; // Mark full-screen overwrite
+			g_renderFlags |= RF_FULL_REDRAW; // Mark full-screen overwrite
 
 		// Full-screen transparent non-RLE overlayPic on surface 0: store as overlay source...
-		if (isFullScreen && page == 0 && (pic->flags & 1) && !(pic->flags & 2)) {
-			memcpy(&surf->overlayPic, pic, sizeof(pic));
-			g_spriteOverlayEnabled |= 1;
+		if (isFullScreen && page == stFront && (pic->flags & 1) && !(pic->flags & 2)) {
+			surf->overlayPic.pixelData = pic->pixelData;
+			surf->overlayPic.width = pic->width;
+			surf->overlayPic.height = pic->height;
+			surf->overlayPic.palette = pic->palette;
+			surf->overlayPic.paletteStart = pic->paletteStart;
+			surf->overlayPic.paletteCount = pic->paletteCount;
+			surf->overlayPic.flags = pic->flags;
+
+			g_renderFlags |= RF_OVERLAY_ACTIVE;
 		} else {
 			// Clear previous overlay if needed...
-			if ((g_spriteOverlayEnabled & 1) && page == 0) {
+			if ((g_renderFlags & RF_OVERLAY_ACTIVE) && page == stFront) {
 				if (!isFullScreen)
-					clipAndBlit(&surf->overlayPic, surf, 0, 0, nullptr);
+					clipAndBlit(&surf->overlayPic, &surf->mainPic, 0, 0, nullptr);
 
-				g_spriteOverlayEnabled &= ~1;
+				g_renderFlags &= ~RF_OVERLAY_ACTIVE;
 			}
 
 			// Clip and blit to surface...
 			Common::Rect clipResult;
-			if (clipAndBlit(pic, surf, x, y, &clipResult))
+			if (clipAndBlit(pic, &surf->mainPic, x, y, &clipResult))
 				addDirtyRect(&clipResult);
 		}
 
 		// Mark page as dirty...
-		g_spriteOverlayEnabled |= (page != 0) ? 0x10 : 0x08;
+		g_renderFlags |= (page != stFront) ? RF_BACK_DIRTY : RF_FRONT_DIRTY;
 	}
 
 	// --- Palette data ---
-	if (pic->palette != nullptr) {
+	if (pic->palette) {
 		int16 palStart = pic->paletteStart;
 		int16 palEnd = palStart + pic->paletteCount - 1;
 		int16 adjStart = palStart;
@@ -206,9 +206,9 @@ void XpLib::displayPic(XPPicDesc *pic, int16 x, int16 y, int16 page) {
 			memcpy(surf->mainPic.palette + destOffset, palData, adjCount * 3);
 		}
 
-		int16 dirtyFlag = (page != 0) ? 0x40 : 0x20;
+		int16 dirtyFlag = (page != stFront) ? RF_BACK_PAL_DIRTY : RF_FRONT_PAL_DIRTY;
 
-		if (g_spriteOverlayEnabled & dirtyFlag) {
+		if (g_renderFlags & dirtyFlag) {
 			// Merge dirty range...
 			if (surf->dirtyPalStart > adjStart)
 				surf->dirtyPalStart = adjStart;
@@ -219,7 +219,7 @@ void XpLib::displayPic(XPPicDesc *pic, int16 x, int16 y, int16 page) {
 			// First palette update, set range...
 			surf->dirtyPalStart = adjStart;
 			surf->dirtyPalEnd = palEnd;
-			g_spriteOverlayEnabled |= dirtyFlag;
+			g_renderFlags |= dirtyFlag;
 		}
 	}
 }
@@ -241,7 +241,7 @@ void XpLib::dispatchBlit(int16 mode, byte *src, uint16 srcStride, byte *dst, uin
 	}
 }
 
-bool XpLib::clipAndBlit(XPPicDesc *src, XPSurface *dest, int16 x, int16 y, Common::Rect *outClip) {
+bool XpLib::clipAndBlit(XPPicDesc *src, XPPicDesc *dest, int16 x, int16 y, Common::Rect *outClip) {
 	int16 blitMode = 0;
 
 	if (src->flags & 1)
@@ -251,7 +251,7 @@ bool XpLib::clipAndBlit(XPPicDesc *src, XPSurface *dest, int16 x, int16 y, Commo
 		blitMode |= 1; // RLE
 
 	Common::Rect srcRect(x, y, x + src->width, y + src->height);
-	Common::Rect destRect(0, 0, dest->mainPic.width, dest->mainPic.height);
+	Common::Rect destRect(0, 0, dest->width, dest->height);
 
 	Common::Rect clipped;
 	clipped = srcRect.findIntersectingRect(destRect);
@@ -260,7 +260,7 @@ bool XpLib::clipAndBlit(XPPicDesc *src, XPSurface *dest, int16 x, int16 y, Commo
 
 	// For transparent blits, check if content actually changed...
 	if (src->flags & 1) {
-		if (memcmp(&clipped, &srcRect, 8) == 0)
+		if (!clipped.equals(srcRect))
 			return false;
 	}
 
@@ -271,14 +271,14 @@ bool XpLib::clipAndBlit(XPPicDesc *src, XPSurface *dest, int16 x, int16 y, Commo
 		outClip->bottom = clipped.bottom;
 	}
 
-	int16 srcOff = (clipped.top - srcRect.top) * src->width + (clipped.left - srcRect.left);
-	int16 destOff = clipped.top * dest->mainPic.width + clipped.left;
-	int16 clipW = clipped.right - clipped.left;
-	int16 clipH = clipped.bottom - clipped.top;
+	int32 srcOff = (clipped.top - srcRect.top) * src->width + (clipped.left - srcRect.left);
+	int32 destOff = clipped.top * dest->width + clipped.left;
+	int32 clipW = clipped.right - clipped.left;
+	int32 clipH = clipped.bottom - clipped.top;
 
 	dispatchBlit(blitMode,
 		src->pixelData + srcOff, src->width,
-		dest->mainPic.pixelData + destOff, dest->mainPic.width,
+		dest->pixelData + destOff, dest->width,
 		clipW, clipH
 	);
 
@@ -286,11 +286,11 @@ bool XpLib::clipAndBlit(XPPicDesc *src, XPSurface *dest, int16 x, int16 y, Commo
 }
 
 void XpLib::addDirtyRect(Common::Rect *rect) {
-	if (g_spriteOverlayEnabled & 4)
+	if (g_renderFlags & RF_FULL_REDRAW)
 		return; // Full-screen overwrite, no need to track...
 
 	if (g_overlayCount >= 30) {
-		g_spriteOverlayEnabled |= 4; // Too many rects, mark full dirty...
+		g_renderFlags |= RF_FULL_REDRAW; // Too many rects, mark full dirty...
 		return;
 	}
 
@@ -309,33 +309,37 @@ void XpLib::setFrameRate(int16 fps) {
 
 void XpLib::updateDisplay() {
 	if (g_cursorHidden == 0)
-		g_spriteOverlayEnabled |= 0x80;
+		g_renderFlags |= RF_CURSOR_VISIBLE;
 	else
-		g_spriteOverlayEnabled &= ~0x80;
+		g_renderFlags &= ~RF_CURSOR_VISIBLE;
 
-	// This will call delayMillis (but not pollEvent, since that has to be handled elsewhere)...
+	// This will call delayMillis (but must not call pollEvent, otherwise events get desynced)...
 	waitForFrameRate();
 
 	handlePaletteTransitions();
 
 	// If back surface pixels changed, mark all with cursor bit and reset row flags...
-	if (g_spriteOverlayEnabled & 0x10) {
-		markCursorPixels(g_surfaces[1].mainPic.pixelData, (uint32)g_surfaces[1].mainPic.width * (uint32)g_surfaces[1].mainPic.height);
-		memset(&g_rowDirtyFlags, 0, 200);
+	if (g_renderFlags & RF_BACK_DIRTY) {
+		markCursorPixels(g_surfaces[stBack].mainPic.pixelData, (uint32)g_surfaces[stBack].mainPic.width * (uint32)g_surfaces[stBack].mainPic.height);
+		if (_bolt->g_extendedViewport) {
+			memset(g_rowDirtyFlags, 0, EXTENDED_SCREEN_HEIGHT);
+		} else {
+			memset(g_rowDirtyFlags, 0, SCREEN_HEIGHT);
+		}
 	}
 
 	// Render to screen!
-	if (g_spriteOverlayEnabled & 1) {
+	if (g_renderFlags & RF_OVERLAY_ACTIVE) {
 		overlayComposite(); // RLE overlay path
 	} else {
 		compositeToScreen(); // Standard dirty-rect path
 	}
 
 	// Save current state, clear per-frame flags...
-	g_spriteOverlayActive = g_spriteOverlayEnabled;
-	g_spriteOverlayEnabled &= 0x03; // Keep only overlay + double-buffer bits...
+	g_prevRenderFlags = g_renderFlags;
+	g_renderFlags &= (RF_OVERLAY_ACTIVE | RF_DOUBLE_BUFFER); // Keep only overlay + double-buffer bits...
 
-	_bolt->_system->copyRectToScreen(g_vgaFramebuffer, 320, 0, 0, 320, 200);
+	_bolt->_system->copyRectToScreen(g_vgaFramebuffer, g_virtualWidth, 0, 0, g_virtualWidth, g_virtualHeight);
 	_bolt->_system->updateScreen();
 }
 
@@ -352,15 +356,15 @@ void XpLib::waitForFrameRate() {
 
 void XpLib::handlePaletteTransitions() {
 	// Only act if palette dirty flags are set alongside pixel dirty flags...
-	if (!((g_spriteOverlayEnabled & 0x20) && (g_spriteOverlayEnabled & 0x08)) &&
-		!((g_spriteOverlayEnabled & 0x40) && (g_spriteOverlayEnabled & 0x10)))
+	if (!((g_renderFlags & RF_FRONT_PAL_DIRTY) && (g_renderFlags & RF_FRONT_DIRTY)) &&
+		!((g_renderFlags & RF_BACK_PAL_DIRTY)  && (g_renderFlags & RF_BACK_DIRTY)))
 		return;
 
 	// Build transition code: bit 0 = entering double-buffer, bit 1 = was double-buffer
 	int16 transition = 0;
-	if (g_spriteOverlayEnabled & 2)
+	if (g_renderFlags & RF_DOUBLE_BUFFER)
 		transition |= 1;
-	if (g_spriteOverlayActive & 2)
+	if (g_prevRenderFlags & RF_DOUBLE_BUFFER)
 		transition |= 2;
 
 	switch (transition) {
@@ -371,11 +375,11 @@ void XpLib::handlePaletteTransitions() {
 
 	case 1: {
 		// Single -> Double
-		if (!(g_spriteOverlayEnabled & 0x40))
+		if (!(g_renderFlags & RF_BACK_PAL_DIRTY))
 			break;
 
 		// Check if back surface palette actually differs from VGA palette...
-		XPSurface *back = &g_surfaces[1];
+		XPSurface *back = &g_surfaces[stBack];
 		int16 count = back->dirtyPalEnd - back->dirtyPalStart + 1;
 		int16 surfOffset = (back->dirtyPalStart - back->mainPic.paletteStart) * 3;
 
@@ -390,11 +394,11 @@ void XpLib::handlePaletteTransitions() {
 	
 	case 2: {
 		// Double -> Single
-		if (!(g_spriteOverlayEnabled & 0x20))
+		if (!(g_renderFlags & RF_FRONT_PAL_DIRTY))
 			break;
 
 		// Check if front surface palette differs from cursor palette page...
-		XPSurface *front = &g_surfaces[0];
+		XPSurface *front = &g_surfaces[stFront];
 		int16 count = front->dirtyPalEnd - front->dirtyPalStart + 1;
 		int16 surfOffset = (front->dirtyPalStart - front->mainPic.paletteStart) * 3;
 
@@ -415,8 +419,8 @@ void XpLib::handlePaletteTransitions() {
 
 void XpLib::flushPalette() {
 	// Front surface palette -> VGA indices 1-127
-	if (g_spriteOverlayEnabled & 0x20) {
-		XPSurface *front = &g_surfaces[0];
+	if (g_renderFlags & RF_FRONT_PAL_DIRTY) {
+		XPSurface *front = &g_surfaces[stFront];
 		int16 start = front->dirtyPalStart;
 		int16 count = front->dirtyPalEnd - start + 1;
 		int16 offset = (start - front->mainPic.paletteStart) * 3;
@@ -424,8 +428,8 @@ void XpLib::flushPalette() {
 	}
 
 	// Back surface palette -> VGA indices 129-255 (start + 128)
-	if (g_spriteOverlayEnabled & 0x40) {
-		XPSurface *back = &g_surfaces[1];
+	if (g_renderFlags & RF_BACK_PAL_DIRTY) {
+		XPSurface *back = &g_surfaces[stBack];
 		int16 start = back->dirtyPalStart;
 		int16 count = back->dirtyPalEnd - start + 1;
 		int16 offset = (start - back->mainPic.paletteStart) * 3;
@@ -434,9 +438,9 @@ void XpLib::flushPalette() {
 }
 
 void XpLib::overlayComposite() {
-	bool pixelsDirty = (g_spriteOverlayEnabled & 0x18) != 0;
-	bool cursorNow = (g_spriteOverlayEnabled & 0x80) != 0;
-	bool cursorPrev = (g_spriteOverlayActive & 0x80) != 0;
+	bool pixelsDirty = (g_renderFlags & (RF_FRONT_DIRTY | RF_BACK_DIRTY)) != 0;
+	bool cursorNow = (g_renderFlags & RF_CURSOR_VISIBLE) != 0;
+	bool cursorPrev = (g_prevRenderFlags & RF_CURSOR_VISIBLE) != 0;
 
 	// Nothing changed at all, just flush the palette...
 	if (!pixelsDirty && !cursorNow && !cursorPrev) {
@@ -448,21 +452,21 @@ void XpLib::overlayComposite() {
 
 	// Decode RLE overlay onto front surface...
 	if (pixelsDirty) {
-		if (g_spriteOverlayEnabled & 2) {
+		if (g_renderFlags & RF_DOUBLE_BUFFER) {
 			// Double-buffer: RLE composite (transparent pixels from back surface)
 			rleCompositeBlit(
-				g_surfaces[0].overlayPic.pixelData,
-				g_surfaces[1].mainPic.pixelData,
-				g_surfaces[0].mainPic.pixelData,
+				g_surfaces[stFront].overlayPic.pixelData,
+				g_surfaces[stBack].mainPic.pixelData,
+				g_surfaces[stFront].mainPic.pixelData,
 				g_virtualWidth, g_virtualHeight,
 				g_rowDirtyFlags);
 		} else {
 			// Single-buffer: straight RLE decode to front
 			rleBlit(
-				g_surfaces[0].overlayPic.pixelData,
-				g_surfaces[0].overlayPic.width,
-				g_surfaces[0].mainPic.pixelData,
-				g_surfaces[0].mainPic.width,
+				g_surfaces[stFront].overlayPic.pixelData,
+				g_surfaces[stFront].overlayPic.width,
+				g_surfaces[stFront].mainPic.pixelData,
+				g_surfaces[stFront].mainPic.width,
 				g_virtualWidth, g_virtualHeight);
 		}
 	}
@@ -473,43 +477,42 @@ void XpLib::overlayComposite() {
 		cursorY = g_lastCursorY - g_cursorHotspotY;
 
 		// Save background under cursor...
-		clipAndBlit(&g_cursorBackgroundSave, &g_surfaces[0],
+		clipAndBlit(&g_surfaces[stFront].mainPic, &g_cursorBackgroundSave,
 					  -cursorX, -cursorY, nullptr);
 
 		// Draw cursor sprite...
-		clipAndBlit(&g_cursorBackgroundSave, &g_surfaces[0],
-					  cursorX, cursorY, &g_cursorClipRect2);
+		clipAndBlit(&g_cursorSprite, &g_surfaces[stFront].mainPic,
+					  cursorX, cursorY, &g_overlayCursorRect);
 	}
 
 	flushPalette();
 
 	// Transfer front surface to main framebuffer...
 	if (pixelsDirty) {
-		if (g_spriteOverlayEnabled & 2) {
+		if (g_renderFlags & RF_DOUBLE_BUFFER) {
 			// Tracked blit: only copy rows that changed...
 			dirtyBlit(
-				g_surfaces[0].mainPic.pixelData,
+				g_surfaces[stFront].mainPic.pixelData,
 				g_vgaFramebuffer,
 				g_virtualWidth, g_virtualHeight,
 				g_rowDirtyFlags);
 		} else {
-			memcpy(g_vgaFramebuffer, g_surfaces[0].mainPic.pixelData,
+			memcpy(g_vgaFramebuffer, g_surfaces[stFront].mainPic.pixelData,
 				   (uint32)g_virtualWidth * (uint32)g_virtualHeight);
 		}
 	} else {
 		if (cursorNow)
-			compositeDirtyRects(&g_cursorClipRect2, 1);
+			compositeDirtyRects(&g_overlayCursorRect, 1);
 
 		if (cursorPrev)
-			compositeDirtyRects(&g_prevCursorRect2, 1);
+			compositeDirtyRects(&g_prevOverlayCursorRect, 1);
 	}
 
 	// Save cursor rect and restore front surface
 	if (cursorNow) {
-		g_prevCursorRect2 = g_cursorClipRect2;
+		g_prevOverlayCursorRect = g_overlayCursorRect;
 
-		clipAndBlit(&g_cursorBackgroundSave, &g_surfaces[0],
-					  cursorX, cursorY, nullptr);
+		clipAndBlit(&g_cursorBackgroundSave, &g_surfaces[stFront].mainPic, cursorX, cursorY, nullptr);
 	}
 }
 
@@ -517,26 +520,26 @@ void XpLib::compositeToScreen() {
 	int16 cursorX = 0, cursorY = 0;
 
 	// Draw cursor onto front surface...
-	if (g_spriteOverlayEnabled & 0x80) {
+	if (g_renderFlags & RF_CURSOR_VISIBLE) {
 		cursorX = g_lastCursorX - g_cursorHotspotX;
 		cursorY = g_lastCursorY - g_cursorHotspotY;
 
 		// Save background under cursor position...
-		clipAndBlit(&g_cursorBackgroundSave, &g_surfaces[0], -cursorX, -cursorY, nullptr);
+		clipAndBlit(&g_cursorBackgroundSave, &g_surfaces[stFront].mainPic, -cursorX, -cursorY, nullptr);
 
 		// Draw cursor sprite at position...
-		clipAndBlit(&g_cursorSprite, &g_surfaces[0], cursorX, cursorY, &g_cursorDirtyRect);
+		clipAndBlit(&g_cursorSprite, &g_surfaces[stFront].mainPic, cursorX, cursorY, &g_cursorRect);
 	}
 
 	flushPalette();
 
-	if (!(g_spriteOverlayEnabled & 2)) {
+	if (!(g_renderFlags & RF_DOUBLE_BUFFER)) {
 		// SINGLE-BUFFER MODE
 
-		if (g_spriteOverlayEnabled & 0x08) {
-			if (g_spriteOverlayEnabled & 4) {
+		if (g_renderFlags & RF_FRONT_DIRTY) {
+			if (g_renderFlags & RF_FULL_REDRAW) {
 				// Full-screen dirty, blit entire front surface to framebuffer...
-				blit(g_surfaces[0].mainPic.pixelData, g_surfaces[0].mainPic.width,
+				blit(g_surfaces[stFront].mainPic.pixelData, g_surfaces[stFront].mainPic.width,
 					   g_vgaFramebuffer, g_virtualWidth,
 					   g_virtualWidth, g_virtualHeight);
 			} else {
@@ -548,29 +551,29 @@ void XpLib::compositeToScreen() {
 
 				blitDirtyRects(g_dirtyRects, g_overlayCount);
 
-				if (g_spriteOverlayEnabled & 0x80)
-					blitDirtyRects(&g_cursorDirtyRect, 1);
+				if (g_renderFlags & RF_CURSOR_VISIBLE)
+					blitDirtyRects(&g_cursorRect, 1);
 
-				if (g_spriteOverlayActive & 0x80)
+				if (g_prevRenderFlags & RF_CURSOR_VISIBLE)
 					blitDirtyRects(&g_prevCursorRect, 1);
 			}
 		} else {
 			// No pixel changes - just cursor
-			if (g_spriteOverlayEnabled & 0x80)
-				blitDirtyRects(&g_cursorDirtyRect, 1);
+			if (g_renderFlags & RF_CURSOR_VISIBLE)
+				blitDirtyRects(&g_cursorRect, 1);
 
-			if (g_spriteOverlayActive & 0x80)
+			if (g_prevRenderFlags & RF_CURSOR_VISIBLE)
 				blitDirtyRects(&g_prevCursorRect, 1);
 		}
 
 	} else {
 		// DOUBLE-BUFFER MODE
 
-		if (g_spriteOverlayEnabled & 0x18) {
-			if ((g_spriteOverlayEnabled & 4) ||
+		if (g_renderFlags & (RF_FRONT_DIRTY | RF_BACK_DIRTY)) {
+			if ((g_renderFlags & RF_FULL_REDRAW) ||
 				g_prevDirtyValid > 0 || g_overlayCount > 0) {
 				// Full composite...
-				compositeBlit(g_surfaces[0].mainPic.pixelData, g_surfaces[1].mainPic.pixelData,
+				compositeBlit(g_surfaces[stFront].mainPic.pixelData, g_surfaces[stBack].mainPic.pixelData,
 							  g_vgaFramebuffer, g_virtualWidth, g_virtualWidth, g_virtualHeight);
 				g_overlayCount = 0;
 				g_prevDirtyCount = 0;
@@ -583,17 +586,17 @@ void XpLib::compositeToScreen() {
 
 				compositeDirtyRects(g_dirtyRects, g_overlayCount);
 
-				if (g_spriteOverlayEnabled & 0x80)
-					compositeDirtyRects(&g_cursorDirtyRect, 1);
+				if (g_renderFlags & RF_CURSOR_VISIBLE)
+					compositeDirtyRects(&g_cursorRect, 1);
 
-				if (g_spriteOverlayActive & 0x80)
+				if (g_prevRenderFlags & RF_CURSOR_VISIBLE)
 					compositeDirtyRects(&g_prevCursorRect, 1);
 			}
 		} else {
-			if (g_spriteOverlayEnabled & 0x80)
-				compositeDirtyRects(&g_cursorDirtyRect, 1);
+			if (g_renderFlags & RF_CURSOR_VISIBLE)
+				compositeDirtyRects(&g_cursorRect, 1);
 
-			if (g_spriteOverlayActive & 0x80)
+			if (g_prevRenderFlags & RF_CURSOR_VISIBLE)
 				compositeDirtyRects(&g_prevCursorRect, 1);
 		}
 	}
@@ -613,11 +616,11 @@ void XpLib::compositeToScreen() {
 	g_overlayCount = 0;
 
 	// Save cursor state for next frame...
-	if (g_spriteOverlayEnabled & 0x80) {
-		g_prevCursorRect = g_cursorDirtyRect;
+	if (g_renderFlags & RF_CURSOR_VISIBLE) {
+		g_prevCursorRect = g_cursorRect;
 
 		// Restore front surface under cursor...
-		clipAndBlit(&g_cursorBackgroundSave, &g_surfaces[0], cursorX, cursorY, nullptr);
+		clipAndBlit(&g_cursorBackgroundSave, &g_surfaces[stFront].mainPic, cursorX, cursorY, nullptr);
 	}
 }
 
@@ -648,11 +651,11 @@ void XpLib::blitDirtyRects(Common::Rect *rects, int16 count) {
 		if (rect->right < 0) {
 			rect->right = -rect->right;
 		} else {
-			int16 offset = rect->top * g_virtualWidth + rect->left;
-			int16 w = rect->right - rect->left;
-			int16 h = rect->bottom - rect->top;
+			uint32 offset = rect->top * g_virtualWidth + rect->left;
+			int32 w = rect->right - rect->left;
+			int32 h = rect->bottom - rect->top;
 
-			blit(g_surfaces[0].mainPic.pixelData + offset, g_surfaces[0].mainPic.width,
+			blit(g_surfaces[stFront].mainPic.pixelData + offset, g_surfaces[stFront].mainPic.width,
 				 g_vgaFramebuffer + offset, g_virtualWidth,
 				 w, h);
 		}
@@ -666,13 +669,13 @@ void XpLib::compositeDirtyRects(Common::Rect *rects, int16 count) {
 		if (rect->right < 0) {
 			rect->right = -rect->right;
 		} else {
-			int16 offset = rect->top * g_virtualWidth + rect->left;
-			int16 w = rect->right - rect->left;
-			int16 h = rect->bottom - rect->top;
+			uint32 offset = rect->top * g_virtualWidth + rect->left;
+			int32 w = rect->right - rect->left;
+			int32 h = rect->bottom - rect->top;
 
 			compositeBlit(
-				g_surfaces[0].mainPic.pixelData + offset,
-				g_surfaces[1].mainPic.pixelData + offset,
+				g_surfaces[stFront].mainPic.pixelData + offset,
+				g_surfaces[stBack].mainPic.pixelData + offset,
 				g_vgaFramebuffer + offset,
 				g_virtualWidth, w, h);
 		}
@@ -686,42 +689,41 @@ void XpLib::applyCursorPalette() {
 
 void XpLib::prepareBackSurface() {
 	XPPicDesc *src;
-	if (g_spriteOverlayEnabled & 1) {
-		src = &g_surfaces[0].overlayPic;
+	if (g_renderFlags & RF_OVERLAY_ACTIVE) {
+		src = &g_surfaces[stFront].overlayPic;
 	} else {
-		src = &g_surfaces[0].mainPic;
+		src = &g_surfaces[stFront].mainPic;
 	}
 
-	clipAndBlit(src, &g_surfaces[1], 0, 0, nullptr);
+	clipAndBlit(src, &g_surfaces[stBack].mainPic, 0, 0, nullptr);
 
-	markCursorPixels(g_surfaces[1].mainPic.pixelData, (uint32)g_surfaces[1].mainPic.width * (uint32)g_surfaces[1].mainPic.height);
+	markCursorPixels(g_surfaces[stBack].mainPic.pixelData, (uint32)g_surfaces[stBack].mainPic.width * (uint32)g_surfaces[stBack].mainPic.height);
 
-	memcpy(g_vgaFramebuffer, g_surfaces[1].mainPic.pixelData, (uint32)g_surfaces[1].mainPic.width * (uint32)g_surfaces[1].mainPic.height);
+	memcpy(g_vgaFramebuffer, g_surfaces[stBack].mainPic.pixelData, (uint32)g_surfaces[stBack].mainPic.width * (uint32)g_surfaces[stBack].mainPic.height);
 }
 
 void XpLib::setTransparency(bool toggle) {
 	if (toggle) {
-		g_spriteOverlayEnabled |= 2; // Enables double-buffer compositing
+		g_renderFlags |= RF_DOUBLE_BUFFER; // Enables double-buffer compositing
 	} else {
-		g_spriteOverlayEnabled &= ~2;
+		g_renderFlags &= ~RF_DOUBLE_BUFFER;
 	}
 }
 
 void XpLib::fillDisplay(byte color, int16 page) {
-
 	XPSurface *surf = &g_surfaces[page];
 	uint32 size = (uint32)surf->mainPic.width * (uint32)surf->mainPic.height;
 
-	// Back surface (page 1) pixels have bit 7 set for cursor palette mapping...
-	byte fillVal = (page == 1) ? (color + 0x80) : color;
+	// Back surface pixels have bit 7 set for cursor palette mapping...
+	byte fillVal = (page == stBack) ? (color + 0x80) : color;
 
 	memset(surf->mainPic.pixelData, fillVal, size);
 
 	// Mark page as pixel-dirty...
-	g_spriteOverlayEnabled |= (page != 0) ? 0x10 : 0x08;
+	g_renderFlags |= (page != stFront) ? RF_BACK_DIRTY : RF_FRONT_DIRTY;
 
 	// Clear overlay flag, signal full redraw needed...
-	g_spriteOverlayEnabled &= ~1;
+	g_renderFlags &= ~RF_OVERLAY_ACTIVE;
 	g_prevDirtyValid = 1;
 }
 
diff --git a/engines/bolt/xplib/events.cpp b/engines/bolt/xplib/events.cpp
index 4528c50253a..3dc0625d029 100644
--- a/engines/bolt/xplib/events.cpp
+++ b/engines/bolt/xplib/events.cpp
@@ -68,7 +68,7 @@ int16 XpLib::getEvent(int16 filter, uint32 *outData) {
 	}
 
 	if (!node)
-		return 0;
+		return etEmpty;
 
 	// Remove from queue...
 	unlinkEvent(node);
@@ -140,7 +140,7 @@ void XpLib::enqueueEvent(XPEvent *node) {
 
 void XpLib::pumpMessages() {
 	Common::Event event;
-	long timerData;
+	uint32 timerData;
 	bool mouseMoved = false;
 
 	// Drain any pending sound events...
@@ -149,6 +149,8 @@ void XpLib::pumpMessages() {
 		postEvent(etSound, timerData);
 	}
 
+	cycleColors();
+
 	// Check inactivity timeout...
 	if (g_inactivityDeadline != 0) {
 		uint32 now = _bolt->_system->getMillis();
@@ -350,7 +352,7 @@ void XpLib::postEvent(XPEventTypes type, uint32 data) {
 			return;
 
 		g_lastMouseEventData = data;
-		if (g_eventQueueTail->type == etMouseMove) {
+		if (g_eventQueueTail && g_eventQueueTail->type == etMouseMove) {
 			g_eventQueueTail->payload = data;
 			return;
 		}
diff --git a/engines/bolt/xplib/file.cpp b/engines/bolt/xplib/file.cpp
index f478a0178df..386c05cabb9 100644
--- a/engines/bolt/xplib/file.cpp
+++ b/engines/bolt/xplib/file.cpp
@@ -31,7 +31,7 @@ void XpLib::fileError(const char *message) {
 	error(message);
 }
 
-Common::File *XpLib::openFile(const char *fileName, short flags) {
+Common::File *XpLib::openFile(const char *fileName, int16 flags) {
 	// Read-write (mode 3) and write (mode 2) are unsupported and not needed by Cartoon Carnival...
 	assert(flags == 1);
 
@@ -76,6 +76,7 @@ void *XpLib::allocMem(uint32 size) {
 	if (!result)
 		error("XpLib::allocMem(): Not enough memory");
 
+	memset(result, 0, size);
 	return result;
 }
 
@@ -83,6 +84,8 @@ void *XpLib::tryAllocMem(uint32 size) {
 	void *result = (void *)malloc(size);
 	if (!result)
 		warning("XpLib::tryAllocMem(): Couldn't allocate memory, returning nullptr");
+	else
+		memset(result, 0, size);
 
 	return result;
 }
diff --git a/engines/bolt/xplib/palette.cpp b/engines/bolt/xplib/palette.cpp
index fd33eadd3ed..ba42e1a3cb4 100644
--- a/engines/bolt/xplib/palette.cpp
+++ b/engines/bolt/xplib/palette.cpp
@@ -26,48 +26,35 @@
 
 namespace Bolt {
 
-void XpLib::getPalette(uint16 startIndex, uint16 count, byte *destBuf) {
-	Common::StackLock lock(g_paletteMutex);
-
+void XpLib::getPalette(int16 startIndex, int16 count, byte *destBuf) {
 	memcpy(destBuf, &g_paletteBuffer[startIndex * 3], count * 3);
 }
 
-void XpLib::setPalette(uint16 count, uint16 startIndex, byte *srcBuf) {
-	Common::StackLock lock(g_paletteMutex);
-
+void XpLib::setPalette(int16 count, int16 startIndex, byte *srcBuf) {
 	if (startIndex == 0 || (startIndex == 128 && count != 1)) {
 		startIndex++;
 		srcBuf += 3;
 		count--;
 	}
 
-	if (count == 0)
-		return;
-
-	memcpy(&g_paletteBuffer[startIndex * 3], srcBuf, count * 3);
-
-	// Apply brightness shift into shifted buffer...
-	const byte *src = &g_paletteBuffer[startIndex * 3];
-	byte *dst = &g_shiftedPaletteBuffer[startIndex * 3];
+	if (count > 0) {
+		memcpy(&g_paletteBuffer[startIndex * 3], srcBuf, count * 3);
 
-	for (uint16 i = 0; i < count * 3; i++)
-		dst[i] = src[i] >> g_brightnessShift;
+		// Apply brightness shift into shifted buffer...
+		const byte *src = &g_paletteBuffer[startIndex * 3];
+		byte *dst = &g_shiftedPaletteBuffer[startIndex * 3];
 
-	// Finally set the palette!
-	g_system->getPaletteManager()->setPalette(&g_shiftedPaletteBuffer[startIndex * 3], startIndex, count);
-}
+		for (uint16 i = 0; i < count * 3; i++)
+			dst[i] = src[i] >> g_brightnessShift;
 
-void XpLib::cycleColorsCallback(void *refCon) {
-	XpLib *xp = (XpLib *)refCon;
-	xp->cycleColors();
+		// Finally set the palette!
+		g_system->getPaletteManager()->setPalette(&g_shiftedPaletteBuffer[startIndex * 3], startIndex, count);
+	}
 }
 
 bool XpLib::startCycle(XPCycleState *specs) {
-	Common::StackLock lock(g_paletteMutex);
-
 	stopCycle();
 
-	bool anyActive = false;
 	uint32 now = g_system->getMillis();
 
 	for (int16 i = 0; i < 4; i++) {
@@ -81,20 +68,12 @@ bool XpLib::startCycle(XPCycleState *specs) {
 		g_cycleStates[i].delay = specs[i].delay;
 		g_cycleStates[i].nextFire = now + specs[i].delay;
 		g_cycleStates[i].active = true;
-		anyActive = true;
-	}
-
-	if (anyActive) {
-		_bolt->getTimerManager()->installTimerProc(
-			cycleColorsCallback, 50 * 1000, this, "BoltPalCycle");
 	}
 
 	return true;
 }
 
 void XpLib::cycleColors() {
-	Common::StackLock lock(g_paletteMutex);
-
 	for (int i = 0; i < 4; i++) {
 		if (!g_cycleStates[i].active)
 			continue;
@@ -121,7 +100,6 @@ void XpLib::cycleColors() {
 
 			// Write back shifted by one (swap buffer starts 3 bytes earlier)...
 			setPalette(count, startIdx, g_cycleTempPalette);
-
 		} else if (startIdx > endIdx) {
 			int16 count = startIdx - endIdx + 1;
 			if (count > 19 || endIdx < 0 || startIdx > 255)
@@ -136,27 +114,20 @@ void XpLib::cycleColors() {
 			// Write back shifted by one (starts one entry later)...
 			setPalette(count, endIdx, &g_cycleTempPalette[3]);
 		}
+
+		g_system->updateScreen();
 	}
 }
 
 void XpLib::stopCycle() {
-	Common::StackLock lock(g_paletteMutex);
-
-	bool wasActive = false;
 	for (int16 i = 0; i < 4; i++) {
 		if (g_cycleStates[i].active) {
-			wasActive = true;
 			g_cycleStates[i].active = false;
 		}
 	}
-
-	if (wasActive)
-		_bolt->getTimerManager()->removeTimerProc(cycleColorsCallback);
 }
 
 void XpLib::setScreenBrightness(uint8 percent) {
-	Common::StackLock lock(g_paletteMutex);
-
 	if (percent >= 100) {
 		g_brightnessShift = 2; // Full brightness (VGA DAC is 6-bit)
 	} else if (percent >= 50) {
diff --git a/engines/bolt/xplib/sound.cpp b/engines/bolt/xplib/sound.cpp
index 2bb62117449..53e8c8d38cb 100644
--- a/engines/bolt/xplib/sound.cpp
+++ b/engines/bolt/xplib/sound.cpp
@@ -22,38 +22,209 @@
 #include "bolt/bolt.h"
 #include "bolt/xplib/xplib.h"
 
-namespace Bolt {
+#include "audio/audiostream.h"
+#include "audio/decoders/raw.h"
+#include "audio/mixer.h"
 
-bool XpLib::pollSound(void *outData) {
-	return true;
-}
+namespace Bolt {
 
 bool XpLib::initSound() {
+	_audioStream = Audio::makeQueuingAudioStream(22050, false);
+	_nextSoundDeadlineMs = 0;
+	_sndPlayState = 0;
+	_sndQueued = 0;
+	_sndPaused = false;
+	_sndCompletedCount = 0;
+	_sndSampleRate = 22050;
+
+	g_system->getMixer()->playStream(
+		Audio::Mixer::kSFXSoundType,
+		&_soundHandle,
+		_audioStream,
+		-1, 255, 0,
+		DisposeAfterUse::NO);
+
 	return true;
 }
 
-void XpLib::waveCb() {
+bool XpLib::pollSound(uint32 *outData) {
+	*outData = 0;
 
-}
+	if (_sndQueued == 0 || _sndPaused)
+		return false;
 
-void XpLib::shutdownSound() {
+	if (!_audioStream)
+		return false;
+
+	bool ready = false;
+
+	int activeStreams = _audioStream->numQueuedStreams();
+	int completed = _sndQueued - activeStreams;
+	if (completed > (int)_sndCompletedCount)
+		_sndCompletedCount = completed;
+
+	uint32 now = _bolt->_system->getMillis();
+
+	debug(5, "pollSound: queued=%d active=%d completed=%d state=%d deadline=%u now=%u",
+		  _sndQueued, activeStreams, _sndCompletedCount, _sndPlayState,
+		  _sndNextDeadline, now);
 
+	if (_sndCompletedCount > 0) {
+		if (_sndPlayState == 0) {
+			_sndPlayState = 1;
+		} else if (_sndPlayState == 2) {
+			_sndPlayState = 0;
+			ready = true;
+		}
+	}
+
+	if (!ready) {
+		if (now >= _sndNextDeadline) {
+			if (_sndPlayState == 0) {
+				_sndPlayState = 2;
+			} else if (_sndPlayState == 1) {
+				_sndPlayState = 0;
+				ready = true;
+			}
+		}
+	}
+
+	if (!ready)
+		return false;
+
+	*outData = 1; // The original puts the elapsed buffer data in here...
+
+	// Buffer completed!
+	_sndCompletedCount--;
+	_sndQueued--;
+
+	if (!_durationQueue.empty())
+		_durationQueue.pop();
+
+	if (_sndQueued == 0)
+		return true;
+
+	// Deadline adjustment for next buffer...
+	uint32 nextDurationMs = 0;
+	if (!_durationQueue.empty())
+		nextDurationMs = _durationQueue.front();
+
+	int32 elapsed = (int32)(_sndNextDeadline - _sndBufferQueueTime);
+
+	if (elapsed > 0) {
+		elapsed /= 50;
+		int32 maxAdj = (int32)nextDurationMs / 10;
+		if (maxAdj < elapsed)
+			elapsed = maxAdj;
+		_sndNextDeadline += (nextDurationMs - elapsed);
+	} else {
+		_sndNextDeadline = _sndBufferQueueTime + nextDurationMs;
+	}
+
+	_sndBufferQueueTime = _bolt->_system->getMillis();
+
+	return true;
 }
 
 bool XpLib::playSound(byte *data, uint32 size, int16 sampleRate) {
-	return false;
+	if (!_audioStream)
+		return false;
+
+	if (sampleRate != 22050)
+		return false;
+
+	// Drain completed buffers if full...
+	int retries = 0;
+	while (_audioStream->numQueuedStreams() >= 50) {
+		uint32 eventData;
+		if (pollSound(&eventData))
+			postEvent(etSound, eventData);
+
+		_bolt->_system->delayMillis(1);
+		if (++retries > 5000) {
+			warning("XpLib::playSound(): drain timeout");
+			return false;
+		}
+	}
+
+	byte *buf = (byte *)malloc(size);
+	if (!buf)
+		return false;
+
+	memcpy(buf, data, size);
+	_audioStream->queueBuffer(buf, size, DisposeAfterUse::YES, Audio::FLAG_UNSIGNED);
+
+	uint32 durationMs = (size * 1000) / (uint32)sampleRate;
+
+	// First buffer: set initial deadline...
+	if (_sndQueued == 0) {
+		uint32 now = _bolt->_system->getMillis();
+		_sndNextDeadline = now + durationMs;
+		_sndBufferQueueTime = now;
+
+		if (_sndPaused)
+			_pauseTimeMs = now;
+	}
+
+	_sndQueued++;
+	_durationQueue.push(durationMs);
+
+	return true;
 }
 
 bool XpLib::pauseSound() {
-	return false;
+	if (_sndPaused)
+		return true;
+
+	_sndPaused = true;
+	_pauseTimeMs = _bolt->_system->getMillis();
+	g_system->getMixer()->pauseHandle(_soundHandle, true);
+	return true;
 }
 
 bool XpLib::resumeSound() {
-	return false;
+	if (_sndQueued > 0) {
+		uint32 now = _bolt->_system->getMillis();
+		uint32 pauseDuration = now - _pauseTimeMs;
+		_sndNextDeadline += pauseDuration;
+	}
+
+	_sndPaused = false;
+	g_system->getMixer()->pauseHandle(_soundHandle, false);
+	return true;
 }
 
 bool XpLib::stopSound() {
-	return false;
+	g_system->getMixer()->stopHandle(_soundHandle);
+
+	_sndQueued = 0;
+	_sndPaused = false;
+	_sndCompletedCount = 0;
+	_sndPlayState = 0;
+	_nextSoundDeadlineMs = 0;
+	_pauseTimeMs = 0;
+
+	while (!_durationQueue.empty())
+		_durationQueue.pop();
+
+	uint32 dummy;
+	while (getEvent(etSound, &dummy) == etSound);
+
+	// Recreate stream for next use...
+	_audioStream = Audio::makeQueuingAudioStream(22050, false);
+	g_system->getMixer()->playStream(
+		Audio::Mixer::kSFXSoundType,
+		&_soundHandle,
+		_audioStream,
+		-1, 255, 0,
+		DisposeAfterUse::NO);
+
+	return true;
+}
+
+void XpLib::shutdownSound() {
+	g_system->getMixer()->stopHandle(_soundHandle);
+	_audioStream = nullptr;
 }
 
 } // End of namespace Bolt
diff --git a/engines/bolt/xplib/xplib.cpp b/engines/bolt/xplib/xplib.cpp
index 6a4057dab61..fd3abccb0b2 100644
--- a/engines/bolt/xplib/xplib.cpp
+++ b/engines/bolt/xplib/xplib.cpp
@@ -39,8 +39,6 @@ XpLib::XpLib(BoltEngine *bolt) {
 }
 
 XpLib::~XpLib() {
-	terminate();
-
 	delete _screen;
 }
 
@@ -50,6 +48,30 @@ bool XpLib::initialize() {
 
 	g_xpInitialized = true;
 
+	//g_videoSurface = new Graphics::Surface();
+
+	if (_bolt->g_extendedViewport) {
+		g_rowDirtyFlags = (byte *)malloc(EXTENDED_SCREEN_HEIGHT);
+		assert(g_rowDirtyFlags);
+		memset(g_rowDirtyFlags, 0, EXTENDED_SCREEN_HEIGHT);
+
+		g_vgaFramebuffer = (byte *)malloc(EXTENDED_SCREEN_WIDTH * EXTENDED_SCREEN_HEIGHT);
+		assert(g_vgaFramebuffer);
+		memset(g_vgaFramebuffer, 0, EXTENDED_SCREEN_WIDTH * EXTENDED_SCREEN_HEIGHT);
+
+		//g_videoSurface->init(EXTENDED_SCREEN_WIDTH, EXTENDED_SCREEN_HEIGHT, EXTENDED_SCREEN_WIDTH, g_vgaFramebuffer, Graphics::PixelFormat::createFormatCLUT8());
+	} else {
+		g_rowDirtyFlags = (byte *)malloc(SCREEN_HEIGHT);
+		assert(g_rowDirtyFlags);
+		memset(g_rowDirtyFlags, 0, SCREEN_HEIGHT);
+
+		g_vgaFramebuffer = (byte *)malloc(SCREEN_WIDTH * SCREEN_HEIGHT);
+		assert(g_vgaFramebuffer);
+		memset(g_vgaFramebuffer, 0, SCREEN_WIDTH * SCREEN_HEIGHT);
+
+		//g_videoSurface->init(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_WIDTH, g_vgaFramebuffer, Graphics::PixelFormat::createFormatCLUT8());
+	}
+
 	if (!initTimer()) {
 		terminate();
 		return false;
@@ -85,6 +107,14 @@ void XpLib::terminate() {
 	shutdownEvents();
 	shutdownTimer();
 
+	free(g_vgaFramebuffer);
+	g_vgaFramebuffer = nullptr;
+
+	free(g_rowDirtyFlags);
+	g_rowDirtyFlags = nullptr;
+
+	//delete g_videoSurface;
+
 	g_xpInitialized = false;
 }
 
diff --git a/engines/bolt/xplib/xplib.h b/engines/bolt/xplib/xplib.h
index ff9057e379d..7cd1672596b 100644
--- a/engines/bolt/xplib/xplib.h
+++ b/engines/bolt/xplib/xplib.h
@@ -24,13 +24,28 @@
 
 #include "bolt/bolt.h"
 
+#include "audio/audiostream.h"
+#include "audio/mixer.h"
+
 #include "common/events.h"
 #include "common/file.h"
 #include "common/mutex.h"
+
 #include "graphics/paletteman.h"
 
 namespace Bolt {
 
+// RENDER FLAGS
+
+#define RF_OVERLAY_ACTIVE    0x01  // RLE overlay source stored on front surface
+#define RF_DOUBLE_BUFFER     0x02  // Double-buffer compositing enabled
+#define RF_FULL_REDRAW       0x04  // Full-screen overwrite, skip dirty tracking
+#define RF_FRONT_DIRTY       0x08  // Front surface pixels changed
+#define RF_BACK_DIRTY        0x10  // Back surface pixels changed
+#define RF_FRONT_PAL_DIRTY   0x20  // Front surface palette changed
+#define RF_BACK_PAL_DIRTY    0x40  // Back surface palette changed
+#define RF_CURSOR_VISIBLE    0x80  // Cursor should be drawn this frame
+
 struct DisplaySpecs;
 class BoltEngine;
 
@@ -70,6 +85,11 @@ typedef struct XPPicDesc {
 	}
 } XPPicDesc;
 
+enum XPSurfaceType : int {
+	stFront = 0,
+	stBack
+};
+
 typedef struct XPSurface {
 	XPPicDesc mainPic;
 	XPPicDesc overlayPic;
@@ -90,7 +110,8 @@ enum XPEventTypes : int16 {
 	etMouseUp    = 4,
 	etJoystick   = 5,
 	etSound      = 6,
-	etInactivity = 7
+	etInactivity = 7,
+	etTrigger    = 8
 };
 
 enum XPEventKeyStates : int16 {
@@ -130,6 +151,8 @@ typedef struct XPTimer {
 } XPTimer;
 
 class XpLib {
+friend class BoltEngine;
+
 public:
 	XpLib(BoltEngine *bolt);
 	~XpLib();
@@ -142,8 +165,8 @@ public:
 	void maskBlit(byte *src, uint16 srcStride, byte *dst, uint16 dstStride, uint16 width, uint16 height);
 
 	// Palette
-	void getPalette(uint16 startIndex, uint16 count, byte *destBuf);
-	void setPalette(uint16 count, uint16 startIndex, byte *srcBuf);
+	void getPalette(int16 startIndex, int16 count, byte *destBuf);
+	void setPalette(int16 count, int16 startIndex, byte *srcBuf);
 	bool startCycle(XPCycleState *specs);
 	void cycleColors();
 	void stopCycle();
@@ -168,7 +191,7 @@ public:
 	void disableController();
 
 	// Display
-	bool chooseDisplaySpec(int *outMode, int numSpecs, DisplaySpecs *specs);
+	bool setDisplaySpec(int *outMode, DisplaySpecs *spec);
 	void setCoordSpec(int16 x, int16 y, int16 width, int16 height);
 	void displayPic(XPPicDesc *pic, int16 x, int16 y, int16 page);
 	void setFrameRate(int16 fps);
@@ -181,7 +204,7 @@ public:
 	void randomize();
 
 	// File
-	Common::File *openFile(const char *fileName, short flags);
+	Common::File *openFile(const char *fileName, int16 flags);
 	void closeFile(Common::File *handle);
 	bool readFile(Common::File *handle, void *buffer, uint32 *size);
 	bool setFilePos(Common::File *handle, int32 offset, int32 origin);
@@ -221,9 +244,6 @@ protected:
 	byte g_cycleTempPalette[3 * 20];
 	uint32 g_cycleTimerIds[4];
 	int16 g_brightnessShift = 0;
-	Common::Mutex g_paletteMutex;
-
-	static void cycleColorsCallback(void *refConf);
 
 	// Cursor
 	bool initCursor();
@@ -231,9 +251,6 @@ protected:
 	void readJoystick(int16 *outX, int16 *outY);
 
 	byte g_cursorBuffer[16 * 16];
-	int16 g_cursorWidth = 0;
-	int16 g_cursorHeight = 0;
-	int16 g_cursorScale = 0;
 	int16 g_cursorHotspotX = 0;
 	int16 g_cursorHotspotY = 0;
 	int16 g_lastCursorX = 0;
@@ -289,7 +306,7 @@ protected:
 	void virtualToScreen(int16 *x, int16 *y);
 	void screenToVirtual(int16 *x, int16 *y);
 	void dispatchBlit(int16 mode, byte *src, uint16 srcStride, byte *dst, uint16 dstStride, uint16 width, uint16 height);
-	bool clipAndBlit(XPPicDesc *src, XPSurface *dest, int16 x, int16 y, Common::Rect *outClip);
+	bool clipAndBlit(XPPicDesc *src, XPPicDesc *dest, int16 x, int16 y, Common::Rect *outClip);
 	void addDirtyRect(Common::Rect *rect);
 	void waitForFrameRate();
 	void handlePaletteTransitions();
@@ -309,8 +326,8 @@ protected:
 	int16 g_virtualWidth = 0;
 	int16 g_virtualHeight = 0;
 	int16 g_currentDisplayPage = 0;
-	int16 g_spriteOverlayActive = 0;
-	int16 g_spriteOverlayEnabled = 0;
+	int16 g_prevRenderFlags = 0;
+	int16 g_renderFlags = 0;
 	int16 g_frameRateFPS = 0;
 	int16 g_overlayCount = 0;
 	int16 g_prevDirtyCount = 0;
@@ -323,14 +340,15 @@ protected:
 
 	Common::Rect g_dirtyRects[30];
 	Common::Rect g_prevDirtyRects[30];
-	Common::Rect g_cursorDirtyRect;
+	Common::Rect g_cursorRect;
 	Common::Rect g_prevCursorRect;
-	Common::Rect g_cursorClipRect2;
-	Common::Rect g_prevCursorRect2;
+	Common::Rect g_overlayCursorRect;
+	Common::Rect g_prevOverlayCursorRect;
 
-	byte g_vgaFramebuffer[320 * 200];
+	byte *g_vgaFramebuffer = nullptr;
+	//Graphics::Surface *g_videoSurface = nullptr;
+	byte *g_rowDirtyFlags = nullptr;
 	byte g_cursorBackgroundSaveBuffer[16 * 16];
-	byte g_rowDirtyFlags[200];
 
 	XPPicDesc g_cursorBackgroundSave;
 	XPPicDesc g_cursorSprite;
@@ -339,10 +357,23 @@ protected:
 	void fileError(const char *message);
 
 	// Sound
-	bool pollSound(void *outData);
+	bool pollSound(uint32 *outData);
 	bool initSound();
 	void shutdownSound();
 
+	Audio::QueuingAudioStream *_audioStream = nullptr;
+	Audio::SoundHandle _soundHandle;
+	Common::Queue<uint32> _durationQueue;
+	uint32 _nextSoundDeadlineMs = 0;
+	uint32 _pauseTimeMs = 0;
+	int16 _sndPlayState = 0;
+	int16 _sndQueued = 0;
+	int _sndCompletedCount = 0;
+	bool _sndPaused = false;
+	int16 _sndSampleRate = 22050;
+	uint32 _sndNextDeadline = 0;
+	uint32 _sndBufferQueueTime = 0;
+
 	// Timer
 	bool initTimer();
 	void shutdownTimer();


Commit: efb9fe545582c88a940eca530a08f3f7ccc3cab5
    https://github.com/scummvm/scummvm/commit/efb9fe545582c88a940eca530a08f3f7ccc3cab5
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-04-07T01:11:14+02:00

Commit Message:
BOLT: CARNIVAL: Fix screensaver dimming routine

Changed paths:
    engines/bolt/xplib/palette.cpp


diff --git a/engines/bolt/xplib/palette.cpp b/engines/bolt/xplib/palette.cpp
index ba42e1a3cb4..abaaed879d9 100644
--- a/engines/bolt/xplib/palette.cpp
+++ b/engines/bolt/xplib/palette.cpp
@@ -129,19 +129,21 @@ void XpLib::stopCycle() {
 
 void XpLib::setScreenBrightness(uint8 percent) {
 	if (percent >= 100) {
-		g_brightnessShift = 2; // Full brightness (VGA DAC is 6-bit)
+		g_brightnessShift = 0; // Full brightness
 	} else if (percent >= 50) {
-		g_brightnessShift = 3; // 50%
+		g_brightnessShift = 1; // 50%
 	} else if (percent >= 25) {
-		g_brightnessShift = 4; // 25%
+		g_brightnessShift = 2; // 25%
 	} else if (percent >= 12) {
-		g_brightnessShift = 5; // 12%
+		g_brightnessShift = 3; // 12%
 	} else {
-		g_brightnessShift = 6; // Near black
+		g_brightnessShift = 4; // Near black
 	}
 
 	// Re-apply entire palette with new brightness...
 	setPalette(256, 0, g_paletteBuffer);
+
+	g_system->updateScreen();
 }
 
 } // End of namespace Bolt


Commit: ca9d735bd4160f129531b3834a9218ad482968c5
    https://github.com/scummvm/scummvm/commit/ca9d735bd4160f129531b3834a9218ad482968c5
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-04-07T01:11:14+02:00

Commit Message:
BOLT: CARNIVAL: Fix palette setup when playing a video over a booth

Changed paths:
    engines/bolt/av.cpp
    engines/bolt/barker.cpp
    engines/bolt/booth.cpp


diff --git a/engines/bolt/av.cpp b/engines/bolt/av.cpp
index c5233c5b443..ff45768412b 100644
--- a/engines/bolt/av.cpp
+++ b/engines/bolt/av.cpp
@@ -114,7 +114,7 @@ void BoltEngine::processPacket(RTFPacket *packet) {
 		// Skip this frame if: low framerate, only 1 behind, or haven't accumulated enough...
 		if (frameRate <= 10 || packet->skipCount > 1 || g_avFrameAccum < g_avSkipLevel) {
 			g_avFrameAccum += packet->duration;
-			warning("BoltEngine::processPacket() WANTS TO RETURN BUT IT WILL SKIP FRAMES");
+			debug(4, "BoltEngine::processPacket(): Skipping frame...");
 			return;
 		}
 
diff --git a/engines/bolt/barker.cpp b/engines/bolt/barker.cpp
index 91f663a9979..2fa68c1f179 100644
--- a/engines/bolt/barker.cpp
+++ b/engines/bolt/barker.cpp
@@ -94,7 +94,7 @@ int16 BoltEngine::barker(BarkerTable *table, int16 startBooth) {
 }
 
 bool BoltEngine::checkError() {
-	return false;
+	return g_curErrorCode != 0;
 }
 
 } // End of namespace Bolt
diff --git a/engines/bolt/booth.cpp b/engines/bolt/booth.cpp
index acfe6f2457c..6ab9fd96232 100644
--- a/engines/bolt/booth.cpp
+++ b/engines/bolt/booth.cpp
@@ -35,7 +35,7 @@ void BoltEngine::startCycle(byte *cycleResource) {
 void BoltEngine::displayBooth(int16 page) {
 	_xp->setTransparency(page);
 	_xp->displayPic(&g_boothLetterSprite, g_displayX, g_displayY, page);
-	displayColors(g_boothPalCycleData, 0, page);
+	displayColors(g_boothPalCycleData, page, 0);
 
 	if (page != stFront)
 		_xp->fillDisplay(0, stFront);
@@ -775,10 +775,10 @@ bool BoltEngine::handleButtonPress(int16 hotspot) {
 
 				if (playAV(g_rtfHandle, 3, g_displayWidth, g_displayHeight, g_displayX, g_displayY) == 0) {
 					fadeToBlack(1);
-					_xp->setTransparency(0);
+					_xp->setTransparency(false);
 					displayPic(getBOLTMember(g_boothsBoltLib, g_displayMode ? 0x1802 : 0x1801), g_displayX, g_displayY, 0);
 					_xp->updateDisplay();
-					displayColors(getBOLTMember(g_boothsBoltLib, 0x1800), 0, 0);
+					displayColors(getBOLTMember(g_boothsBoltLib, 0x1800), stFront, 0);
 					_xp->updateDisplay();
 
 					uint32 timer = _xp->startTimer(5000);


Commit: 8f27afd84e6fdd6092d4c142d99f51c928c49962
    https://github.com/scummvm/scummvm/commit/8f27afd84e6fdd6092d4c142d99f51c928c49962
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-04-07T01:11:14+02:00

Commit Message:
BOLT: CARNIVAL: Implement Fred's minigame

Also add support for italian demo

Changed paths:
    engines/bolt/anim.cpp
    engines/bolt/bolt.cpp
    engines/bolt/bolt.h
    engines/bolt/booth.cpp
    engines/bolt/booths/fred.cpp
    engines/bolt/resource.cpp
    engines/bolt/xplib/display.cpp
    engines/bolt/xplib/events.cpp
    engines/bolt/xplib/random.cpp
    engines/bolt/xplib/xplib.h


diff --git a/engines/bolt/anim.cpp b/engines/bolt/anim.cpp
index 9c869224c5e..7d9b90fc830 100644
--- a/engines/bolt/anim.cpp
+++ b/engines/bolt/anim.cpp
@@ -26,6 +26,7 @@ namespace Bolt {
 bool BoltEngine::startAnimation(RTFResource *rtf, int16 animIndex) {
 	if (!initAnim(rtf, animIndex)) {
 		cleanUpAnim();
+		debug("BoltEngine::startAnimation(): Failed to start animation %d", animIndex);
 		return false;
 	}
 
diff --git a/engines/bolt/bolt.cpp b/engines/bolt/bolt.cpp
index 4baa8fc5b72..76a04e3e052 100644
--- a/engines/bolt/bolt.cpp
+++ b/engines/bolt/bolt.cpp
@@ -70,6 +70,9 @@ Common::Error BoltEngine::run() {
 		initGraphics(EXTENDED_SCREEN_WIDTH, EXTENDED_SCREEN_HEIGHT);
 	}
 
+	if ((getFeatures() & ADGF_DEMO) != 0)
+		g_isDemo = true;
+
 	// Set the engine's debugger console
 	setDebugger(new Console());
 
@@ -107,7 +110,7 @@ void BoltEngine::boltMain() {
 	if (allocResourceIndex()) {
 		g_boothsBoltLib = nullptr;
 
-		if (openBOLTLib(&g_boothsBoltLib, &g_resourceDefaultCallbacks, assetPath("booths.blt"))) {
+		if (openBOLTLib(&g_boothsBoltLib, &g_boothsBoltCallbacks, assetPath("booths.blt"))) {
 			int16 chosenSpecId = g_extendedViewport ? 0 : 1;
 
 			if (_xp->setDisplaySpec(&g_displayMode, &g_displaySpecs[chosenSpecId])) {
@@ -128,29 +131,33 @@ void BoltEngine::boltMain() {
 				}
 
 				if (g_rtfHandle) {
-					playAV(g_rtfHandle, 0, g_displayWidth, g_displayHeight, g_displayX, g_displayY);
+					int16 boothGroupBase = g_isDemo ? 0x0E00 : 0x1700;
+
+					if (!g_isDemo) {
+						playAV(g_rtfHandle, 0, g_displayWidth, g_displayHeight, g_displayX, g_displayY);
+					}
 
-					boothSprite = getBOLTMember(g_boothsBoltLib, (g_displayMode != 0) ? 0x1701 : 0x1702);
+					boothSprite = getBOLTMember(g_boothsBoltLib, (g_displayMode != 0) ? (boothGroupBase + 1) : (boothGroupBase + 2));
 
 					_xp->setTransparency(false);
 
-					displayColors(getBOLTMember(g_boothsBoltLib, 0x1700), stFront, 0);
+					displayColors(getBOLTMember(g_boothsBoltLib, boothGroupBase), stFront, 0);
 					displayPic(boothSprite, g_displayX, g_displayY, stFront);
 
 					_xp->updateDisplay();
 
-					displayColors(getBOLTMember(g_boothsBoltLib, 0x1700), stBack, 0);
+					displayColors(getBOLTMember(g_boothsBoltLib, boothGroupBase), stBack, 0);
 					displayPic(boothSprite, g_displayX, g_displayY, stBack);
 
-					playAV(g_rtfHandle, 2, g_displayWidth, g_displayHeight, g_displayX, g_displayY);
+					playAV(g_rtfHandle, g_isDemo ? 0 : 2, g_displayWidth, g_displayHeight, g_displayX, g_displayY);
 
-					freeBOLTGroup(g_boothsBoltLib, 0x1700, 1);
+					freeBOLTGroup(g_boothsBoltLib, boothGroupBase, 1);
 
 					if (getBOLTGroup(g_boothsBoltLib, 0, 1)) {
 						setCursorPict(memberAddr(g_boothsBoltLib, 0));
 
 						if (initVRam(1500)) {
-							barkerTable = createBarker(2, 17);
+							barkerTable = createBarker(2, g_isDemo ? 19 : 17);
 							if (barkerTable) {
 								// Register booth handlers...
 								registerSideShow(barkerTable, &BoltEngine::hucksBooth, 3);
@@ -168,6 +175,11 @@ void BoltEngine::boltMain() {
 								registerSideShow(barkerTable, &BoltEngine::topCatGame, 15);
 								registerSideShow(barkerTable, &BoltEngine::winALetter, 16);
 
+								if (g_isDemo) {
+									registerSideShow(barkerTable, &BoltEngine::displayDemoPict, 17);
+									registerSideShow(barkerTable, &BoltEngine::endDemo, 18);
+								}
+
 								g_lettersWon = 0;
 								_xp->setScreenSaverTimer(1800);
 
@@ -215,4 +227,25 @@ void BoltEngine::setCursorPict(byte *sprite) {
 	_xp->setCursorImage(cursorBitmap, 7, 7);
 }
 
+int16 BoltEngine::displayDemoPict(int16 prevBooth) {
+	uint32 eventData;
+
+	while (_xp->getEvent(etMouseDown, &eventData) == etMouseDown);
+
+	while (_xp->getEvent(etTimer, &eventData) != etTimer);
+
+	_xp->stopCycle();
+	_xp->setTransparency(false);
+
+	int16 memberId = (g_displayMode != 0) ? 0x1002 : 0x1001;
+	displayPic(getBOLTMember(g_boothsBoltLib, memberId), g_displayX, g_displayY, stFront);
+	displayColors(getBOLTMember(g_boothsBoltLib, 0x1000), stFront, 0);
+
+	_xp->updateDisplay();
+
+	while (_xp->getEvent(etMouseDown, &eventData) != etMouseDown);
+
+	return prevBooth;
+}
+
 } // End of namespace Bolt
diff --git a/engines/bolt/bolt.h b/engines/bolt/bolt.h
index c24dcb6fd5c..c0f62fb8284 100644
--- a/engines/bolt/bolt.h
+++ b/engines/bolt/bolt.h
@@ -77,12 +77,12 @@ typedef struct BOLTHeader {
 typedef void (*BOLTCallback)(void);
 
 typedef struct BOLTCallbacks {
-	BOLTCallback *typeLoadCallbacks;   // +0x00: type-based post-load fixup (25 entries)
-	BOLTCallback *typeFreeCallbacks;   // +0x04: type-based post-free cleanup (25 entries)
-	BOLTCallback *memberLoadCallbacks; // +0x08: per-member pre-load callbacks
-	BOLTCallback *memberFreeCallbacks; // +0x0C: per-member pre-free callbacks
-	BOLTCallback *groupLoadCallbacks;  // +0x10: per-group load callbacks
-	BOLTCallback *groupFreeCallbacks;  // +0x14: per-group free callbacks
+	BOLTCallback *typeLoadCallbacks;
+	BOLTCallback *typeFreeCallbacks;
+	BOLTCallback *memberLoadCallbacks;
+	BOLTCallback *memberFreeCallbacks;
+	BOLTCallback *groupLoadCallbacks;
+	BOLTCallback *groupFreeCallbacks;
 
 	BOLTCallbacks() {
 		typeLoadCallbacks = nullptr;
@@ -95,15 +95,14 @@ typedef struct BOLTCallbacks {
 } BOLTCallbacks;
 
 typedef struct BOLTMemberEntry {
-	byte flags;          // +0x00: bit 3 = uncompressed
-	byte preLoadCbIndex; // +0x01: index into lib->userData1 callback table
-	byte preFreeCbIndex; // +0x02: index into lib->userData2 callback table
-	byte typeCbIndex;    // +0x03: index into lib->loadCallbacks (post-load)
-						 //        and lib->freeCallbacks (post-free)
-	uint32 decompSize;   // +0x04: decompressed data size (big-endian in file)
-	uint32 fileOffset;   // +0x08: file offset of compressed/raw data (big-endian in file)
+	byte flags;
+	byte preLoadCbIndex;
+	byte preFreeCbIndex;
+	byte typeCbIndex;
+	uint32 decompSize;
+	uint32 fileOffset;
 	uint32 dataPtrPlaceholder;
-	byte *dataPtr; // +0x0C: pointer to loaded data (NULL if not loaded)
+	byte *dataPtr;
 
 	BOLTMemberEntry() {
 		flags = 0;
@@ -115,16 +114,16 @@ typedef struct BOLTMemberEntry {
 		dataPtrPlaceholder = 0;
 		dataPtr = nullptr;
 	}
-} BOLTMemberEntry; // 0x10 = 16 bytes
+} BOLTMemberEntry;
 
 typedef struct BOLTGroupEntry {
-	byte flags;                     // +0x00: unknown flags
-	byte loadCbIndex;               // +0x01: group load callback index (into lib+0x18)
-	byte freeCbIndex;               // +0x02: group free callback index (into lib+0x1C)
-	byte memberCount;               // +0x03: number of members (0 = 256)
-	uint32 memberDirOffset;         // +0x04: file offset to member directory (big-endian)
-	uint32 memberDataOffset;        // +0x08: file offset to member data (big-endian)
-	uint32 groupDataPtrPlaceholder; // +0x0C: pointer to loaded group data block (NULL if not loaded)
+	byte flags;
+	byte loadCbIndex;
+	byte freeCbIndex;
+	byte memberCount;
+	uint32 memberDirOffset;
+	uint32 memberDataOffset;
+	uint32 groupDataPtrPlaceholder;
 	byte *memberData;
 	BOLTMemberEntry *members;
 
@@ -153,14 +152,14 @@ typedef struct BOLTGroupEntry {
 		members = new BOLTMemberEntry[actualNumber];
 	}
 
-} BOLTGroupEntry;            // 0x10 = 16 bytes
+} BOLTGroupEntry;
 
 typedef struct BOLTLib {
-	int16 refCount;           // +0x00
-	int16 groupCount;         // +0x02 (0 = 256)
-	Common::File *fileHandle; // +0x04
+	int16 refCount;
+	int16 groupCount;
+	Common::File *fileHandle;
 	BOLTCallbacks callbacks;
-	BOLTGroupEntry *groups; // +0x20
+	BOLTGroupEntry *groups;
 
 	BOLTLib(int inGroupCount) {
 		refCount = 0;
@@ -201,16 +200,15 @@ typedef struct RTFResource {
 
 struct RTFPacket;
 typedef struct RTFPacket {
-	uint32 tag;       // +0x00: chunk tag (e.g. MKTAG('A','2','2','8'))
-	uint32 allocSize; // +0x04: total allocated size (including header)
-	uint32 dataSize;  // +0x08: raw data size
-	byte *dataPtr;    // +0x0C: pointer to data (= this + 0x1C)
-	int16 skipCount;  // +0x10: how late this frame is
-	int16 frameRate;    // +0x12: frame rate reference (or 50 if no sound)
-	int16 duration;   // +0x14: sound duration in hundredths of a second
-	int16 timestamp;  // +0x16: cumulative timeline position
-	RTFPacket *next;  // +0x18: next packet in linked list
-	// +0x1C: data begins here
+	uint32 tag;
+	uint32 allocSize;
+	uint32 dataSize;
+	byte *dataPtr;
+	int16 skipCount;
+	int16 frameRate;
+	int16 duration;
+	int16 timestamp;
+	RTFPacket *next;
 	byte *ringBufPtr;
 
 	RTFPacket() {
@@ -225,7 +223,51 @@ typedef struct RTFPacket {
 		next = nullptr;
 		ringBufPtr = nullptr;
 	}
-} RTFPacket; // 0x1C header = 28 bytes
+} RTFPacket;
+
+// FRED GAME
+
+typedef struct FredSoundInfo {
+	byte *data;
+	uint32 size;
+
+	FredSoundInfo() {
+		data = nullptr;
+		size = 0;
+	}
+} FredSoundInfo;
+
+typedef struct FredEntityState {
+	uint16 flags;
+	int16 frameCountdown;
+	int16 animMode;
+	int16 frameIndex;
+	byte *animTable;
+	int16 direction;
+	int32 xPos;
+	int32 yPos;
+	int32 prevXPos;
+	int32 prevYPos;
+	int16 speed;
+	byte *pathTable;
+	int16 pathIndex;
+
+	FredEntityState() {
+		flags = 0;
+		frameCountdown = 0;
+		animMode = 0;
+		frameIndex = 0;
+		animTable = nullptr;
+		direction = 0;
+		xPos = 0;
+		yPos = 0;
+		prevXPos = 0;
+		prevYPos = 0;
+		speed = 0;
+		pathTable = nullptr;
+		pathIndex = 0;
+	}
+} FredEntityState;
 
 class BoltEngine : public Engine {
 friend class XpLib;
@@ -256,17 +298,14 @@ public:
 	}
 
 	bool hasFeature(EngineFeature f) const override {
-		return
-		    (f == kSupportsLoadingDuringRuntime) ||
-		    (f == kSupportsSavingDuringRuntime) ||
-		    (f == kSupportsReturnToLauncher);
+		return (f == kSupportsReturnToLauncher);
 	};
 
 	bool canLoadGameStateCurrently(Common::U32String *msg = nullptr) override {
-		return true;
+		return false;
 	}
 	bool canSaveGameStateCurrently(Common::U32String *msg = nullptr) override {
-		return true;
+		return false;
 	}
 
 	/**
@@ -292,10 +331,12 @@ protected:
 
 	XpLib *_xp = nullptr;
 	bool g_extendedViewport = false;
+	bool g_isDemo = false;
 
 	// xpMain
 	void boltMain();
 	void setCursorPict(byte *sprite);
+	int16 displayDemoPict(int16 prevBooth);
 
 	// Booth
 	void startCycle(byte *cycleResource);
@@ -310,7 +351,7 @@ protected:
 	int16 mainEntrance(int16 prevBooth);
 	bool loadBooth(int16 boothId);
 	void unloadBooth();
-	int16 openBooth(int16 sceneId);
+	int16 openBooth(int16 boothId);
 	void closeBooth();
 	void playTour();
 	void finishPlayingHelp(int16 activeHotspot);
@@ -325,12 +366,13 @@ protected:
 	void loadColors();
 	void shiftColorMap(byte *colorMap, int16 delta);
 	void playBoothAV();
-	void mapIdleAnimation();
-	void boothIdleAnimation();
-	void screensaverStep();
+	void mainEntranceHelpBlink();
+	void boothHelpBlink();
+	void tourPaletteCycleStep();
 	void fadeToBlack(int16 steps);
 	void flushInput();
 	int16 winALetter(int16 prevBooth);
+	int16 endDemo(int16 prevBooth);
 
 	int16 g_lettersWon = 0;
 	bool g_allLettersWonFlag = false;
@@ -354,27 +396,34 @@ protected:
 	byte *g_boothPalCycleData = nullptr;
 	XPPicDesc g_boothLetterSprite;
 	bool g_needInitCursorPos = true;
-	byte *g_boothExitLeft = nullptr;
-	byte *g_boothExitRight = nullptr;
+	byte *g_boothVisitSignOn = nullptr;
+	byte *g_boothVisitSignOff = nullptr;
 	byte g_leftDoorNavTable[3] = {3, 2, 4};
 	byte g_rightDoorNavTable[3] = {1, 0, 5};
 
 	int16 g_cursorX = 0;
 	int16 g_cursorY = 0;
 	int16 g_hoveredHotspot = 0;
-	uint32 g_keyTimer = 0;
+	uint32 g_helpTimer = 0;
 	int16 g_keyReleased = 0;
 	int16 g_keyLeft = 0;
 	int16 g_keyRight = 0;
 	int16 g_keyUp = 0;
 	int16 g_keyDown = 0;
-	int16 g_keyHelp = 0;
+	int16 g_helpFlag = 0;
 	int16 g_keyEnter = 0;
 
-	int16 g_screensaverStep = 0;
+	int16 g_tourStep = 0;
 	int16 g_helpPlaying = 0;
 	int16 g_helpIsIdle = 0;
-	int16 g_idleHelpAvailable = 0;
+	int16 g_idleHelpAudioAvailable = 1;
+
+	int16 g_huckWins = 0;
+	int16 g_fredWins = 0;
+	int16 g_scoobyWins = 0;
+	int16 g_yogiWins = 0;
+	int16 g_georgeWins = 0;
+	int16 g_topCatWins = 0;
 
 	// Barker
 	BarkerTable *createBarker(int16 minIndex, int16 maxIndex);
@@ -432,7 +481,7 @@ protected:
 	void swapAllLongs();
 
 	BOLTLib *g_boothsBoltLib = nullptr;
-	BOLTCallbacks g_resourceDefaultCallbacks;
+	BOLTCallbacks g_boothsBoltCallbacks;
 
 	static BOLTCallback g_defaultTypeLoadCallbacks[25];
 	static BOLTCallback g_defaultTypeFreeCallbacks[25];
@@ -566,22 +615,95 @@ protected:
 
 	// --- MINIGAMES ---
 
-	// Fred
-	int16 fredGame(int16 prevBooth) { return 0; }
-
-	// George
+	// --- FRED ---
+	int16 fredGame(int16 prevBooth);
+	bool initFred();
+	void cleanUpFred();
+	bool initFredLevel(int16 levelGroup, int16 palGroup);
+	void termFredLevel(int16 levelGroup, int16 palGroup);
+	void swapFredAnimEntry();
+	void swapFredAnimDesc();
+	void swapFredLevelDesc();
+	int16 playFred();
+	int16 helpFred();
+	void hiliteFredHelpObject(byte *entry, int16 highlight);
+	void helpAnimStep();
+	bool spawnBalloon();
+	int16 calcBalloonSpawnDelay();
+	int16 selectBalloonRow();
+	void setFredAnimMode(FredEntityState *state, int16 mode);
+	void renderFredScene();
+	void getFredSoundInfo(BOLTLib *lib, int16 memberId, FredSoundInfo *soundInfo);
+	void playFredSound(FredSoundInfo *oneShot, FredSoundInfo *loop);
+	void updateFredSound();
+
+	static void resolveAllRefsCb();
+	static void swapFredAnimEntryCb();
+	static void swapFredAnimDescCb();
+	static void swapFredLevelDescCb();
+
+	BOLTLib *g_fredBoltLib = nullptr;
+	BOLTCallbacks g_fredBoltCallbacks;
+
+	static BOLTCallback g_fredTypeLoadCallbacks[28];
+	static BOLTCallback g_fredTypeFreeCallbacks[28];
+
+	byte *g_fredLevelPtr = nullptr;
+	byte *g_fredBackground = nullptr;
+	byte *g_fredBalloonString = nullptr;
+	byte *g_fredPalette = nullptr;
+	byte *g_fredFacingLeftRect = nullptr;
+	byte *g_fredFacingRightRect = nullptr;
+	byte *g_fredTurningRect = nullptr;
+	byte *g_fredBalloonRect = nullptr;
+	byte *g_fredHelpEntries = nullptr;
+	byte *g_fredPlayButton = nullptr;
+	byte *g_fredSprites[10] = { nullptr };
+	FredEntityState **g_fredEntitiesTable = nullptr;
+
+	byte *g_fredRowBounds = nullptr;
+	byte *g_fredShuffleTable = nullptr;
+	byte *g_fredCycleRaw = nullptr;
+	byte *g_fredBalloonSprite = nullptr;
+	byte *g_fredPathMatrix = nullptr;
+	byte *g_fredCurrentHelpObject = nullptr;
+	byte *g_fredHoveredEntry = nullptr;
+
+	FredSoundInfo g_fredSounds[4];
+
+	FredSoundInfo *g_fredCurrentSound = nullptr;
+	FredSoundInfo *g_fredLoopSound = nullptr;
+	FredSoundInfo *g_fredPendingOneShot = nullptr;
+	FredSoundInfo *g_fredPendingLoop = nullptr;
+
+	FredEntityState g_fredSprite;
+
+	int16 g_fredSaveData[3] = { 0, 0, 0 };
+	int16 g_fredLevelIndex = 0;
+
+	int16 g_fredBalloonSpawnDelay = 0;
+	int16 g_fredBalloonSearchIdx = 0;
+	uint32 g_fredTimer = 0;
+	int16 g_fredHelpStep = -1;
+	int16 g_fredShowHelp = 1;
+
+	XPCycleState g_fredCycleSpecs[4];
+
+	const char *g_fredSaveFile = "FredBC";
+
+	// --- GEORGE ---
 	int16 georgeGame(int16 prevBooth) { return 0; }
 
-	// Huck
+	// --- HUCK ---
 	int16 huckGame(int16 prevBooth) { return 0; }
 
-	// Scooby
+	// --- SCOOBY ---
 	int16 scoobyGame(int16 prevBooth) { return 0; }
 
-	// TopCat
+	// --- TOPCAT ---
 	int16 topCatGame(int16 prevBooth) { return 0; }
 
-	// Yogi
+	// --- YOGI ---
 	int16 yogiGame(int16 prevBooth) { return 0; }
 };
 
diff --git a/engines/bolt/booth.cpp b/engines/bolt/booth.cpp
index 6ab9fd96232..a8f26fd9ef0 100644
--- a/engines/bolt/booth.cpp
+++ b/engines/bolt/booth.cpp
@@ -65,7 +65,7 @@ int16 BoltEngine::hucksBooth(int16 prevBooth) {
 	case 2:
 		return 10; // default (play game)
 	case 3:
-		return 10; // play game
+		return g_isDemo ? 17 : 10; // play game
 	default:
 		return 10;
 	}
@@ -85,7 +85,7 @@ int16 BoltEngine::fredsBooth(int16 prevBooth) {
 	case 2:
 		return 11;
 	case 3:
-		return 11;
+		return g_isDemo ? 17 : 11;
 	default:
 		return 11;
 	}
@@ -105,6 +105,9 @@ int16 BoltEngine::scoobysBooth(int16 prevBooth) {
 	case 2:
 		return 12;
 	case 3:
+		if (g_isDemo && g_scoobyWins >= 2)
+			return 17;
+
 		return 12;
 	default:
 		return 12;
@@ -125,6 +128,9 @@ int16 BoltEngine::yogisBooth(int16 prevBooth) {
 	case 2:
 		return 13;
 	case 3:
+		if (g_isDemo && g_yogiWins >= 2)
+			return 17;
+
 		return 13;
 	default:
 		return 13;
@@ -145,6 +151,9 @@ int16 BoltEngine::georgesBooth(int16 prevBooth) {
 	case 2:
 		return 14;
 	case 3:
+		if (g_isDemo && g_georgeWins >= 2)
+			return 17;
+
 		return 14;
 	default:
 		return 14;
@@ -165,7 +174,7 @@ int16 BoltEngine::topCatsBooth(int16 prevBooth) {
 	case 2:
 		return 15;
 	case 3:
-		return 15;
+		return g_isDemo ? 17 : 15;
 	default:
 		return 15;
 	}
@@ -177,7 +186,7 @@ int16 BoltEngine::mainEntrance(int16 prevBooth) {
 
 	switch (result) {
 	case -1:
-		return 0; // exit
+		return g_isDemo ? 18 : 0; // exit
 	case 0:
 		return 8; // left (TopCat)
 	case 1:
@@ -189,7 +198,9 @@ int16 BoltEngine::mainEntrance(int16 prevBooth) {
 	case 4:
 		return 9; // self
 	case 5:
-		return 0; // exit
+		return g_isDemo ? 18 : 0; // exit
+	case 6:
+		return g_isDemo ? 18 : 9; // Case only available in the demo
 	default:
 		return 9;
 	}
@@ -286,19 +297,19 @@ void BoltEngine::unloadBooth() {
 	g_boothLoadedMask = 0;
 }
 
-int16 BoltEngine::openBooth(int16 sceneId) {
+int16 BoltEngine::openBooth(int16 boothId) {
 	int16 baseResId;
 	int16 resId;
 
-	g_currentBoothScene = sceneId;
+	g_currentBoothScene = boothId;
 
-	if (!loadBooth(sceneId))
+	if (!loadBooth(boothId))
 		return -1;
 
-	baseResId = (sceneId - 3) << 8;
+	baseResId = (boothId - 3) << 8;
 
 	// Main entrance has more hotspots/animations than regular booths
-	if (sceneId == 9) {
+	if (boothId == 9) {
 		g_boothNumHotspots = 8;
 		g_boothNumAnimations = 7;
 	} else {
@@ -317,7 +328,7 @@ int16 BoltEngine::openBooth(int16 sceneId) {
 	}
 
 	// Load animation descriptors
-	if (sceneId == 9) {
+	if (boothId == 9) {
 		for (int16 i = 0; i < g_boothNumAnimations; i++)
 			g_boothAnimDescs[i] = memberAddr(g_boothsBoltLib, 0x709 + i);
 	} else {
@@ -329,7 +340,7 @@ int16 BoltEngine::openBooth(int16 sceneId) {
 	}
 
 	// Load hotspot descriptors
-	if (sceneId == 9) {
+	if (boothId == 9) {
 		for (int16 i = 0; i < g_boothNumHotspots; i++)
 			g_boothHotspotDescs[i] = memberToRect(memberAddr(g_boothsBoltLib, 0x710 + i));
 	} else {
@@ -340,7 +351,7 @@ int16 BoltEngine::openBooth(int16 sceneId) {
 		}
 	}
 
-	if (sceneId == 9) {
+	if (boothId == 9) {
 		// Main entrance palette cycling data
 		g_boothPalCycleData = memberAddr(g_boothsBoltLib, 0x718);
 
@@ -348,9 +359,9 @@ int16 BoltEngine::openBooth(int16 sceneId) {
 		byte *sprite = memberAddr(g_boothsBoltLib, (g_displayMode != 0 ? 0x801 : 0x800) + (g_lettersWon << 8));
 		boltPict2Pict(&g_boothLetterSprite, sprite);
 
-		// Exit sign sprites
-		g_boothExitLeft = memberAddr(g_boothsBoltLib, 0x747);
-		g_boothExitRight = memberAddr(g_boothsBoltLib, 0x748);
+		// Visit sign sprite and its off variant
+		g_boothVisitSignOn = memberAddr(g_boothsBoltLib, 0x747);
+		g_boothVisitSignOff = memberAddr(g_boothsBoltLib, 0x748);
 	} else {
 		g_boothPalCycleData = memberAddr(g_boothsBoltLib, baseResId + 0x112);
 
@@ -372,6 +383,12 @@ int16 BoltEngine::openBooth(int16 sceneId) {
 	_xp->setCursorPos(g_cursorX, g_cursorY);
 
 	flushInput();
+
+	if (g_isDemo && boothId == 9) {
+		g_helpFlag = 1;
+		g_helpTimer = _xp->startTimer(500);
+	}
+
 	return boothEventLoop();
 }
 
@@ -388,9 +405,9 @@ void BoltEngine::playTour() {
 	int16 playing = 1;
 
 	_xp->hideCursor();
-	g_screensaverStep = 0;
+	g_tourStep = 0;
 
-	if (prepareAV(g_rtfHandle, 4, g_displayWidth, g_displayHeight, g_displayX, g_displayY)) {
+	if (prepareAV(g_rtfHandle, 4 - (g_isDemo ? 3 : 0), g_displayWidth, g_displayHeight, g_displayX, g_displayY)) {
 		while (playing) {
 			playing = maintainAV(0);
 			if (!playing)
@@ -400,17 +417,17 @@ void BoltEngine::playTour() {
 			int16 event = _xp->getEvent(etEmpty, &eventData);
 
 			switch (event) {
-			case etMouseDown: // Abort tour
+			case etMouseDown:
 				stopAV();
 				playing = 0;
 				break;
 
-			case etSound: // Sound marker
+			case etSound:
 				playing = maintainAV(1);
 				break;
 
-			case etTrigger: // Trigger marker from RTF stream
-				screensaverStep();
+			case etTrigger:
+				tourPaletteCycleStep();
 				break;
 			}
 		}
@@ -437,7 +454,7 @@ void BoltEngine::finishPlayingHelp(int16 activeHotspot) {
 	if (activeHotspot != 1)
 		restoreColors(1);
 
-	if (activeHotspot != 2 && g_keyHelp == 0)
+	if (activeHotspot != 2 && g_helpFlag == 0)
 		restoreColors(2);
 
 	g_keyLeft = 0;
@@ -470,7 +487,7 @@ int16 BoltEngine::hotSpotActive(int16 hotspot) {
 		return g_keyLeft;
 
 	case 2:
-		return (g_helpPlaying || g_keyHelp) ? 1 : 0;
+		return (g_helpPlaying || g_helpFlag) ? 1 : 0;
 
 	case 3:
 		return g_keyEnter;
@@ -526,9 +543,9 @@ int16 BoltEngine::boothEventLoop() {
 	g_hoveredHotspot = -1;
 	hoverHotSpot();
 
-	while (exitCode == 0 && !shouldQuit()) {
-		_xp->setInactivityTimer(30);
+	_xp->setInactivityTimer(30);
 
+	while (exitCode == 0 && !shouldQuit()) {
 		if (g_helpPlaying != 0) {
 			g_helpPlaying = maintainAudioPlay(0);
 			if (g_helpPlaying == 0)
@@ -540,7 +557,7 @@ int16 BoltEngine::boothEventLoop() {
 		case etTimer:
 			if (g_keyLeft == 0 && g_keyRight == 0 &&
 				g_keyUp == 0 && g_keyDown == 0 &&
-				g_keyEnter == 0 && g_keyHelp == 0)
+				g_keyEnter == 0 && g_helpFlag == 0)
 				break;
 
 			if (g_keyReleased != 0) {
@@ -557,7 +574,7 @@ int16 BoltEngine::boothEventLoop() {
 					restoreColors(5);
 				} else if (g_keyEnter != 0) {
 					restoreColors(3);
-				} else if (g_keyHelp != 0) {
+				} else if (g_helpFlag != 0) {
 					restoreColors(2);
 
 					if (g_helpPlaying != 0) {
@@ -581,7 +598,7 @@ int16 BoltEngine::boothEventLoop() {
 					setColors(5);
 				} else if (g_keyEnter != 0) {
 					setColors(3);
-				} else if (g_keyHelp != 0) {
+				} else if (g_helpFlag != 0) {
 					setColors(2);
 
 					if (g_helpPlaying != 0) {
@@ -593,7 +610,7 @@ int16 BoltEngine::boothEventLoop() {
 				}
 			}
 
-			g_keyTimer = _xp->startTimer(500);
+			g_helpTimer = _xp->startTimer(500);
 			break;
 
 		case etMouseMove:
@@ -638,17 +655,19 @@ int16 BoltEngine::boothEventLoop() {
 			if (g_helpPlaying != 0)
 				break;
 
-			if (g_keyHelp == 0) {
-				g_keyHelp = 1;
-				g_keyTimer = _xp->startTimer(500);
-				if (g_idleHelpAvailable != 0)
+			if (g_helpFlag == 0) {
+				g_helpFlag = 1;
+				g_helpTimer = _xp->startTimer(500);
+				if (g_idleHelpAudioAvailable != 0) {
+					_xp->setInactivityTimer(30);
 					continue;
+				}
 			} else {
-				if (g_idleHelpAvailable == 0)
+				if (g_idleHelpAudioAvailable == 0)
 					break;
 
-				if (startAnimation(g_rtfHandle, 0x19)) {
-					g_idleHelpAvailable = 0;
+				if (startAnimation(g_rtfHandle, g_isDemo ? 0x16 : 0x19)) {
+					g_idleHelpAudioAvailable = 0;
 					g_helpPlaying = 1;
 					g_helpIsIdle = 1;
 				}
@@ -661,9 +680,9 @@ int16 BoltEngine::boothEventLoop() {
 				break;
 
 			if (g_currentBoothScene == 9)
-				mapIdleAnimation();
+				mainEntranceHelpBlink();
 			else
-				boothIdleAnimation();
+				boothHelpBlink();
 
 			break;
 		}
@@ -678,21 +697,21 @@ int16 BoltEngine::boothEventLoop() {
 }
 
 void BoltEngine::resetInactivityState() {
-	if (g_keyTimer != 0) {
-		_xp->killTimer(g_keyTimer);
-		g_keyTimer = 0;
+	if (g_helpTimer != 0) {
+		_xp->killTimer(g_helpTimer);
+		g_helpTimer = 0;
 	}
 
-	g_idleHelpAvailable = 0;
+	g_idleHelpAudioAvailable = 0;
 	_xp->setInactivityTimer(30);
 
-	if (g_keyHelp != 0) {
+	if (g_helpFlag != 0) {
 		restoreColors(2);
 		if (g_currentBoothScene == 9)
 			restoreColors(8);
 	}
 
-	g_keyHelp = 0;
+	g_helpFlag = 0;
 }
 
 bool BoltEngine::handleButtonPress(int16 hotspot) {
@@ -718,25 +737,25 @@ bool BoltEngine::handleButtonPress(int16 hotspot) {
 		int16 animId;
 		switch (g_currentBoothScene) {
 		case 3:
-			animId = 0x18;
+			animId = g_isDemo ? 0x0F : 0x18;
 			break;
 		case 4:
-			animId = 0x12;
+			animId = g_isDemo ? 0x10 : 0x12;
 			break;
 		case 5:
-			animId = 0x13;
+			animId = g_isDemo ? 0x11 : 0x13;
 			break;
 		case 6:
-			animId = 0x14;
+			animId = g_isDemo ? 0x12 : 0x14;
 			break;
 		case 7:
-			animId = 0x15;
+			animId = g_isDemo ? 0x13 : 0x15;
 			break;
 		case 8:
-			animId = 0x16;
+			animId = g_isDemo ? 0x14 : 0x16;
 			break;
 		case 9:
-			animId = 0x17;
+			animId = g_isDemo ? 0x15 : 0x17;
 			break;
 		default:
 			animId = -1;
@@ -750,8 +769,8 @@ bool BoltEngine::handleButtonPress(int16 hotspot) {
 		if (ok) {
 			g_helpPlaying = 1;
 			setColors(2);
-			g_keyHelp = 0;
-			g_screensaverStep = 0;
+			g_helpFlag = 0;
+			g_tourStep = 0;
 		}
 
 		return false;
@@ -765,11 +784,15 @@ bool BoltEngine::handleButtonPress(int16 hotspot) {
 			{
 				byte *navTable = (hotspot == 3) ? savedLeftDoor : savedRightDoor;
 				int8 boothOffset = (int8)navTable[g_lettersWon % 3];
-				playAVOverBooth(boothOffset + 12);
+				playAVOverBooth(boothOffset + (g_isDemo ? 9 : 12));
 				return false;
 			}
 
 			case 5:
+				if (g_isDemo) {
+					return true;
+				}
+
 				_xp->hideCursor();
 				displayBooth(stBack);
 
@@ -804,8 +827,15 @@ bool BoltEngine::handleButtonPress(int16 hotspot) {
 				vDelete("TopCatBF");
 				vDelete("Yogi");
 
+				g_huckWins = 0;
+				g_fredWins = 0;
+				g_scoobyWins = 0;
+				g_yogiWins = 0;
+				g_georgeWins = 0;
+				g_topCatWins = 0;
+
 				displayBooth(stBack);
-				playAV(g_rtfHandle, 5,
+				playAV(g_rtfHandle, 5 - (g_isDemo ? 3 : 0),
 					   g_displayWidth, g_displayHeight,
 					   g_displayX, g_displayY);
 
@@ -831,7 +861,7 @@ bool BoltEngine::handleButtonPress(int16 hotspot) {
 		} else {
 			switch (hotspot) {
 			case 3:
-				startCycle(g_boothPalCycleData);
+				startCycle(g_boothSceneDesc);
 				_xp->startTimer(2000);
 				return true;
 
@@ -862,8 +892,9 @@ void BoltEngine::blastColors(byte **paletteTable, int16 index, int16 mode) {
 		si += 3;
 	}
 
-	if (mode == 1)
+	if (mode == 1) {
 		_xp->setPalette(count, startIndex, localPalette);
+	}
 }
 
 void BoltEngine::setColors(int16 index) {
@@ -886,7 +917,7 @@ void BoltEngine::setColors(int16 index) {
 			break;
 
 		case 8:
-			displayPic(g_boothExitLeft, 0, 0, 0);
+			displayPic(g_boothVisitSignOn, 0, 0, stFront);
 			_xp->updateDisplay();
 			break;
 		}
@@ -940,7 +971,7 @@ void BoltEngine::restoreColors(int16 index) {
 		}
 
 		case 8:
-			displayPic(g_boothExitRight, 0, 0, 0);
+			displayPic(g_boothVisitSignOff, 0, 0, stFront);
 			_xp->updateDisplay();
 			break;
 		}
@@ -1000,36 +1031,36 @@ void BoltEngine::shiftColorMap(byte *colorMap, int16 delta) {
 void BoltEngine::playBoothAV() {
 	switch (g_currentBoothScene) {
 	case 3:
-		playAVOverBooth(6);
+		playAVOverBooth(6 - (g_isDemo ? 3 : 0));
 		break;
 	case 4:
-		playAVOverBooth(7);
+		playAVOverBooth(7 - (g_isDemo ? 3 : 0));
 		break;
 	case 5:
-		playAVOverBooth(8);
+		playAVOverBooth(8 - (g_isDemo ? 3 : 0));
 		break;
 	case 6:
-		playAVOverBooth(9);
+		playAVOverBooth(9 - (g_isDemo ? 3 : 0));
 		break;
 	case 7:
-		playAVOverBooth(10);
+		playAVOverBooth(10 - (g_isDemo ? 3 : 0));
 		break;
 	case 8:
-		playAVOverBooth(11);
+		playAVOverBooth(11 - (g_isDemo ? 3 : 0));
 		break;
 	}
 }
 
-void BoltEngine::mapIdleAnimation() {
-	g_screensaverStep++;
+void BoltEngine::mainEntranceHelpBlink() {
+	g_tourStep++;
 
-	switch (g_screensaverStep) {
+	switch (g_tourStep) {
 	case 1:
 		setColors(0);
 		setColors(1);
 		g_keyLeft = 1;
 		g_keyReleased = 1;
-		g_keyTimer = _xp->startTimer(500);
+		g_helpTimer = _xp->startTimer(500);
 		break;
 
 	case 2:
@@ -1048,7 +1079,7 @@ void BoltEngine::mapIdleAnimation() {
 		setColors(7);
 		g_keyRight = 1;
 		g_keyReleased = 1;
-		g_keyTimer = _xp->startTimer(500);
+		g_helpTimer = _xp->startTimer(500);
 		break;
 
 	case 4:
@@ -1063,7 +1094,7 @@ void BoltEngine::mapIdleAnimation() {
 		setColors(6);
 		g_keyUp = 1;
 		g_keyReleased = 1;
-		g_keyTimer = _xp->startTimer(500);
+		g_helpTimer = _xp->startTimer(500);
 		break;
 
 	case 6:
@@ -1078,13 +1109,13 @@ void BoltEngine::mapIdleAnimation() {
 		setColors(5);
 		g_keyDown = 1;
 		g_keyReleased = 1;
-		g_keyTimer = _xp->startTimer(500);
+		g_helpTimer = _xp->startTimer(500);
 		break;
 
 	case 8:
 		g_keyDown = 0;
 		g_keyReleased = 0;
-		g_keyTimer = 0;
+		g_helpTimer = 0;
 
 		if (!g_boothHotspotDescs[5].contains(g_cursorX, g_cursorY))
 			restoreColors(5);
@@ -1092,15 +1123,15 @@ void BoltEngine::mapIdleAnimation() {
 	}
 }
 
-void BoltEngine::boothIdleAnimation() {
-	g_screensaverStep++;
+void BoltEngine::boothHelpBlink() {
+	g_tourStep++;
 
-	switch (g_screensaverStep) {
+	switch (g_tourStep) {
 	case 1:
 		setColors(3);
 		g_keyEnter = 1;
 		g_keyReleased = 1;
-		g_keyTimer = _xp->startTimer(500);
+		g_helpTimer = _xp->startTimer(500);
 		break;
 
 	case 2:
@@ -1117,13 +1148,13 @@ void BoltEngine::boothIdleAnimation() {
 		setColors(1);
 		g_keyLeft = 1;
 		g_keyReleased = 1;
-		g_keyTimer = _xp->startTimer(500);
+		g_helpTimer = _xp->startTimer(500);
 		break;
 
 	case 4:
 		g_keyLeft = 0;
 		g_keyReleased = 0;
-		g_keyTimer = 0;
+		g_helpTimer = 0;
 
 		if (!g_boothHotspotDescs[0].contains(g_cursorX, g_cursorY))
 			restoreColors(0);
@@ -1135,20 +1166,20 @@ void BoltEngine::boothIdleAnimation() {
 	}
 }
 
-void BoltEngine::screensaverStep() {
+void BoltEngine::tourPaletteCycleStep() {
 	static const int16 cycleResIds[] = {
 		0x700, 0x719, 0x71E, 0x720, 0x725, 0x72A, 0x72F, 0x731,
 		-1,    0x736, 0x73B, 0x73F, 0x743, -1
 	};
 
-	g_screensaverStep++;
+	g_tourStep++;
 
-	if (!(g_screensaverStep & 1)) {
+	if ((g_tourStep & 1) == 0) {
 		_xp->stopCycle();
 		return;
 	}
 
-	int16 index = g_screensaverStep >> 1;
+	int16 index = g_tourStep >> 1;
 	if (index > 13)
 		return;
 
@@ -1208,6 +1239,9 @@ void BoltEngine::flushInput() {
 int16 BoltEngine::winALetter(int16 prevBooth) {
 	g_lettersWon++;
 
+	if (g_isDemo && g_lettersWon == 6)
+		return 18;
+
 	if (g_lettersWon == 15) {
 		if (g_allLettersWonFlag) {
 			g_allLettersWonFlag = false;
@@ -1217,7 +1251,8 @@ int16 BoltEngine::winALetter(int16 prevBooth) {
 		}
 	}
 
-	playAV(g_rtfHandle, g_lettersWon + 31, g_displayWidth, g_displayHeight, g_displayX, g_displayY);
+	int16 avIndex = g_isDemo ? 25 : 31;
+	playAV(g_rtfHandle, g_lettersWon + avIndex, g_displayWidth, g_displayHeight, g_displayX, g_displayY);
 
 	if (g_lettersWon >= 15)
 		g_lettersWon = 0;
@@ -1225,20 +1260,60 @@ int16 BoltEngine::winALetter(int16 prevBooth) {
 	// Return the booth front ID for the game just played
 	switch (prevBooth) {
 	case 10:
+		if (g_isDemo) {
+			g_huckWins++;
+		}
+
 		return 3; // HuckGame    -> HucksBooth
 	case 11:
+		if (g_isDemo) {
+			g_fredWins++;
+		}
+
 		return 4; // FredGame    -> FredsBooth
 	case 12:
+		if (g_isDemo) {
+			g_scoobyWins++;
+		}
+
 		return 5; // ScoobyGame  -> ScoobysBooth
 	case 13:
+		if (g_isDemo) {
+			g_yogiWins++;
+		}
+
 		return 6; // YogiGame    -> YogisBooth
 	case 14:
+		if (g_isDemo) {
+			g_georgeWins++;
+		}
+
 		return 7; // GeorgeGame  -> GeorgesBooth
 	case 15:
+		if (g_isDemo) {
+			g_topCatWins++;
+		}
+
 		return 8; // TopCatGame  -> TopCatsBooth
 	default:
 		return 9; // MainEntrance
 	}
 }
 
+int16 BoltEngine::endDemo(int16 prevBooth) {
+	_xp->hideCursor();
+	_xp->setTransparency(false);
+
+	int16 baseId = (g_displayMode != 0) ? 0x0801 : 0x0800;
+	int16 memberId = baseId + (g_lettersWon << 8);
+	displayPic(getBOLTMember(g_boothsBoltLib, memberId), g_displayX, g_displayY, stFront);
+	displayColors(getBOLTMember(g_boothsBoltLib, 0x718), stFront, 0);
+
+	_xp->updateDisplay();
+
+	fadeToBlack(32);
+
+	return 0;
+}
+
 } // End of namespace Bolt
diff --git a/engines/bolt/booths/fred.cpp b/engines/bolt/booths/fred.cpp
index d0e5b38e0bf..e4a4a69c534 100644
--- a/engines/bolt/booths/fred.cpp
+++ b/engines/bolt/booths/fred.cpp
@@ -23,6 +23,1246 @@
 
 namespace Bolt {
 
+int16 BoltEngine::fredGame(int16 prevBooth) {
+	int16 savedTimer = _xp->setInactivityTimer(30);
 
+	if (!initFred()) {
+		cleanUpFred();
+		return 4; // FredsBooth
+	}
+
+	int16 result = playFred();
+	cleanUpFred();
+
+	_xp->setInactivityTimer(savedTimer);
+	return result;
+}
+
+bool BoltEngine::initFred() {
+	const char *path = assetPath("fred.blt");
+
+	if (!openBOLTLib(&g_fredBoltLib, &g_fredBoltCallbacks, path))
+		return false;
+
+	if (!getBOLTGroup(g_fredBoltLib, 0, 1))
+		return false;
+
+	// Load assets...
+	g_fredBackground = memberAddr(g_fredBoltLib, (g_displayMode != 0) ? 5 : 4);
+
+	g_fredBalloonString = memberAddr(g_fredBoltLib, 6);
+
+	g_fredFacingLeftRect = memberAddr(g_fredBoltLib, 0);
+	g_fredFacingRightRect = memberAddr(g_fredBoltLib, 1);
+	g_fredTurningRect = memberAddr(g_fredBoltLib, 2);
+	g_fredBalloonRect = memberAddr(g_fredBoltLib, 3);
+
+	g_fredHelpEntries = memberAddr(g_fredBoltLib, 0x38);
+	g_fredPlayButton = memberAddr(g_fredBoltLib, 0x39);
+	g_fredTimer = 0;
+
+	for (int i = 0; i < 10; i++)
+		g_fredSprites[i] = memberAddr(g_fredBoltLib, 0x28 + i);
+
+	getFredSoundInfo(g_fredBoltLib, 0x3A, &g_fredSounds[0]);
+	getFredSoundInfo(g_fredBoltLib, 0x3B, &g_fredSounds[1]);
+	getFredSoundInfo(g_fredBoltLib, 0x3C, &g_fredSounds[2]);
+	getFredSoundInfo(g_fredBoltLib, 0x3D, &g_fredSounds[3]);
+
+	g_fredLoopSound = nullptr;
+	g_fredCurrentSound = nullptr;
+	g_fredPendingLoop = nullptr;
+	g_fredPendingOneShot = nullptr;
+
+	// Init Fred sprite struct...
+	g_fredSprite.flags = 1;
+	g_fredSprite.direction = 0;
+	g_fredSprite.xPos = 0xA000; // Fixed-point coordinate
+	g_fredSprite.yPos = 0x1600;	// Fixed-point coordinate
+	g_fredSprite.speed = 0x600;
+	g_fredSprite.pathTable = nullptr;
+
+	setFredAnimMode(&g_fredSprite, 0);
+
+	// Load save data...
+	if (!vLoad(&g_fredSaveData, g_fredSaveFile)) {
+		g_fredSaveData[0] = 0; // Balloon catch count
+		g_fredSaveData[1] = 0; // Level data group index, increments on level complete, caps at 9
+		g_fredSaveData[2] = 0; // Palette group index, increments on level complete, wraps at 10
+	}
+
+	if (!initFredLevel(g_fredSaveData[1], g_fredSaveData[2]))
+		return false;
+
+	g_fredLevelIndex = 0;
+	while (g_fredLevelIndex < g_fredSaveData[0]) {
+		FredEntityState *entry = g_fredEntitiesTable[g_fredLevelIndex + 1];
+		setFredAnimMode(entry, 10);
+		g_fredLevelIndex++;
+	}
+
+	// Flush timer events...
+	uint32 dummy;
+	while (_xp->getEvent(etTimer, &dummy) != etTimer);
+
+	_xp->stopCycle();
+	_xp->setTransparency(false);
+
+	// Display background on back and front buffers...
+	displayColors(g_fredPalette, stBack, 0);
+	displayPic(g_fredBackground, g_displayX, g_displayY, stFront);
+	_xp->updateDisplay();
+
+	_xp->setTransparency(true);
+	displayColors(g_fredPalette, stFront, 0);
+	displayColors(g_fredPalette, stBack, 1);
+	displayPic(g_fredBackground, g_displayX, g_displayY, stBack);
+
+	renderFredScene();
+	_xp->updateDisplay();
+
+	_xp->startCycle(g_fredCycleSpecs);
+	_xp->setFrameRate(15);
+
+	return true;
+}
+
+void BoltEngine::cleanUpFred() {
+	int16 savedLevel = g_fredSaveData[1]; // Level data group index
+	int16 savedPalette = g_fredSaveData[2]; // Palette group index
+
+	g_fredSaveData[0] = g_fredLevelIndex;
+
+	byte *levelPtr = g_fredLevelPtr;
+	if (READ_UINT16(levelPtr + 0x0A) <= g_fredLevelIndex) {
+		g_fredSaveData[1]++;
+		if (g_fredSaveData[1] >= 10)
+			g_fredSaveData[1] = 9;
+
+		g_fredSaveData[2]++;
+		if (g_fredSaveData[2] >= 10)
+			g_fredSaveData[2] = 0;
+
+		g_fredSaveData[0] = 0;
+	}
+
+	vSave(&g_fredSaveData, sizeof(g_fredSaveData), g_fredSaveFile);
+
+	_xp->stopCycle();
+	termFredLevel(savedLevel, savedPalette);
+
+	freeBOLTGroup(g_fredBoltLib, 0, 1);
+	closeBOLTLib(&g_fredBoltLib);
+
+	_xp->setFrameRate(0);
+	_xp->fillDisplay(0, stFront);
+	_xp->updateDisplay();
+}
+
+bool BoltEngine::initFredLevel(int16 levelGroup, int16 palGroup) {
+	// Load level data group...
+	if (!getBOLTGroup(g_fredBoltLib, levelGroup * 0x200 + 0x100, 1))
+		return false;
+
+	// Load palette group...
+	if (!getBOLTGroup(g_fredBoltLib, palGroup * 0x200 + 0x200, 1)) {
+		freeBOLTGroup(g_fredBoltLib, levelGroup * 0x200 + 0x100, 1);
+		return false;
+	}
+
+	// Level descriptor member...
+	g_fredLevelPtr = memberAddr(g_fredBoltLib, levelGroup * 0x200 + 0x100);
+
+	// Palette member...
+	g_fredPalette = memberAddr(g_fredBoltLib, palGroup * 0x200 + 0x200);
+
+	// Cycle data...
+	g_fredCycleRaw = memberAddr(g_fredBoltLib, palGroup * 0x200 + 0x20A);
+	boltCycleToXPCycle(g_fredCycleRaw, g_fredCycleSpecs);
+
+	// Other resources...
+	g_fredBalloonSprite = memberAddr(g_fredBoltLib, palGroup * 0x200 + 0x209);
+	g_fredPathMatrix = memberAddr(g_fredBoltLib, levelGroup * 0x200 + 0x132);
+	g_fredRowBounds = memberAddr(g_fredBoltLib, levelGroup * 0x200 + 0x101);
+
+	// Allocate shuffle table: numRows * (numCols + 1)
+	uint16 numRows = READ_UINT16(g_fredLevelPtr + 4);
+	uint16 numCols = READ_UINT16(g_fredLevelPtr + 6);
+
+	g_fredShuffleTable = (byte *)_xp->allocMem(numRows * (numCols + 1));
+	if (!g_fredShuffleTable) {
+		termFredLevel(levelGroup, palGroup);
+		return false;
+	}
+
+	// Build and shuffle column indices for each row...
+	for (int16 row = 0; row < numRows; row++) {
+		numCols = READ_UINT16(g_fredLevelPtr + 6);
+		int16 stride = numCols + 1;
+		int16 rowBase = stride * row;
+		g_fredShuffleTable[rowBase] = 0;
+
+		for (int16 col = 0; col < numCols; col++) {
+			g_fredShuffleTable[rowBase + col + 1] = (byte)col;
+		}
+
+		// Fisher-Yates shuffle...
+		for (int16 col = 0; col < numCols; col++) {
+			int16 randIdx = _xp->getRandom(numCols);
+			int16 posA = rowBase + col + 1;
+			int16 posB = rowBase + randIdx + 1;
+
+			byte tmp = g_fredShuffleTable[posA];
+			g_fredShuffleTable[posA] = g_fredShuffleTable[posB];
+			g_fredShuffleTable[posB] = tmp;
+		}
+	}
+
+	// Allocate balloon table...
+	uint16 numBalloons = READ_UINT16(g_fredLevelPtr + 0x0A);
+	g_fredEntitiesTable = (FredEntityState **)_xp->allocMem((numBalloons + 2) * sizeof(FredEntityState *));
+	if (!g_fredEntitiesTable) {
+		termFredLevel(levelGroup, palGroup);
+		return false;
+	}
+
+	// Allocate each balloon state struct...
+	for (int16 i = 0; i < numBalloons; i++) {
+		FredEntityState *entry = new FredEntityState();
+
+		if (!entry) {
+			termFredLevel(levelGroup, palGroup);
+			return false;
+		}
+
+		entry->flags = 0;
+		g_fredEntitiesTable[i + 1] = entry;
+	}
+
+	// Sentinel: null pointer after last balloon...
+	g_fredEntitiesTable[numBalloons + 1] = nullptr;
+
+	// First entry points to Fred's sprite...
+	g_fredEntitiesTable[0] = &g_fredSprite;
+
+	// Allocate balloon type shuffle array...
+	uint16 numTypes = READ_UINT16(g_fredLevelPtr + 0x0C);
+	byte *typeShuf = (byte *)_xp->allocMem(numTypes);
+	if (!typeShuf) {
+		termFredLevel(levelGroup, palGroup);
+		return false;
+	}
+
+	for (int16 i = 0; i < numTypes; i++) {
+		typeShuf[i] = (byte)i;
+	}
+
+	// Reshuffle every numTypes balloons...
+	for (int16 i = 0; i < numBalloons; i++) {
+		if ((i % numTypes) == 0) {
+			for (int16 j = 0; j < numTypes; j++) {
+				int16 randIdx = _xp->getRandom(numTypes);
+				byte tmp = typeShuf[j];
+				typeShuf[j] = typeShuf[randIdx];
+				typeShuf[randIdx] = tmp;
+			}
+		}
+
+		// Assign balloon type...
+		byte balloonType = typeShuf[i % numTypes] + 0x0B;
+		FredEntityState *balloonEntry = g_fredEntitiesTable[i + 1];
+		setFredAnimMode(balloonEntry, balloonType);
+	}
+
+	_xp->freeMem(typeShuf);
+
+	g_fredBalloonSpawnDelay = calcBalloonSpawnDelay();
+	g_fredBalloonSearchIdx = 0;
+	g_fredLevelIndex = 0;
+
+	return true;
+}
+
+void BoltEngine::termFredLevel(int16 levelGroup, int16 palGroup) {
+	// Free balloon state structs...
+	if (g_fredEntitiesTable) {
+		uint16 numBalloons = READ_UINT16(g_fredLevelPtr + 0x0A);
+		for (int16 i = 0; i < numBalloons; i++) {
+			FredEntityState *entry = g_fredEntitiesTable[i + 1];
+			if (entry) {
+				delete entry;
+				g_fredEntitiesTable[i + 1] = nullptr;
+			}
+		}
+
+		delete[] g_fredEntitiesTable;
+		g_fredEntitiesTable = nullptr;
+	}
+
+	// Free shuffle table...
+	if (g_fredShuffleTable) {
+		_xp->freeMem(g_fredShuffleTable);
+		g_fredShuffleTable = nullptr;
+	}
+
+	// Free BOLT groups...
+	freeBOLTGroup(g_fredBoltLib, palGroup * 0x200 + 0x200, 1);
+	freeBOLTGroup(g_fredBoltLib, levelGroup * 0x200 + 0x100, 1);
+}
+
+void BoltEngine::swapFredAnimEntry() {
+	byte *data = g_boltCurrentMemberEntry->dataPtr;
+	uint32 decompSize = g_boltCurrentMemberEntry->decompSize;
+	uint32 offset = 0;
+
+	while (offset < decompSize) {
+		WRITE_UINT16(data + offset + 4, READ_BE_INT16(data + offset + 4));
+		resolveIt((uint32 *)(data + offset));
+		offset += 6;
+	}
+}
+
+void BoltEngine::swapFredAnimDesc() {
+	byte *data = g_boltCurrentMemberEntry->dataPtr;
+	uint32 decompSize = g_boltCurrentMemberEntry->decompSize;
+	uint32 offset = 0;
+	byte *ptr = data;
+	byte *colorCountPtr = data + 0x0E;
+
+	while (offset < decompSize) {
+		WRITE_UINT32(ptr + 0x00, READ_BE_INT32(ptr + 0x00));
+		WRITE_UINT32(ptr + 0x08, READ_BE_INT32(ptr + 0x08));
+		WRITE_UINT16(ptr + 0x0C, READ_BE_INT16(ptr + 0x0C));
+		WRITE_UINT16(colorCountPtr, READ_BE_INT16(colorCountPtr));
+
+		uint16 colorCount = READ_UINT16(colorCountPtr);
+		unpackColors(colorCount, ptr + 0x10);
+		unpackColors(colorCount, ptr + 0x2C);
+
+		resolveIt((uint32 *)(ptr + 0x04));
+
+		offset += 0x48;
+		colorCountPtr += 0x48;
+		ptr += 0x48;
+	}
+}
+
+void BoltEngine::swapFredLevelDesc() {
+	byte *data = g_boltCurrentMemberEntry->dataPtr;
+
+	WRITE_UINT16(data + 0x00, READ_BE_INT16(data + 0x00));
+	WRITE_UINT16(data + 0x02, READ_BE_INT16(data + 0x02));
+	WRITE_UINT16(data + 0x04, READ_BE_INT16(data + 0x04));
+	WRITE_UINT16(data + 0x06, READ_BE_INT16(data + 0x06));
+	WRITE_UINT16(data + 0x08, READ_BE_INT16(data + 0x08));
+	WRITE_UINT16(data + 0x0A, READ_BE_INT16(data + 0x0A));
+	WRITE_UINT16(data + 0x0C, READ_BE_INT16(data + 0x0C));
+}
+
+int16 BoltEngine::playFred() {
+	int16 result = 0;
+	int16 allCaught = 0;
+	int16 exitLoop = 0;
+	int16 joystickX = 0;
+
+	// Show help on first play...
+	if (g_fredShowHelp != 0) {
+		if (!helpFred())
+			return 4;
+	}
+
+	_xp->setInactivityTimer(30);
+	_xp->enableController();
+
+	while (!exitLoop) {
+		if (joystickX != 0)
+			_xp->setInactivityTimer(30);
+
+		// Process events...
+		int16 eventType;
+		uint32 eventData;
+		while ((eventType = _xp->getEvent(0, &eventData)) != 0) {
+			switch (eventType) {
+			case etJoystick:
+				joystickX = (int16)(eventData >> 16);
+				break;
+			case etMouseDown:
+			case etInactivity:
+				if (!allCaught) {
+					int16 helpMode = (g_fredSprite.animMode == 8 || g_fredSprite.animMode == 6) ? 1 : 0;
+					setFredAnimMode(&g_fredSprite, helpMode);
+					if (!helpFred()) {
+						result = 4;
+						exitLoop = 1;
+					}
+				}
+
+				break;
+			case etSound:
+				updateFredSound();
+				break;
+			default:
+				break;
+			}
+		}
+
+		if (exitLoop)
+			break;
+
+		// Render and update...
+		renderFredScene();
+		_xp->updateDisplay();
+
+		// Balloon spawn timer...
+		g_fredBalloonSpawnDelay--;
+		if (g_fredBalloonSpawnDelay == 0) {
+			if (spawnBalloon())
+				g_fredBalloonSpawnDelay = calcBalloonSpawnDelay();
+			else
+				g_fredBalloonSpawnDelay = 1;
+		}
+
+		// --- First pass: update Fred and balloons animations ---
+		int16 idx = 0;
+		FredEntityState *entry = g_fredEntitiesTable[idx];
+		while (entry) {
+			if (entry->flags & 1) { // active
+				// Save previous position...
+				entry->prevXPos = entry->xPos;
+				entry->prevYPos = entry->yPos;
+
+				// Store joystick direction...
+				entry->direction = joystickX;
+
+				// Clear frame-changed and anim-ended flags...
+				entry->flags &= 0xFFF9;
+
+				// Advance frame countdown...
+				int16 countdown = entry->frameCountdown - 1;
+				entry->frameCountdown = countdown;
+
+				if (countdown == 0) {
+					byte *animTable = entry->animTable;
+					int16 frameIdx = entry->frameIndex + 1;
+					entry->frameIndex = frameIdx;
+
+					// Check if next frame exists...
+					byte *nextSprite = getResolvedPtr(animTable, frameIdx * 6);
+					if (!nextSprite) {
+						// Loop back to frame 0...
+						entry->frameIndex = 0;
+						entry->flags |= 4; // set anim-ended
+					}
+
+					// Read new frame duration...
+					entry->frameCountdown = READ_UINT16(animTable + entry->frameIndex * 6 + 4);
+					entry->flags |= 2; // frame-changed
+				}
+
+				// Mode-specific behavior...
+				int16 mode = entry->animMode;
+				switch (mode) {
+				case 0: // Fred idle facing right
+					if (allCaught) {
+						int16 newMode = (entry->xPos < 0x8200) ? 7 : 4;
+						setFredAnimMode(entry, newMode);
+					} else {
+						int16 dir = entry->direction;
+						if (dir == 1)
+							setFredAnimMode(entry, 4); // run right
+						else if (dir == -1)
+							setFredAnimMode(entry, 7); // turn to run left
+					}
+
+					break;
+				case 2: // Fred idle facing right (variant)
+					if (allCaught) {
+						int16 newMode = (entry->xPos < 0x8200) ? 7 : 4;
+						setFredAnimMode(entry, newMode);
+					} else {
+						int16 dir = entry->direction;
+						if (dir == 1)
+							setFredAnimMode(entry, 4);
+						else if (dir == -1)
+							setFredAnimMode(entry, 7);
+					}
+
+					break;
+				case 1: // Fred idle facing left
+					if (allCaught) {
+						int16 newMode = (entry->xPos < 0x8200) ? 8 : 3;
+						setFredAnimMode(entry, newMode);
+					} else {
+						int16 dir = entry->direction;
+						if (dir == -1)
+							setFredAnimMode(entry, 8); // run left
+						else if (dir == 1)
+							setFredAnimMode(entry, 3); // turn to run right
+					}
+
+					break;
+				case 6: // Fred idle facing left (variant)
+					if (allCaught) {
+						int16 newMode = (entry->xPos < 0x8200) ? 8 : 3;
+						setFredAnimMode(entry, newMode);
+					} else {
+						int16 dir = entry->direction;
+						if (dir == -1)
+							setFredAnimMode(entry, 8);
+						else if (dir == 1)
+							setFredAnimMode(entry, 3);
+					}
+
+					break;
+				case 3: // Turn right->left
+					if (entry->flags & 4) // anim ended
+						setFredAnimMode(entry, 4);
+
+					break;
+				case 7: // Turn left->right
+					if (entry->flags & 4)
+						setFredAnimMode(entry, 8);
+
+					break;
+				case 4: // Fred running right (stopped)
+					if (!allCaught) {
+						int16 dir = entry->direction;
+						if (dir == -1)
+							setFredAnimMode(entry, 7);
+						else if (dir == 0)
+							setFredAnimMode(entry, 2); // stop facing right
+					}
+
+					break;
+				case 8: // Fred running left (stopped)
+					if (!allCaught) {
+						int16 dir = entry->direction;
+						if (dir == 1)
+							setFredAnimMode(entry, 3);
+						else if (dir == 0)
+							setFredAnimMode(entry, 6); // stop facing left
+					}
+
+					break;
+				case 5: // ?
+					if (entry->flags & 4)
+						setFredAnimMode(entry, 2);
+
+					break;
+				case 9: // ?
+					if (entry->flags & 4)
+						setFredAnimMode(entry, 6);
+
+					break;
+				}
+			}
+
+			idx++;
+			entry = g_fredEntitiesTable[idx];
+		}
+
+		// --- Second pass: move balloons and Fred ---
+		idx = 0;
+		entry = g_fredEntitiesTable[idx];
+		while (entry) {
+			if (entry->flags & 1) {
+				int16 mode = entry->animMode;
+
+				if (mode == 4) {
+					// Running right
+					int32 speed = (int32)entry->speed;
+					int32 xPos = entry->xPos + speed;
+					entry->xPos = xPos;
+
+					if (!allCaught) {
+						if (xPos > 0x11D00) {
+							entry->xPos = 0x11D00;
+							setFredAnimMode(entry, 2); // stop at right edge
+						}
+					} else {
+						if (xPos > 0x18000)
+							exitLoop = 1; // ran off right side
+					}
+
+				} else if (mode == 8) {
+					// Running left
+					int32 speed = (int32)entry->speed;
+					int32 xPos = entry->xPos - speed;
+					entry->xPos = xPos;
+
+					if (!allCaught) {
+						if (xPos < 0) {
+							entry->xPos = 0;
+							setFredAnimMode(entry, 6); // stop at left edge
+						}
+					} else {
+						if (xPos < (int32)0xFFFF9400)
+							exitLoop = 1; // ran off left side
+					}
+
+				} else if (mode == 0x0B) {
+					// Balloon floating...
+					int16 pathIdx = entry->pathIndex + 1;
+					entry->pathIndex = pathIdx;
+
+					byte *pathTable = entry->pathTable;
+					int16 pathCount = READ_UINT16(pathTable);
+
+					if (pathIdx >= pathCount) {
+						// Balloon escaped, deactivate...
+						entry->flags &= 0xFFFE;
+					} else {
+						// Update position from path table...
+						byte *pathEntry = pathTable + pathIdx * 4;
+						int32 newX = ((int32)READ_UINT16(pathEntry + 2)) << 8;
+						entry->xPos = newX;
+						int32 newY = ((int32)READ_UINT16(pathEntry + 4)) << 8;
+						entry->yPos = newY;
+					}
+				}
+			}
+
+			idx++;
+			entry = g_fredEntitiesTable[idx];
+		}
+
+		// --- Skip collision check if all caught ---
+		if (allCaught)
+			continue;
+
+		// --- Get Fred's collision rect based on current mode ---
+		byte *fredRect;
+		switch (g_fredSprite.animMode) {
+		case 6:
+		case 8:
+			fredRect = g_fredFacingLeftRect; // facing/running left
+			break;
+		case 2:
+		case 4:
+			fredRect = g_fredFacingRightRect; // facing/running right
+			break;
+		case 7:
+			fredRect = g_fredTurningRect; // turning left
+			break;
+		case 3:
+			fredRect = g_fredTurningRect; // turning right
+			break;
+		default:
+			fredRect = nullptr;
+			break;
+		}
+
+		if (!fredRect)
+			continue;
+
+		// Build Fred's bounding rect...
+		int16 fredX = ((int32)g_fredSprite.xPos >> 8) + READ_UINT16(fredRect);
+		int16 fredY = ((int32)g_fredSprite.yPos >> 8) + READ_UINT16(fredRect + 2);
+		int16 fredW = READ_UINT16(fredRect + 4);
+		int16 fredH = READ_UINT16(fredRect + 6);
+
+		// --- Third pass: check collisions with balloons ---
+		idx = 0;
+		entry = g_fredEntitiesTable[idx];
+		while (entry) {
+			if ((entry->flags & 1) && entry->animMode == 0x0B) {
+				// Build balloon bounding rect...
+				int16 bx = ((int32)entry->xPos >> 8) + READ_UINT16(g_fredBalloonRect);
+				int16 by = ((int32)entry->yPos >> 8) + READ_UINT16(g_fredBalloonRect + 2);
+				int16 bw = READ_UINT16(g_fredBalloonRect + 4);
+				int16 bh = READ_UINT16(g_fredBalloonRect + 6);
+
+				// {x, y, w, h} rect overlap test...
+				Common::Rect balloonRect(bx, by, bx + bw, by + bh);
+				Common::Rect playerRect(fredX, fredY, fredX + fredW, fredY + fredH);
+
+				if (playerRect.intersects(balloonRect)) {
+					// Caught a balloon!
+					int16 catchMode;
+					if (g_fredSprite.animMode == 6 || g_fredSprite.animMode == 8 || g_fredSprite.animMode == 7)
+						catchMode = 9; // catch facing left
+					else
+						catchMode = 5; // catch facing right
+
+					setFredAnimMode(&g_fredSprite, catchMode);
+					setFredAnimMode(entry, 0x0A); // balloon disappearing
+
+					g_fredLevelIndex++;
+					if (g_fredLevelIndex >= READ_UINT16(g_fredLevelPtr + 0x0A)) {
+						allCaught = 1;
+						result = 0x10;
+					}
+
+					// Reset spawn timer...
+					if (spawnBalloon())
+						g_fredBalloonSpawnDelay = calcBalloonSpawnDelay();
+					else
+						g_fredBalloonSpawnDelay = 1;
+
+					break; // Only catch one per frame...
+				}
+			}
+
+			idx++;
+			entry = g_fredEntitiesTable[idx];
+		}
+	}
+
+	_xp->stopSound();
+	_xp->disableController();
+	return result;
+}
+
+int16 BoltEngine::helpFred() {
+	byte *firstEntry = getResolvedPtr(g_fredHelpEntries, 0);
+	byte *picDesc = getResolvedPtr(firstEntry, 4);
+
+	int16 cursorX = READ_UINT16(picDesc + 6) + READ_UINT16(picDesc + 0x0A) - 10;
+	int16 cursorY = READ_UINT16(picDesc + 8) + READ_UINT16(picDesc + 0x0C) - 10;
+
+	byte *playableEntry = nullptr;
+	int16 exitResult = -1;
+	int16 isPlaying = 0;
+	int16 soundPending = 0;
+
+	_xp->setFrameRate(0);
+	_xp->setInactivityTimer(0);
+	_xp->stopSound();
+
+	g_fredLoopSound = nullptr;
+	g_fredCurrentSound = nullptr;
+	g_fredPendingLoop = nullptr;
+	g_fredPendingOneShot = nullptr;
+
+	renderFredScene();
+
+	// Display all help entry pics on front surface...
+	int16 off = 0;
+	byte *entry;
+	while (true) {
+		entry = getResolvedPtr(g_fredHelpEntries, off);
+		if (!entry)
+			break;
+		byte *pic = getResolvedPtr(entry, 4);
+		displayPic(pic, 0, 0, stFront);
+		off += 4;
+	}
+
+	_xp->updateDisplay();
+
+	// Save current palette for each entry, find playable entry...
+	off = 0;
+	while (true) {
+		entry = getResolvedPtr(g_fredHelpEntries, off);
+		if (!entry)
+			break;
+
+		_xp->getPalette(READ_UINT16(entry + 0x0C), READ_UINT16(entry + 0x0E), entry + 0x2C);
+		WRITE_UINT32(entry + 8, READ_UINT32(entry + 8) & 0xFFFFFFFE); // clear highlight flag
+
+		if (READ_UINT32(entry) == 2)
+			playableEntry = entry;
+
+		off += 4;
+	}
+
+	// Setup cursor...
+	_xp->setCursorPos(cursorX, cursorY);
+	_xp->setCursorColor(255, 255, 0);
+	_xp->disableController();
+	_xp->showCursor();
+
+	// Highlight first entry...
+	g_fredHoveredEntry = getResolvedPtr(g_fredHelpEntries, 0);
+	hiliteFredHelpObject(getResolvedPtr(g_fredHelpEntries, 0), 1);
+
+	// Main help loop
+	while (exitResult == -1) {
+		// Handle audio playback...
+		if (isPlaying) {
+			if (!maintainAudioPlay(soundPending)) {
+				int16 highlight = (playableEntry == g_fredHoveredEntry) ? 1 : 0;
+				hiliteFredHelpObject(playableEntry, highlight);
+				isPlaying = 0;
+			}
+			soundPending = 0;
+		}
+
+		// Process events...
+		uint32 eventData;
+		int16 eventType = _xp->getEvent(0, &eventData);
+
+		switch (eventType) {
+		case etTimer: {
+			// Timer expired, toggle selected entry highlight...
+			if (!g_fredCurrentHelpObject)
+				break;
+
+			if (g_fredTimer != eventData)
+				break;
+
+			g_fredTimer = _xp->startTimer(500);
+
+			byte *sel = g_fredCurrentHelpObject;
+			int16 highlighted = (READ_UINT32(sel + 8) & 1) ? 0 : 1;
+			hiliteFredHelpObject(sel, highlighted);
+			break;
+		}
+		case etMouseMove: {
+			cursorX = (int16)(eventData >> 16);
+			cursorY = (int16)(eventData & 0xFFFF);
+
+			// Hit test against all help entries...
+			off = 0;
+			byte *hitEntry = nullptr;
+			while (true) {
+				entry = getResolvedPtr(g_fredHelpEntries, off);
+				if (!entry)
+					break;
+
+				byte *pd = getResolvedPtr(entry, 4);
+				int16 ex = READ_UINT16(pd + 6);
+				int16 ey = READ_UINT16(pd + 8);
+				int16 ew = READ_UINT16(pd + 0x0A);
+				int16 eh = READ_UINT16(pd + 0x0C);
+
+				if (cursorX >= ex && cursorX < ex + ew &&
+					cursorY >= ey && cursorY < ey + eh) {
+					hitEntry = entry;
+					break;
+				}
+				off += 4;
+			}
+
+			// Update hover state...
+			if (hitEntry == g_fredHoveredEntry)
+				break;
+
+			// Unhighlight previous...
+			if (g_fredHoveredEntry &&
+				g_fredHoveredEntry != g_fredCurrentHelpObject &&
+				!(g_fredHoveredEntry == playableEntry && isPlaying)) {
+				hiliteFredHelpObject(g_fredHoveredEntry, 0);
+			}
+
+			g_fredHoveredEntry = hitEntry;
+
+			// Highlight new...
+			if (hitEntry && hitEntry != g_fredCurrentHelpObject) {
+				hiliteFredHelpObject(hitEntry, 1);
+			}
+
+			break;
+		}
+		case etMouseDown: {
+			int16 justStopped = 0;
+
+			// Stop current animation if playing...
+			if (isPlaying) {
+				if (g_fredTimer) {
+					_xp->killTimer(g_fredTimer);
+					g_fredTimer = 0;
+				}
+
+				int16 hl = (playableEntry == g_fredHoveredEntry) ? 1 : 0;
+				hiliteFredHelpObject(playableEntry, hl);
+
+				hl = (g_fredCurrentHelpObject == g_fredHoveredEntry) ? 1 : 0;
+				hiliteFredHelpObject(g_fredCurrentHelpObject, hl);
+
+				g_fredCurrentHelpObject = nullptr;
+				stopAnimation();
+				isPlaying = 0;
+				justStopped = 1;
+			}
+
+			// Handle click on hovered entry...
+			if (!g_fredHoveredEntry)
+				break;
+
+			uint32 entryType = READ_UINT32(g_fredHoveredEntry);
+
+			if (entryType == 0) {
+				// Exit help...
+				exitResult = 0;
+			} else if (entryType == 1) {
+				// Exit help and clear show-help flag...
+				g_fredShowHelp = 0;
+				exitResult = 1;
+			} else if (entryType == 2) {
+				// Play animation...
+				if (!isPlaying && !justStopped) {
+					if (startAnimation(g_rtfHandle, 0x1B)) {
+						g_fredHelpStep = 0;
+						isPlaying = 1;
+					}
+				}
+			}
+
+			break;
+		}
+		case etSound:
+			soundPending = 1;
+			break;
+		case etTrigger:
+			if (isPlaying)
+				helpAnimStep();
+
+			break;
+		}
+	}
+
+	// Cleanup: restore help screen
+	_xp->hideCursor();
+	_xp->enableController();
+
+	// Restore all entries to highlight state matching hover
+	off = 0;
+	while (true) {
+		entry = getResolvedPtr(g_fredHelpEntries, off);
+		if (!entry)
+			break;
+		int16 hl = (entry == g_fredHoveredEntry) ? 1 : 0;
+		hiliteFredHelpObject(entry, hl);
+		off += 4;
+	}
+
+	_xp->updateDisplay();
+
+	// Unhighlight all entries
+	off = 0;
+	while (true) {
+		entry = getResolvedPtr(g_fredHelpEntries, off);
+		if (!entry)
+			break;
+		hiliteFredHelpObject(entry, 0);
+		off += 4;
+	}
+
+	renderFredScene();
+	_xp->updateDisplay();
+
+	_xp->setFrameRate(15);
+	_xp->setInactivityTimer(30);
+	updateFredSound();
+
+	return exitResult;
+}
+
+void BoltEngine::hiliteFredHelpObject(byte *entry, int16 highlight) {
+	if (!entry)
+		return;
+
+	if (highlight) {
+		// Apply highlight palette...
+		_xp->setPalette(READ_UINT16(entry + 0x0E), READ_UINT16(entry + 0x0C), entry + 0x10);
+		WRITE_UINT32(entry + 8, READ_UINT32(entry + 8) | 1);
+		_system->updateScreen();
+	} else {
+		// Restore saved palette...
+		_xp->setPalette(READ_UINT16(entry + 0x0E), READ_UINT16(entry + 0x0C), entry + 0x2C);
+		WRITE_UINT32(entry + 8, READ_UINT32(entry + 8) & 0xFFFFFFFE);
+		_system->updateScreen();
+	}
+}
+
+void BoltEngine::helpAnimStep() {
+	if (g_fredHelpStep < 0 || g_fredHelpStep >= 4)
+		return;
+
+	// Unhighlight previous selected if not hovered...
+	int16 hl = (g_fredCurrentHelpObject == g_fredHoveredEntry) ? 1 : 0;
+	hiliteFredHelpObject(g_fredCurrentHelpObject, hl);
+
+	// On even steps, select entry from g_fredPlayButton table; on odd steps, deselect...
+	if (!(g_fredHelpStep & 1)) {
+		int16 idx = g_fredHelpStep >> 1;
+		g_fredCurrentHelpObject = getResolvedPtr(g_fredPlayButton, idx * 4);
+	} else {
+		g_fredCurrentHelpObject = nullptr;
+	}
+
+	g_fredTimer = 0;
+
+	// Highlight new selected entry...
+	hiliteFredHelpObject(g_fredCurrentHelpObject, 1);
+
+	// Start/stop blink timer...
+	if (!(g_fredHelpStep & 1)) {
+		g_fredTimer = _xp->startTimer(500);
+	} else {
+		if (g_fredTimer) {
+			_xp->killTimer(g_fredTimer);
+			g_fredTimer = 0;
+		}
+	}
+
+	g_fredHelpStep++;
+}
+
+bool BoltEngine::spawnBalloon() {
+	// Count active balloons...
+	int16 activeCount = 0;
+	int16 i = 0;
+
+	FredEntityState *entry;
+	while (true) {
+		entry = g_fredEntitiesTable[i];
+		if (!entry)
+			break;
+
+		if (entry->animMode == 0x0B && (entry->flags & 1))
+			activeCount++;
+
+		i++;
+	}
+
+	// Max 6 active balloons at once...
+	if (activeCount >= 6)
+		return false;
+
+	// Find next free balloon slot starting from the search index...
+	int16 searchStart = g_fredBalloonSearchIdx;
+	int16 wrapped = 0;
+	i = searchStart;
+
+	while (true) {
+		entry = g_fredEntitiesTable[i];
+		if (!entry) {
+			wrapped = 1;
+			i = -1;
+		} else if (entry->animMode == 0x0B && (entry->flags & 1) == 0) {
+			// Found free balloon slot!
+			break;
+		}
+
+		i++;
+
+		if (i == searchStart && wrapped)
+			break;
+	}
+
+	// No free slot found...
+	if (i == searchStart && wrapped)
+		return false;
+
+	g_fredBalloonSearchIdx = i + 1;
+
+	// Pick row from shuffle table...
+	int16 row = selectBalloonRow();
+
+	uint16 numCols = READ_UINT16(g_fredLevelPtr + 6);
+	int16 stride = numCols + 1;
+	int16 rowBase = stride * row;
+
+	// Get next column from shuffle table...
+	byte *shuffleRow = g_fredShuffleTable + rowBase;
+	byte colCounter = shuffleRow[0];
+	byte col = g_fredShuffleTable[rowBase + colCounter + 1];
+
+	// Look up path table: g_fredPathMatrix[row][col]
+	byte *rowPaths = getResolvedPtr(g_fredPathMatrix, row * 4);
+	byte *pathTable = getResolvedPtr(rowPaths, col * 4);
+
+	// Assign path to balloon...
+	entry->pathTable = pathTable;
+
+	// Advance column counter, wrap if needed...
+	shuffleRow[0]++;
+	if (shuffleRow[0] >= numCols)
+		shuffleRow[0] = 0;
+
+	// Init balloon position from path entry 0...
+	entry->pathIndex = 0;
+
+	byte *pathEntry0 = pathTable + 0;
+	int32 startX = (int32)(int16)READ_UINT16(pathEntry0 + 2) << 8;
+	entry->xPos = startX;
+	int32 startY = (int32)(int16)READ_UINT16(pathEntry0 + 4) << 8;
+	entry->yPos = startY;
+
+	// Activate!
+	entry->flags |= 1;
+
+	return true;
+}
+
+int16 BoltEngine::calcBalloonSpawnDelay() {
+	int16 range = READ_UINT16(g_fredLevelPtr + 2);
+	int16 base = READ_UINT16(g_fredLevelPtr + 0);
+	return _xp->getRandom(range) + base;
+}
+
+int16 BoltEngine::selectBalloonRow() {
+	// Get Fred's center X position...
+	int32 fredX = g_fredSprite.xPos >> 8;
+
+	// Get Fred's sprite width/2 to find center...
+	int16 frameIdx = g_fredSprite.frameIndex;
+	byte *spriteDesc = getResolvedPtr(g_fredSprite.animTable, frameIdx * 6);
+	int16 halfWidth = READ_UINT16(spriteDesc + 0x0A) >> 1;
+	int16 fredCenter = (int16)fredX + halfWidth;
+
+	// Find which row Fred is in...
+	int16 row = 0;
+	byte *rowBounds = g_fredRowBounds; // Array of int16 x-values
+	uint16 numRows = READ_UINT16(g_fredLevelPtr + 4);
+
+	while (row < numRows) {
+		if (READ_UINT16(rowBounds + row * 2) > fredCenter)
+			break;
+		row++;
+	}
+
+	// Pick random row, rejecting if too close to Fred's row...
+	int16 rowBias = READ_UINT16(g_fredLevelPtr + 8);
+	int16 randomRow;
+	do {
+		randomRow = _xp->getRandom(numRows);
+	} while (randomRow >= row - rowBias && randomRow <= row + rowBias);
+
+	return randomRow;
+}
+
+void BoltEngine::setFredAnimMode(FredEntityState *state, int16 mode) {
+	if (mode >= 0x0B && mode < 0x0B + (g_fredLevelPtr ? READ_UINT16(g_fredLevelPtr + 0x0C) : 0)) {
+		state->animMode = 0x0B;
+
+		int16 typeIdx = mode - 0x0B;
+		state->animTable = getResolvedPtr(g_fredBalloonSprite, typeIdx * 4);
+		state->frameIndex = 0;
+		state->frameCountdown = READ_UINT16(g_fredBalloonSprite + typeIdx * 4 + 4);
+		return;
+	}
+
+	if (mode == 0x0A) {
+		// Balloon deactivate...
+		state->animMode = 0x0A;
+		state->flags &= 0xFFFE; // clear active flag
+		return;
+	}
+
+	// Standard Fred animation...
+	state->animMode = mode;
+
+	byte *animTable = g_fredSprites[mode];
+	state->animTable = animTable;
+
+	state->frameIndex = 0;
+	state->frameCountdown = READ_UINT16(animTable + 4);
+
+	// Play sound based on mode...
+	switch (mode) {
+	case 3: // turn right
+		playFredSound(&g_fredSounds[2], nullptr);
+		break;
+	case 4: // run right
+		playFredSound(&g_fredSounds[0], &g_fredSounds[0]);
+		break;
+	case 5: // catch right
+		playFredSound(&g_fredSounds[3], nullptr);
+		break;
+	case 7: // turn left
+		playFredSound(&g_fredSounds[2], nullptr);
+		break;
+	case 8: // run left
+		playFredSound(&g_fredSounds[1], &g_fredSounds[1]);
+		break;
+	case 9: // catch left
+		playFredSound(&g_fredSounds[3], nullptr);
+		break;
+	default: // 0, 1, 2, 6
+		playFredSound(nullptr, nullptr);
+		break;
+	}
+}
+
+void BoltEngine::renderFredScene() {
+	_xp->fillDisplay(0, stFront);
+
+	int16 idx = 0;
+
+	while (true) {
+		FredEntityState *entry = g_fredEntitiesTable[idx];
+		if (!entry)
+			break;
+
+		if (entry->flags & 1) { // active
+			int16 x = (int16)(entry->xPos >> 8);
+			int16 y = (int16)(entry->yPos >> 8);
+
+			// Get current frame sprite...
+			byte *sprite = getResolvedPtr(entry->animTable, entry->frameIndex * 6);
+
+			displayPic(sprite, x, y, stFront);
+
+			// If balloon (mode 0x0B), draw string below...
+			if (entry->animMode == 0x0B) {
+				displayPic(g_fredBalloonString, x + 10, y + 25, stFront);
+			}
+		}
+
+		idx++;
+	}
+}
+
+void BoltEngine::getFredSoundInfo(BOLTLib *lib, int16 memberId, FredSoundInfo *soundInfo) {
+	soundInfo->data = memberAddr(lib, memberId);
+	soundInfo->size = memberSize(lib, memberId);
+}
+
+void BoltEngine::playFredSound(FredSoundInfo *oneShot, FredSoundInfo *loop) {
+	g_fredPendingOneShot = oneShot;
+	g_fredPendingLoop = loop; 
+
+	if (g_fredLoopSound != nullptr || loop == nullptr) {
+		// Check if current loop is g_fredSounds[2] and new one-shot is g_fredSounds[1]
+		// (sound transition check)
+		if (g_fredCurrentSound == &g_fredSounds[3] && g_fredPendingOneShot == &g_fredSounds[2]) {
+			// Allow transition without stopping...
+		} else {
+			g_fredLoopSound = nullptr;
+			g_fredCurrentSound = nullptr;
+			_xp->stopSound();
+		}
+	}
+
+	// If there's a pending sound and nothing currently playing, trigger it!
+	if (g_fredPendingOneShot != nullptr && g_fredCurrentSound == nullptr) {
+		updateFredSound();
+	}
+}
+
+void BoltEngine::updateFredSound() {
+	bool startLoop = false;
+
+	if (g_fredLoopSound != nullptr) {
+		// Loop sound active, continue with it...
+		g_fredCurrentSound = g_fredLoopSound;
+	} else if (g_fredPendingOneShot != nullptr) {
+		// Start pending one-shot...
+		g_fredCurrentSound = g_fredPendingOneShot;
+		g_fredLoopSound = g_fredPendingLoop;
+		startLoop = (g_fredPendingLoop != nullptr);
+		g_fredPendingLoop = 0;
+		g_fredPendingOneShot = 0;
+	} else {
+		// Nothing to play...
+		g_fredCurrentSound = nullptr;
+	}
+
+	if (g_fredCurrentSound != nullptr) {
+		// Play the current sound...
+		FredSoundInfo *snd = g_fredCurrentSound;
+		_xp->playSound(snd->data, snd->size, 22050);
+
+		if (startLoop) {
+			// Queue loop sound twice...
+			FredSoundInfo *loopSnd = g_fredLoopSound;
+			_xp->playSound(loopSnd->data, loopSnd->size, 22050);
+			_xp->playSound(loopSnd->data, loopSnd->size, 22050);
+		}
+	}
+}
 
 } // End of namespace Bolt
diff --git a/engines/bolt/resource.cpp b/engines/bolt/resource.cpp
index 4d8ae6f4c1c..c84fac76c80 100644
--- a/engines/bolt/resource.cpp
+++ b/engines/bolt/resource.cpp
@@ -644,6 +644,9 @@ BOLTCallback BoltEngine::g_defaultMemberFreeCallbacks[25];
 BOLTCallback BoltEngine::g_defaultGroupLoadCallbacks[25];
 BOLTCallback BoltEngine::g_defaultGroupFreeCallbacks[25];
 
+BOLTCallback BoltEngine::g_fredTypeLoadCallbacks[28];
+BOLTCallback BoltEngine::g_fredTypeFreeCallbacks[28];
+
 void BoltEngine::noOpCb() {}
 void BoltEngine::swapAllWordsCb() { ((BoltEngine *)g_engine)->swapAllWords(); }
 void BoltEngine::swapPicHeaderCb() { ((BoltEngine *)g_engine)->swapPicHeader(); }
@@ -654,7 +657,14 @@ void BoltEngine::swapFirstFourWordsCb() { ((BoltEngine *)g_engine)->swapFirstFou
 void BoltEngine::swapSpriteHeaderCb() { ((BoltEngine *)g_engine)->swapSpriteHeader(); }
 void BoltEngine::freeSpriteCleanUpCb() { ((BoltEngine *)g_engine)->freeSpriteCleanUp(); }
 
+void BoltEngine::resolveAllRefsCb() { ((BoltEngine *)g_engine)->resolveAllRefs(); }
+void BoltEngine::swapFredAnimEntryCb() { ((BoltEngine *)g_engine)->swapFredAnimEntry(); }
+void BoltEngine::swapFredAnimDescCb() { ((BoltEngine *)g_engine)->swapFredAnimDesc(); }
+void BoltEngine::swapFredLevelDescCb() { ((BoltEngine *)g_engine)->swapFredLevelDesc(); }
+
 void BoltEngine::initCallbacks() {
+	// --- BOOTHS ---
+
 	for (int i = 0; i < ARRAYSIZE(g_defaultTypeLoadCallbacks); i++) {
 		g_defaultTypeLoadCallbacks[i] = noOpCb;
 	}
@@ -688,12 +698,42 @@ void BoltEngine::initCallbacks() {
 		g_defaultGroupFreeCallbacks[i] = noOpCb;
 	}
 
-	g_resourceDefaultCallbacks.typeLoadCallbacks = g_defaultTypeLoadCallbacks;
-	g_resourceDefaultCallbacks.typeFreeCallbacks = g_defaultTypeFreeCallbacks;
-	g_resourceDefaultCallbacks.memberLoadCallbacks = g_defaultMemberLoadCallbacks;
-	g_resourceDefaultCallbacks.memberFreeCallbacks = g_defaultMemberFreeCallbacks;
-	g_resourceDefaultCallbacks.groupLoadCallbacks = g_defaultGroupLoadCallbacks;
-	g_resourceDefaultCallbacks.groupFreeCallbacks = g_defaultGroupFreeCallbacks;
+	g_boothsBoltCallbacks.typeLoadCallbacks = g_defaultTypeLoadCallbacks;
+	g_boothsBoltCallbacks.typeFreeCallbacks = g_defaultTypeFreeCallbacks;
+	g_boothsBoltCallbacks.memberLoadCallbacks = g_defaultMemberLoadCallbacks;
+	g_boothsBoltCallbacks.memberFreeCallbacks = g_defaultMemberFreeCallbacks;
+	g_boothsBoltCallbacks.groupLoadCallbacks = g_defaultGroupLoadCallbacks;
+	g_boothsBoltCallbacks.groupFreeCallbacks = g_defaultGroupFreeCallbacks;
+
+	// --- FRED ---
+
+	for (int i = 0; i < ARRAYSIZE(g_fredTypeLoadCallbacks); i++) {
+		g_fredTypeLoadCallbacks[i] = noOpCb;
+	}
+
+	g_fredTypeLoadCallbacks[2] = swapAllWordsCb;
+	g_fredTypeLoadCallbacks[6] = resolveAllRefsCb;
+	g_fredTypeLoadCallbacks[8] = swapSpriteHeaderCb;
+	g_fredTypeLoadCallbacks[10] = swapPicHeaderCb;
+	g_fredTypeLoadCallbacks[11] = swapAndResolvePicDescCb;
+	g_fredTypeLoadCallbacks[12] = swapFirstTwoWordsCb;
+	g_fredTypeLoadCallbacks[14] = swapFirstFourWordsCb;
+	g_fredTypeLoadCallbacks[25] = swapFredAnimEntryCb;
+	g_fredTypeLoadCallbacks[26] = swapFredAnimDescCb;
+	g_fredTypeLoadCallbacks[27] = swapFredLevelDescCb;
+
+	for (int i = 0; i < ARRAYSIZE(g_fredTypeFreeCallbacks); i++) {
+		g_fredTypeFreeCallbacks[i] = noOpCb;
+	}
+
+	g_fredTypeFreeCallbacks[8] = freeSpriteCleanUpCb;
+
+	g_fredBoltCallbacks.typeLoadCallbacks = g_fredTypeLoadCallbacks;
+	g_fredBoltCallbacks.typeFreeCallbacks = g_fredTypeFreeCallbacks;
+	g_fredBoltCallbacks.memberLoadCallbacks = g_defaultMemberLoadCallbacks;
+	g_fredBoltCallbacks.memberFreeCallbacks = g_defaultMemberFreeCallbacks;
+	g_fredBoltCallbacks.groupLoadCallbacks = g_defaultGroupLoadCallbacks;
+	g_fredBoltCallbacks.groupFreeCallbacks = g_defaultGroupFreeCallbacks;
 }
 
 } // End of namespace Bolt
diff --git a/engines/bolt/xplib/display.cpp b/engines/bolt/xplib/display.cpp
index 78ef3391987..48ae842ef8d 100644
--- a/engines/bolt/xplib/display.cpp
+++ b/engines/bolt/xplib/display.cpp
@@ -525,7 +525,7 @@ void XpLib::compositeToScreen() {
 		cursorY = g_lastCursorY - g_cursorHotspotY;
 
 		// Save background under cursor position...
-		clipAndBlit(&g_cursorBackgroundSave, &g_surfaces[stFront].mainPic, -cursorX, -cursorY, nullptr);
+		clipAndBlit(&g_surfaces[stFront].mainPic, &g_cursorBackgroundSave, -cursorX, -cursorY, nullptr);
 
 		// Draw cursor sprite at position...
 		clipAndBlit(&g_cursorSprite, &g_surfaces[stFront].mainPic, cursorX, cursorY, &g_cursorRect);
diff --git a/engines/bolt/xplib/events.cpp b/engines/bolt/xplib/events.cpp
index 3dc0625d029..873bd4e509e 100644
--- a/engines/bolt/xplib/events.cpp
+++ b/engines/bolt/xplib/events.cpp
@@ -216,6 +216,7 @@ void XpLib::pumpMessages() {
 
 	updateTimers();
 	_bolt->_system->delayMillis(5);
+	_bolt->_system->updateScreen();
 }
 
 void XpLib::handleTimer(uint32 timerId) {
@@ -230,13 +231,13 @@ void XpLib::handleTimer(uint32 timerId) {
 			postEvent(etInactivity, 0);
 	}
 
-	if (g_cursorBlinkCountdown != 0) {
-		g_cursorBlinkCountdown--;
-		if (g_cursorBlinkCountdown == 0)
+	if (g_screensaverCountdown != 0) {
+		g_screensaverCountdown--;
+		if (g_screensaverCountdown == 0)
 			activateScreenSaver();
 	}
 
-	if (g_inactivityCountdown != 0 || g_cursorBlinkCountdown != 0) {
+	if (g_inactivityCountdown != 0 || g_screensaverCountdown != 0) {
 		g_inactivityTimerId = startTimer(1000);
 	} else {
 		g_inactivityTimerId = 0;
@@ -423,7 +424,7 @@ int16 XpLib::setScreenSaverTimer(int16 seconds) {
 	int16 prev = g_screenSaverTimerValue;
 
 	g_screenSaverTimerValue = seconds;
-	g_cursorBlinkCountdown = seconds;
+	g_screensaverCountdown = seconds;
 
 	if (seconds != 0 && g_inactivityTimerId == 0)
 		g_inactivityTimerId = startTimer(1000);
@@ -443,7 +444,7 @@ void XpLib::resetInactivity() {
 	}
 
 	// Reset screensaver countdown
-	g_cursorBlinkCountdown = g_screenSaverTimerValue;
+	g_screensaverCountdown = g_screenSaverTimerValue;
 
 	if (g_screenSaverTimerValue != 0 && g_inactivityTimerId == 0)
 		g_inactivityTimerId = startTimer(1000);
diff --git a/engines/bolt/xplib/random.cpp b/engines/bolt/xplib/random.cpp
index 412c0d47861..2063c0e9990 100644
--- a/engines/bolt/xplib/random.cpp
+++ b/engines/bolt/xplib/random.cpp
@@ -25,7 +25,8 @@
 namespace Bolt {
 
 int16 XpLib::getRandom(int16 range) {
-	return (int16)(((uint32)_bolt->_randomSource.getRandomNumber(UINT_MAX) * (uint32)range) / 0x8000);
+	int16 r = (int16)_bolt->_randomSource.getRandomNumber(0x7FFF); // [0, 32767]
+	return (int16)((int32)r * (int32)(uint16)range / 0x8000L);
 }
 
 void XpLib::randomize() {
diff --git a/engines/bolt/xplib/xplib.h b/engines/bolt/xplib/xplib.h
index 7cd1672596b..bc30e2012f2 100644
--- a/engines/bolt/xplib/xplib.h
+++ b/engines/bolt/xplib/xplib.h
@@ -381,7 +381,7 @@ protected:
 	uint32 g_inactivityTimerId = 0;
 	int16 g_inactivityCountdown = 0;
 	int16 g_inactivityTimerValue = 0;
-	int16 g_cursorBlinkCountdown = 0;
+	int16 g_screensaverCountdown = 0;
 	int16 g_screenSaverTimerValue = 0;
 	int16 g_inactivityTimeout = 0;
 


Commit: 584db066a00ffb29a13503b53a18f74cb632f24f
    https://github.com/scummvm/scummvm/commit/584db066a00ffb29a13503b53a18f74cb632f24f
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-04-07T01:11:14+02:00

Commit Message:
BOLT: CARNIVAL: Implement TopCat's minigame

Changed paths:
    engines/bolt/av.cpp
    engines/bolt/bolt.h
    engines/bolt/booths/topcat.cpp
    engines/bolt/resource.cpp
    engines/bolt/xplib/events.cpp
    engines/bolt/xplib/sound.cpp
    engines/bolt/xplib/xplib.h


diff --git a/engines/bolt/av.cpp b/engines/bolt/av.cpp
index ff45768412b..710d13fd6d2 100644
--- a/engines/bolt/av.cpp
+++ b/engines/bolt/av.cpp
@@ -99,6 +99,7 @@ bool BoltEngine::playAV(RTFResource *rtfHandle, int16 animIndex, int16 width, in
 
 	// Not in the original, but it helps cleaning up
 	// if the user wants to quit during a video...
+	assert(shouldQuit());
 	killRTF(nullptr);
 	cleanUpAV();
 	return false;
diff --git a/engines/bolt/bolt.h b/engines/bolt/bolt.h
index c0f62fb8284..20605cacff7 100644
--- a/engines/bolt/bolt.h
+++ b/engines/bolt/bolt.h
@@ -269,6 +269,20 @@ typedef struct FredEntityState {
 	}
 } FredEntityState;
 
+typedef struct TopCatAnim {
+	int16 animType; // + 00
+	int16 animIndex;// + 02
+	int16 transitionToNextQuestionFlag;// + 04
+	int16 *seqPtr;  // + 04
+
+	TopCatAnim() {
+		animType = 0;
+		animIndex = 0;
+		transitionToNextQuestionFlag = 0;
+		seqPtr = nullptr;
+	}
+} TopCatAnim;
+
 class BoltEngine : public Engine {
 friend class XpLib;
 
@@ -493,6 +507,7 @@ protected:
 	static void noOpCb();
 
 	static void swapAllWordsCb();
+	static void swapAllLongsCb();
 	static void swapPicHeaderCb();
 	static void swapAndResolvePicDescCb();
 	static void swapFirstWordCb();
@@ -701,7 +716,104 @@ protected:
 	int16 scoobyGame(int16 prevBooth) { return 0; }
 
 	// --- TOPCAT ---
-	int16 topCatGame(int16 prevBooth) { return 0; }
+	int16 topCatGame(int16 prevBooth);
+	bool initTopCat();
+	void cleanUpTopCat();
+	int16 playTopCat();
+	int16 handleActionButton(int16 *result);
+	void queueAnim(int16 animType, int16 param);
+	bool maintainAnim(int16 soundEvent);
+	void maintainIdleSound(int16 decrement);
+	bool startNextAnim(int16 *playAnswerAnim);
+	void setAnimType(int16 newType);
+	void highlightObject(byte *entry, int16 highlight);
+	bool setupNextQuestion();
+	void adjustColors(byte *pic, int8 shift);
+	void shuffleTopCatQuestions();
+	void shuffleTopCatPermutations();
+	void getTopCatSoundInfo(BOLTLib *lib, int16 memberId, FredSoundInfo *soundInfo);
+	void setScoreLights(int16 litMask);
+	void swapTopCatHelpEntry();
+
+	static void swapTopCatHelpEntryCb();
+
+	RTFResource *g_topCatRtfHandle = nullptr;
+	RTFResource *g_topCatAvHandle = nullptr;
+	BOLTLib *g_topCatBoltLib = nullptr;
+	BOLTCallbacks g_topCatBoltCallbacks;
+
+	static BOLTCallback g_topCatTypeLoadCallbacks[26];
+	static BOLTCallback g_topCatTypeFreeCallbacks[26];
+
+	byte *g_topCatBackgroundPalette = nullptr;
+	byte *g_topCatBackground = nullptr;
+	byte *g_topCatBackgroundAnimationPalette = nullptr;
+	byte *g_topCatGraphicsAssets = nullptr;
+
+	int16 g_topCatBackgroundAnimFrame = 0;
+	int16 g_topCatMaxBackgroundAnimFrames = 0;
+	int16 g_topCatCurrentAnimType = 0;
+	int16 g_topCatAnimStateMachineStep = 0;
+	int16 g_topCatAnimQueueSize = 0;
+	int16 g_topCatQueuedSoundFrames = 0;
+	byte *g_topCatButtonsPalette = nullptr;
+	byte *g_topCatBlinkEntries = nullptr;
+	byte *g_topCatLightsPalette = nullptr;
+	int16 *g_topCatBlinkSeqPtr = nullptr;
+	byte *g_topCatSelectedChoiceOverlayGfx = nullptr;
+	byte *g_topCatCycleData = nullptr;
+	XPCycleState g_topCatCycleSpecs[4];
+	byte *g_topCatShuffledQuestions = nullptr;
+	byte *g_topCatShuffledAnswers = nullptr;
+	byte *g_topCatAnswersPermutations = nullptr;
+	byte *g_topCatAnswers = nullptr;
+	byte *g_topCatAnswersScreenPositions = nullptr;
+	int16 g_topCatSavedScore = 0;
+	int16 g_topCatSaveHistory = 0;
+	const char *g_topCatSaveFileName = "TopCatBF";
+	const char *g_topCatStaticSaveFileName = "TopCatBFStatic";
+	int16 g_topCatScore = 0;
+	int16 g_topCatShuffledQuestionsArrayIdx = 0;
+	uint32 g_topCatBlinkTimer = 0;
+	FredSoundInfo g_topCatSoundInfo;
+	byte g_topCatSavedShuffledQuestions[60] = { 0 };
+	byte g_topCatSavedShuffledAnswers[60] = { 0 };
+	byte g_topCatSavedAnswersPermutations[60 * 3] = { 0 };
+	byte g_topCatSaveBuffer[302] = { 0 };
+	byte *g_topCatHoveredEntry = nullptr;
+	byte *g_topCatHelpButton = nullptr;
+	byte *g_topCatBackButton = nullptr;
+	byte *g_topCatBlinkEntry = nullptr;
+	int16 g_topCatCycleStep = 0;
+	int16 g_topCatDrawnQuestionId = 0;
+	int16 g_topCatCurrentAnswerIdx = 0;
+	XPCycleState g_topCatChoiceCycleState[4];
+	TopCatAnim g_topCatAnimQueue[3];
+	int16 g_topCatCorrectAnimIdx = 0;
+	int16 g_topCatWrongAnimIdx = 0;
+	int16 g_topCatShouldPlayAnswerAnim = 0;
+	int16 g_topCatMaintainSoundFlag = 0;
+	int16 g_topCatPermTableA[3] = { 0, 1, 2 };
+	int16 g_topCatPermTableB[3] = { 0, 1, 2 };
+	int16 g_topCatBlinkSeq0[5] = { 0x01, 0x00, 0x01, 0x00, 0x01 };
+	int16 g_topCatBlinkSeq1[5] = { 0x03, 0x01, 0x03, 0x01, 0x03 };
+	int16 g_topCatBlinkSeq2[5] = { 0x07, 0x03, 0x07, 0x03, 0x07 };
+	int16 g_topCatBlinkSeq3[5] = { 0x0F, 0x07, 0x0F, 0x07, 0x0F };
+	int16 g_topCatBlinkSeq4[5] = { 0x1F, 0x0F, 0x1F, 0x0F, 0x1F };
+	int16 g_topCatBlinkSeq5[5] = { 0x3F, 0x1F, 0x3F, 0x1F, 0x3F };
+	int16 g_topCatBlinkSeq6[25] = {
+		0x09, 0x12, 0x24, 0x09, 0x12, 0x24,
+		0x09, 0x12, 0x24, 0x09, 0x12, 0x24,
+		0x09, 0x12, 0x24, 0x09, 0x12, 0x24,
+		0x09, 0x12, 0x24, 0x09, 0x12, 0x24,
+		0x3F
+	};
+
+	int16 *g_topCatBlinkSeqTable[7] = {
+		g_topCatBlinkSeq0, g_topCatBlinkSeq1, g_topCatBlinkSeq2,
+		g_topCatBlinkSeq3, g_topCatBlinkSeq4, g_topCatBlinkSeq5,
+		g_topCatBlinkSeq6
+	};
 
 	// --- YOGI ---
 	int16 yogiGame(int16 prevBooth) { return 0; }
diff --git a/engines/bolt/booths/topcat.cpp b/engines/bolt/booths/topcat.cpp
index d0e5b38e0bf..133a89ca72d 100644
--- a/engines/bolt/booths/topcat.cpp
+++ b/engines/bolt/booths/topcat.cpp
@@ -23,6 +23,1132 @@
 
 namespace Bolt {
 
+int16 Bolt::BoltEngine::topCatGame(int16 prevBooth) {
+	int16 savedTimer = _xp->setInactivityTimer(30);
 
+	if (!initTopCat()) {
+		cleanUpTopCat();
+		return 8;
+	}
+
+	int16 result = playTopCat();
+	cleanUpTopCat();
+
+	_xp->setInactivityTimer(savedTimer);
+	return result;
+}
+
+bool BoltEngine::initTopCat() {
+	g_topCatRtfHandle = openRTF(assetPath(g_displayMode != 0 ? "topcatcr.av" : "topcatog.av"));
+	if (!g_topCatRtfHandle)
+		return false;
+
+	g_topCatAvHandle = openRTF(assetPath("topcatau.av"));
+	if (!g_topCatAvHandle)
+		return false;
+
+	if (!openBOLTLib(&g_topCatBoltLib, &g_topCatBoltCallbacks, assetPath("topcat.blt")))
+		return false;
+
+	if (!getBOLTGroup(g_topCatBoltLib, 0, 1))
+		return false;
+
+	g_topCatBackgroundPalette = memberAddr(g_topCatBoltLib, 0x20);
+	g_topCatBackground = memberAddr(g_topCatBoltLib, g_displayMode != 0 ? 0x22 : 0x21);
+	g_topCatBackgroundAnimationPalette = memberAddr(g_topCatBoltLib, 0x00);
+	g_topCatGraphicsAssets = memberAddr(g_topCatBoltLib, g_displayMode != 0 ? 0x1E : 0x1D);
+
+	g_topCatBackgroundAnimFrame = 0;
+
+	// Count background anim frames...
+	g_topCatMaxBackgroundAnimFrames = 0;
+	while (getResolvedPtr(g_topCatGraphicsAssets, g_topCatMaxBackgroundAnimFrames * 4) != 0) {
+		g_topCatMaxBackgroundAnimFrames++;
+	}
+
+	g_topCatCurrentAnimType = 0;
+	g_topCatAnimStateMachineStep = 0;
+	g_topCatQueuedSoundFrames = 0;
+	g_topCatAnimQueueSize = 0;
+
+	g_topCatButtonsPalette = memberAddr(g_topCatBoltLib, 0x3D);
+	g_topCatBlinkEntries = memberAddr(g_topCatBoltLib, 0x3E);
+
+	g_topCatLightsPalette = memberAddr(g_topCatBoltLib, 0x31);
+	g_topCatBlinkSeqPtr = nullptr;
+
+	g_topCatSelectedChoiceOverlayGfx = memberAddr(g_topCatBoltLib, 0x26);
+	g_topCatCycleData = memberAddr(g_topCatBoltLib, 0x27);
+	boltCycleToXPCycle(g_topCatCycleData, g_topCatCycleSpecs);
+
+	g_topCatChoiceCycleState->startIndex = 0;
+	g_topCatChoiceCycleState->endIndex = 0;
+	g_topCatChoiceCycleState->delay = 0;
+	g_topCatChoiceCycleState->nextFire = 0;
+	g_topCatChoiceCycleState->active = false;
+
+	g_topCatShuffledQuestions = memberAddr(g_topCatBoltLib, 0x32); // [60]
+	g_topCatShuffledAnswers = memberAddr(g_topCatBoltLib, 0x33); // [60]
+	g_topCatAnswersPermutations = memberAddr(g_topCatBoltLib, 0x34); // [60 * 3]
+	g_topCatAnswers = memberAddr(g_topCatBoltLib, 0x35);
+	g_topCatAnswersScreenPositions = memberAddr(g_topCatBoltLib, 0x36);
+
+	if (vLoad(&g_topCatSavedScore, g_topCatSaveFileName)) {
+		g_topCatScore = g_topCatSavedScore;
+	} else {
+		g_topCatScore = 0;
+	}
+
+	if (vLoad(g_topCatSaveBuffer, g_topCatStaticSaveFileName)) {
+		g_topCatShuffledQuestionsArrayIdx = READ_BE_INT16(g_topCatSaveBuffer);
+
+		int offset;
+		for (int i = 0; i < ARRAYSIZE(g_topCatSavedShuffledQuestions); i++) {
+			offset = sizeof(int16) + i;
+			g_topCatSavedShuffledQuestions[i] = g_topCatSaveBuffer[offset];
+		}
+
+		for (int i = 0; i < ARRAYSIZE(g_topCatSavedShuffledAnswers); i++) {
+			offset = sizeof(g_topCatSavedShuffledQuestions) + sizeof(int16) + i;
+			g_topCatSavedShuffledAnswers[i] = g_topCatSaveBuffer[offset];
+		}
+
+		for (int i = 0; i < ARRAYSIZE(g_topCatSavedAnswersPermutations); i++) {
+			offset = sizeof(g_topCatSavedShuffledAnswers) + sizeof(g_topCatSavedShuffledQuestions) + sizeof(int16) + i;
+			g_topCatSavedAnswersPermutations[i] = g_topCatSaveBuffer[offset];
+		}
+
+		int16 answerOff = 0;
+		for (int16 i = 0; i < 60; i++) {
+			g_topCatShuffledQuestions[i] = g_topCatSavedShuffledQuestions[i];
+			g_topCatShuffledAnswers[i] = g_topCatSavedShuffledAnswers[i];
+
+			for (int16 j = 0; j < 3; j++) {
+				g_topCatAnswersPermutations[answerOff + j] = g_topCatSavedAnswersPermutations[answerOff + j];
+			}
+
+			answerOff += 3;
+		}
+	} else {
+		g_topCatShuffledQuestionsArrayIdx = -1;
+		shuffleTopCatQuestions();
+		shuffleTopCatPermutations();
+	}
+
+	g_topCatBlinkTimer = 0;
+
+	getTopCatSoundInfo(g_topCatBoltLib, 0x1F, &g_topCatSoundInfo);
+
+	uint32 dummy;
+	while (_xp->getEvent(etTimer, &dummy) != etTimer);
+
+	_xp->stopCycle();
+	_xp->setTransparency(false);
+
+	displayColors(g_topCatBackgroundPalette, stFront, 0);
+	displayPic(g_topCatBackground, g_displayX, g_displayY, stFront);
+	_xp->updateDisplay();
+
+	_xp->setTransparency(true);
+	displayColors(g_topCatBackgroundAnimationPalette, stFront, 0);
+	displayPic(g_topCatBackground, g_displayX, g_displayY, stBack);
+
+	byte *firstBackgroundAnimFrame = getResolvedPtr(g_topCatGraphicsAssets, 0);
+	displayPic(firstBackgroundAnimFrame, g_displayX, g_displayY, stFront);
+
+	displayColors(g_topCatBackgroundPalette, stBack, 0);
+
+	if (!setupNextQuestion())
+		return false;
+
+	_xp->updateDisplay();
+
+	g_topCatHoveredEntry = nullptr;
+
+	// Init button palettes...
+	int16 off = 0;
+	byte *entry;
+	while (true) {
+		entry = getResolvedPtr(g_topCatButtonsPalette, off);
+		if (!entry)
+			break;
+
+		if (READ_UINT32(entry) == 1)
+			g_topCatHelpButton = entry;
+
+		if (READ_UINT32(entry) == 2)
+			g_topCatBackButton = entry;
+
+		_xp->getPalette(READ_UINT16(entry + 0x0E), READ_UINT16(entry + 0x10), entry + 0x2E);
+		unpackColors(READ_UINT16(entry + 0x10), entry + 0x12);
+
+		off += 4;
+	}
+
+	// Init light palelttes...
+	int16 lightOff = 0;
+	for (int16 i = 0; i < 6; i++) {
+		byte *lightEntry = getResolvedPtr(g_topCatLightsPalette, lightOff);
+
+		int16 colorCount = READ_UINT16(lightEntry + 4);
+		int16 palStart = READ_UINT16(lightEntry);
+		_xp->getPalette(palStart + 0x80, colorCount, lightEntry + colorCount * 4 + 8);
+
+		for (int16 j = 0; j < colorCount; j++) {
+			WRITE_UINT32(lightEntry + j * 4 + 8, READ_BE_UINT32(lightEntry + j * 4 + 8));
+		}
+
+		unpackColors(colorCount, lightEntry + 8);
+
+		lightOff += 4;
+	}
+
+	// Set lights based on saved score...
+	int16 litMask = (1 << g_topCatScore) - 1;
+	setScoreLights(litMask);
+
+	return true;
+}
+
+void BoltEngine::cleanUpTopCat() {
+	_xp->stopSound();
+	_xp->stopCycle();
+
+	if (g_topCatScore < 6) {
+		byte questionIdx = g_topCatShuffledQuestions[g_topCatShuffledQuestionsArrayIdx];
+		g_topCatShuffledAnswers[questionIdx]--;
+		g_topCatShuffledQuestionsArrayIdx--;
+	} else {
+		g_topCatScore = 0;
+	}
+
+	g_topCatSavedScore = g_topCatScore;
+	g_topCatSaveHistory = g_topCatShuffledQuestionsArrayIdx;
+
+	int16 answerOff = 0;
+	for (int16 i = 0; i < 60; i++) {
+		g_topCatSavedShuffledQuestions[i] = g_topCatShuffledQuestions[i];
+		g_topCatSavedShuffledAnswers[i] = g_topCatShuffledAnswers[i];
+
+		for (int16 j = 0; j < 3; j++) {
+			g_topCatSavedAnswersPermutations[answerOff + j] = g_topCatAnswersPermutations[answerOff + j];
+		}
+
+		answerOff += 3;
+	}
+
+	memset(g_topCatSaveBuffer, 0, sizeof(g_topCatSaveBuffer));
+
+	WRITE_BE_INT16(g_topCatSaveBuffer, g_topCatSaveHistory);
+
+	int offset;
+	for (int i = 0; i < ARRAYSIZE(g_topCatSavedShuffledQuestions); i++) {
+		offset = sizeof(int16) + i;
+		g_topCatSaveBuffer[offset] = g_topCatSavedShuffledQuestions[i];
+	}
+
+	for (int i = 0; i < ARRAYSIZE(g_topCatSavedShuffledAnswers); i++) {
+		offset = sizeof(g_topCatSavedShuffledQuestions) + sizeof(int16) + i;
+		g_topCatSaveBuffer[offset] = g_topCatSavedShuffledAnswers[i];
+	}
+
+	for (int i = 0; i < ARRAYSIZE(g_topCatSavedAnswersPermutations); i++) {
+		offset = sizeof(g_topCatSavedShuffledAnswers) + sizeof(g_topCatSavedShuffledQuestions) + sizeof(int16) + i;
+		g_topCatSaveBuffer[offset] = g_topCatSavedAnswersPermutations[i];
+	}
+
+	vSave(&g_topCatSavedScore, sizeof(g_topCatSavedScore), g_topCatSaveFileName);
+	vSave(g_topCatSaveBuffer, sizeof(g_topCatSaveBuffer), g_topCatStaticSaveFileName);
+
+	freeBOLTGroup(g_topCatBoltLib, 0, 1);
+	closeBOLTLib(&g_topCatBoltLib);
+
+	if (g_topCatRtfHandle) {
+		closeRTF(g_topCatRtfHandle);
+		g_topCatRtfHandle = nullptr;
+	}
+
+	if (g_topCatAvHandle) {
+		closeRTF(g_topCatAvHandle);
+		g_topCatAvHandle = nullptr;
+	}
+
+	// These currently erroneously clear some graphics while on the "win a letter"
+	// animation, unsure why the issue is not on the original...
+#if 0
+	_xp->fillDisplay(0, stFront);
+	_xp->updateDisplay();
+#endif
+}
+
+int16 BoltEngine::playTopCat() {
+	int16 result = 0;
+	int16 exitFlag = 0;
+
+	setAnimType(1);
+	queueAnim(3, 0); // Play question audio
+
+	int16 cursorX = 120;
+	int16 cursorY = 192;
+	_xp->setCursorPos(cursorY, cursorX);
+	_xp->setCursorColor(0x00, 0xFF, 0xFF);
+	_xp->showCursor();
+
+	while (true) {
+		uint32 eventData;
+		byte *soundDataPtr;
+		int16 eventType = _xp->getEvent(etEmpty, &eventData, &soundDataPtr);
+
+		switch (eventType) {
+		case etEmpty: {
+			if (!maintainAnim(0)) {
+				return 8;
+			}
+
+			break;
+		}
+
+		case etTimer: { // Timer expired
+			if (eventData != g_topCatBlinkTimer)
+				break;
+
+			if (!g_topCatBlinkEntry)
+				break;
+
+			g_topCatBlinkTimer = _xp->startTimer(500);
+
+			// Toggle highlight...
+			int16 highlight;
+			if (READ_UINT16(g_topCatBlinkEntry + 0x0C) & 1) {
+				highlight = 0;
+			} else {
+				highlight = 1;
+			}
+
+			highlightObject(g_topCatBlinkEntry, highlight);
+			break;
+		}
+
+		case etMouseMove: {
+			cursorY = (int16)(eventData >> 16);
+			cursorX = (int16)(eventData & 0xFFFF);
+
+			// Hit-test against buttons...
+			int16 entryIdx = 0;
+			byte *entry = nullptr;
+			int16 off = 0;
+
+			while (true) {
+				entry = getResolvedPtr(g_topCatButtonsPalette, off);
+				if (!entry)
+					break;
+
+				int16 entryY = READ_UINT16(entry + 4);
+				int16 entryX = READ_UINT16(entry + 6);
+				int16 entryH = READ_UINT16(entry + 8);
+				int16 entryW = READ_UINT16(entry + 0x0A);
+
+				if (cursorY > entryY &&
+					cursorY < entryY + entryH &&
+					cursorX > entryX &&
+					cursorX < entryX + entryW)
+					break;
+
+				off += 4;
+				entryIdx++;
+			}
+
+			// Unhighlight previous entry if needed...
+			if (g_topCatHoveredEntry != entry &&
+				g_topCatHoveredEntry != nullptr &&
+				g_topCatHoveredEntry != g_topCatBlinkEntry &&
+				g_topCatHoveredEntry != g_topCatBackButton) {
+
+				if (g_topCatHoveredEntry == g_topCatHelpButton) {
+					if (g_topCatCurrentAnimType != 2)
+						highlightObject(g_topCatHoveredEntry, 0);
+				} else {
+					byte *hovered = g_topCatHoveredEntry;
+					if (READ_UINT32(hovered) < 3 || READ_UINT32(hovered) >= 6)
+						highlightObject(g_topCatHoveredEntry, 0);
+				}
+			}
+
+			g_topCatHoveredEntry = entry;
+
+			// Highlight new entry if applicable...
+			if (!entry)
+				break;
+
+			if (entry == g_topCatBlinkEntry)
+				break;
+
+			if (entry == g_topCatBackButton)
+				break;
+
+			if (READ_UINT32(entry) >= 3 && READ_UINT32(entry) < 6)
+				break;
+
+			highlightObject(g_topCatHoveredEntry, 1);
+			break;
+		}
+
+		case etMouseDown: {
+			if (exitFlag)
+				break;
+
+			exitFlag = handleActionButton(&result);
+			break;
+		}
+
+		case etSound: {
+			int16 soundType;
+			if (soundDataPtr >= g_topCatSoundInfo.data && soundDataPtr < g_topCatSoundInfo.data + g_topCatSoundInfo.size) {
+				soundType = 1;
+			} else {
+				soundType = 2;
+			}
+
+			if (!maintainAnim(soundType)) {
+				return 8;
+			}
+
+			break;
+		}
+
+		case etInactivity: {
+			// Toggle blinking highlight...
+			int16 highlight;
+			if (g_topCatBlinkEntry == g_topCatHoveredEntry &&
+				g_topCatBlinkEntry != 0) {
+				if (READ_UINT32(g_topCatBlinkEntry) < 3 || READ_UINT32(g_topCatBlinkEntry) >= 6) {
+					highlight = 1;
+				} else {
+					highlight = 0;
+				}
+			} else {
+				highlight = 0;
+			}
+
+			highlightObject(g_topCatBlinkEntry, highlight);
+
+			// Set help button as new blinking entry...
+			g_topCatBlinkEntry = g_topCatHelpButton;
+
+			if (g_topCatHelpButton != 0) {
+				if (READ_UINT32(g_topCatBlinkEntry) < 3 || READ_UINT32(g_topCatBlinkEntry) >= 6) {
+					g_topCatBlinkTimer = _xp->startTimer(500);
+				} else {
+					g_topCatBlinkTimer = 0;
+				}
+			} else {
+				g_topCatBlinkTimer = 0;
+			}
+
+			highlightObject(g_topCatBlinkEntry, 1);
+			break;
+		}
+
+		case etTrigger: { // Palette cycle event
+			if (g_topCatCurrentAnimType == 2) {
+				// Toggle blinking highlight...
+				int16 highlight;
+				if (g_topCatBlinkEntry == g_topCatHoveredEntry &&
+					g_topCatBlinkEntry != 0) {
+					if (READ_UINT32(g_topCatBlinkEntry) < 3 || READ_UINT32(g_topCatBlinkEntry) >= 6) {
+						highlight = 1;
+					} else {
+						highlight = 0;
+					}
+				} else {
+					highlight = 0;
+				}
+
+				highlightObject(g_topCatBlinkEntry, highlight);
+
+				// Advance to next blinking entry...
+				if (g_topCatCycleStep & 1) {
+					g_topCatBlinkEntry = nullptr;
+				} else {
+					g_topCatBlinkEntry = getResolvedPtr(g_topCatBlinkEntries, (g_topCatCycleStep >> 1) * 4);
+				}
+
+				if (g_topCatBlinkEntry != 0) {
+					if (READ_UINT32(g_topCatBlinkEntry) < 3 || READ_UINT32(g_topCatBlinkEntry) >= 6) {
+						g_topCatBlinkTimer = _xp->startTimer(500);
+					} else {
+						g_topCatBlinkTimer = 0;
+					}
+				} else {
+					g_topCatBlinkTimer = 0;
+				}
+
+				highlightObject(g_topCatBlinkEntry, 1);
+				g_topCatCycleStep++;
+
+			} else if (g_topCatCurrentAnimType == 3) {
+				// Unhighlight previous...
+				if (g_topCatCycleStep > 3) {
+					byte *prevEntry = getResolvedPtr(g_topCatButtonsPalette, (g_topCatCycleStep - 1) * 4);
+					highlightObject(prevEntry, 0);
+				} else {
+					highlightObject(g_topCatBackButton, 0);
+				}
+
+				// Highlight next...
+				byte *nextEntry = getResolvedPtr(g_topCatButtonsPalette, g_topCatCycleStep * 4);
+				highlightObject(nextEntry, 1);
+				g_topCatCycleStep++;
+			} else if (g_topCatCurrentAnimType == 6) {
+				// Light blinking animation...
+				int16 mask = READ_UINT16(g_topCatBlinkSeqPtr);
+				g_topCatBlinkSeqPtr++;
+
+				setScoreLights(mask);
+			}
+
+			break;
+		}
+
+		default:
+			break;
+		}
+
+		if (exitFlag && g_topCatAnimQueueSize == 0 && g_topCatCurrentAnimType == 1) {
+			if (g_topCatScore != 6)
+				_xp->hideCursor();
+
+			return result;
+		}
+	}
+}
+
+int16 BoltEngine::handleActionButton(int16 *result) {
+	int16 prevState = g_topCatCurrentAnimType;
+
+	switch (g_topCatCurrentAnimType) {
+	case 2:
+	case 3:
+		stopAnimation();
+		_xp->stopSound();
+		g_topCatQueuedSoundFrames = 0;
+		setAnimType(1);
+		break;
+
+	case 4:
+	case 5:
+	case 6:
+		return 0;
+	default:
+		break;
+	}
+
+	if (g_topCatAnimQueueSize != 0 && g_topCatHoveredEntry != 0) {
+		if (READ_UINT32(g_topCatHoveredEntry) != 0)
+			return 0;
+	}
+
+	if (!g_topCatHoveredEntry)
+		return 0;
+
+	_xp->setInactivityTimer(30);
+
+	// If blinking entry is the help button, stop blinking...
+	if (g_topCatBlinkEntry == g_topCatHelpButton) {
+		int16 highlight;
+		if (g_topCatBlinkEntry == g_topCatHoveredEntry &&
+			g_topCatBlinkEntry != 0) {
+			if (READ_UINT32(g_topCatBlinkEntry) < 3 || READ_UINT32(g_topCatBlinkEntry) >= 6) {
+				highlight = 1;
+			} else {
+				highlight = 0;
+			}
+		} else {
+			highlight = 0;
+		}
+
+		highlightObject(g_topCatBlinkEntry, highlight);
+		g_topCatBlinkEntry = 0;
+		g_topCatBlinkTimer = 0;
+		highlightObject(g_topCatBlinkEntry, 1);
+	}
+
+	int32 entryType = READ_UINT32(g_topCatHoveredEntry);
+
+	if (entryType == 0) {
+		*result = 8;
+		return 1;
+	}
+
+	if (entryType == 1) {
+		if (prevState == 2)
+			return 0;
+
+		queueAnim(2, 0); // Play help audio
+		return 0;
+	}
+
+	if (entryType == 2) {
+		if (prevState == 3)
+			return 0;
+
+		queueAnim(3, 0); // Play question audio
+		return 0;
+	}
+
+	if (entryType < 3 || entryType >= 6)
+		return 0;
+
+	// Answer choice clicked...
+	int16 answerSlot = (int16)entryType - 3;
+
+	// Display overlay pic on the answer slot...
+	int16 selectedChoiceOverlayX = READ_UINT16(g_topCatAnswersScreenPositions + answerSlot * 4);
+	int16 selectedChoiceOverlayY = READ_UINT16(g_topCatAnswersScreenPositions + answerSlot * 4 + 2);
+	byte *selectedChoiceOverlayPic = getResolvedPtr(g_topCatSelectedChoiceOverlayGfx, answerSlot * 4);
+	displayPic(selectedChoiceOverlayPic, selectedChoiceOverlayX, selectedChoiceOverlayY, stBack);
+
+	_xp->updateDisplay();
+
+	// Start palette cycle on the answer slot overlay...
+	g_topCatChoiceCycleState[0].startIndex = g_topCatCycleSpecs[answerSlot].startIndex;
+	g_topCatChoiceCycleState[0].endIndex = g_topCatCycleSpecs[answerSlot].endIndex;
+	g_topCatChoiceCycleState[0].delay = g_topCatCycleSpecs[answerSlot].delay;
+	g_topCatChoiceCycleState[0].nextFire = g_topCatCycleSpecs[answerSlot].nextFire;
+	g_topCatChoiceCycleState[0].active = g_topCatCycleSpecs[answerSlot].active;
+
+	_xp->startCycle(g_topCatChoiceCycleState);
+
+	// Check answer...
+	int8 correctAnswer = (int8)g_topCatAnswers[g_topCatCurrentAnswerIdx * 3];
+
+	if (correctAnswer == answerSlot) {
+		// Correct! :-)
+		queueAnim(4, 0); // Correct answer animation
+		queueAnim(6, 0); // Blink the lights
+
+		g_topCatShuffledQuestions[g_topCatShuffledQuestionsArrayIdx] |= 0x80; // Answered!
+		g_topCatScore++;
+
+		if (g_topCatScore == 6) {
+			queueAnim(6, 0); // Blink the lights
+			*result = 0x10;
+			return 1;
+		}
+
+		queueAnim(3, 1); // Transition to next question
+	} else {
+		// Wrong... :-(
+		queueAnim(5, 0); // Wrong answer animation
+		queueAnim(3, 1); // Transition to next question
+	}
+
+	return 0;
+}
+
+void BoltEngine::queueAnim(int16 animType, int16 param) {
+	int16 slot = g_topCatAnimQueueSize;
+	g_topCatAnimQueueSize++;
+
+	TopCatAnim *entry = &g_topCatAnimQueue[slot];
+
+	if (g_topCatAnimQueueSize > 3) {
+		g_topCatAnimQueueSize--;
+		return;
+	}
+
+	entry->animType = animType;
+
+	switch (animType) {
+	case 2: // Help audio
+		entry->animIndex = 0x1F;
+		break;
+
+	case 3: // Question audio
+		entry->transitionToNextQuestionFlag = param;
+		break;
+
+	case 4: // Correct answer
+		entry->animIndex = g_topCatCorrectAnimIdx;
+		g_topCatCorrectAnimIdx++;
+		g_topCatCorrectAnimIdx %= 3;
+		break;
+
+	case 5: // Wrong answer
+		entry->animIndex = g_topCatWrongAnimIdx + 3;
+		g_topCatWrongAnimIdx++;
+		g_topCatWrongAnimIdx %= 3;
+		break;
+
+	case 6: // Score light
+		if (g_topCatScore < 6)
+			entry->animIndex = g_topCatScore + 1;
+		else
+			entry->animIndex = 0;
+
+		// Store blink sequence pointer from table
+		entry->seqPtr = g_topCatBlinkSeqTable[g_topCatScore];
+		break;
+	}
+}
+
+bool BoltEngine::maintainAnim(int16 soundEvent) {
+	int16 animDone = 0;
+
+	switch (g_topCatAnimStateMachineStep) {
+	case 0: // Waiting to start next anim
+		if (g_topCatAnimQueueSize == 0)
+			break;
+
+		if (!startNextAnim(&g_topCatShouldPlayAnswerAnim))
+			return false;
+
+		g_topCatAnimStateMachineStep = 1;
+		break;
+	case 1: // Filling RTF buffer
+		if (fillRTFBuffer()) {
+			if (fillRTFBuffer())
+				break;
+		}
+
+		g_topCatAnimStateMachineStep = 2;
+		break;
+	case 2: // Waiting for sound sync
+		if (!soundEvent)
+			break;
+
+		if (g_topCatMaxBackgroundAnimFrames - g_topCatQueuedSoundFrames != g_topCatBackgroundAnimFrame)
+			break;
+
+		flushRTFSoundQueue();
+		g_topCatAnimStateMachineStep = 3;
+		break;
+	case 3: // Waiting for AV end
+	case 4: // Playing AV
+		if (g_topCatAnimStateMachineStep == 3) {
+			if (soundEvent != 2)
+				break;
+
+			g_topCatAnimStateMachineStep = 4;
+			g_topCatMaintainSoundFlag = 1;
+		}
+
+		int16 avResult;
+		if (g_topCatShouldPlayAnswerAnim != 0)
+			avResult = maintainAV(soundEvent == 2 ? 1 : 0);
+		else
+			avResult = maintainAudioPlay(soundEvent == 2 ? 1 : 0);
+
+		if (!avResult) { // AV finished
+			if (g_topCatShouldPlayAnswerAnim != 0) {
+				// Display background anim frame...
+				byte *animFrame = getResolvedPtr(g_topCatGraphicsAssets, g_topCatBackgroundAnimFrame * 4);
+				displayPic(animFrame, g_displayX, g_displayY, stFront);
+			}
+
+			animDone = 1;
+		} else {
+			// Check if idle sound needs maintaining...
+			if (g_topCatMaintainSoundFlag != 0) {
+				if (!isRTFPlaying()) {
+					int16 savedFrame = g_topCatBackgroundAnimFrame;
+					g_topCatBackgroundAnimFrame = 0;
+					maintainIdleSound(0);
+					g_topCatBackgroundAnimFrame = savedFrame;
+					g_topCatMaintainSoundFlag = 0;
+				}
+			}
+		}
+
+		break;
+	}
+
+	// Advance audio and the background animation together...
+	if (soundEvent == 1 || (soundEvent == 2 && g_topCatShouldPlayAnswerAnim == 0)) {
+		g_topCatBackgroundAnimFrame++;
+		if (g_topCatBackgroundAnimFrame >= g_topCatMaxBackgroundAnimFrames)
+			g_topCatBackgroundAnimFrame = 0;
+
+		if (soundEvent == 1) {
+			if (g_topCatAnimStateMachineStep == 3 || g_topCatAnimStateMachineStep == 4) {
+				g_topCatQueuedSoundFrames--;
+			} else {
+				maintainIdleSound(1);
+			}
+		}
+
+		// If no pending sound event, display the next background anim frame...
+		uint32 peekData;
+		if (_xp->peekEvent(etSound, &peekData) != etSound) {
+			byte *backgroundFrame = getResolvedPtr(g_topCatGraphicsAssets, g_topCatBackgroundAnimFrame * 4);
+			displayPic(backgroundFrame, g_displayX, g_displayY, stFront);
+			_xp->updateDisplay();
+		}
+	}
+
+	if (animDone)
+		setAnimType(1);
+
+	return true;
+}
+
+void BoltEngine::maintainIdleSound(int16 decrement) {
+	byte *soundData = g_topCatSoundInfo.data;
+	int16 chunkSize = (int16)(g_topCatSoundInfo.size / g_topCatMaxBackgroundAnimFrames);
+
+	if (decrement)
+		g_topCatQueuedSoundFrames--;
+
+	int16 idx = g_topCatBackgroundAnimFrame + g_topCatQueuedSoundFrames;
+
+	while (g_topCatQueuedSoundFrames < 10) {
+		g_topCatQueuedSoundFrames++;
+
+		if (idx >= g_topCatMaxBackgroundAnimFrames)
+			idx -= g_topCatMaxBackgroundAnimFrames;
+
+		_xp->playSound(soundData + chunkSize * idx, chunkSize, 22050);
+		idx++;
+	}
+}
+
+bool BoltEngine::startNextAnim(int16 *playAnswerAnim) {
+	TopCatAnim *entry = &g_topCatAnimQueue[0];
+	bool startResult = false;
+
+	if (g_topCatAnimQueueSize == 0)
+		return false;
+
+	// Setup next question, if requested...
+	if (entry->animType == 3) {
+		if (entry->transitionToNextQuestionFlag != 0) {
+			if (!setupNextQuestion())
+				return false;
+
+			_xp->updateDisplay();
+		}
+
+		entry->animIndex = g_topCatCurrentAnswerIdx * 60 + g_topCatDrawnQuestionId + 7;
+	}
+
+	int16 animType = entry->animType;
+
+	if (animType == 6) {
+		// Score light animation
+		g_topCatBlinkSeqPtr = entry->seqPtr;
+
+		*playAnswerAnim = 0;
+		startResult = startAnimation(g_topCatAvHandle, entry->animIndex);
+	} else if (animType == 3) {
+		// Question audio
+		g_topCatCycleStep = 3;
+
+		*playAnswerAnim = 0;
+		startResult = startAnimation(g_topCatAvHandle, entry->animIndex);
+	} else if (animType == 2) {
+		// Help audio
+		g_topCatCycleStep = 0;
+
+		*playAnswerAnim = 0;
+		startResult = startAnimation(g_rtfHandle, entry->animIndex);
+	} else {
+		// Correct/wrong answer animations
+		*playAnswerAnim = 1;
+		setAVBufferSize(0x4B000);
+		startResult = prepareAV(g_topCatRtfHandle, entry->animIndex,
+								g_displayWidth, g_displayHeight,
+								g_displayX, g_displayY);
+	}
+
+	if (startResult) {
+		setAnimType(entry->animType);
+	} else {
+		g_topCatCurrentAnimType = 1;
+		g_topCatAnimStateMachineStep = 0;
+		maintainIdleSound(0);
+	}
+
+	// Shift queue: copy entries 1..n down to 0..n-1
+	for (int16 i = 1; i < g_topCatAnimQueueSize; i++) {
+		g_topCatAnimQueue[i - 1] = g_topCatAnimQueue[i];
+	}
+
+	g_topCatAnimQueueSize--;
+
+	return true;
+}
+
+void BoltEngine::setAnimType(int16 newType) {
+	if (g_topCatCurrentAnimType == newType)
+		return;
+
+	// Clean-up the previous state...
+	switch (g_topCatCurrentAnimType) {
+	case 2: // Was playing the help audio
+		// Stop blinking entry
+		int16 highlight;
+		if (g_topCatBlinkEntry == g_topCatHoveredEntry &&
+			g_topCatBlinkEntry != 0) {
+			if (READ_UINT32(g_topCatBlinkEntry) < 3 || READ_UINT32(g_topCatBlinkEntry) >= 6) {
+				highlight = 1;
+			} else {
+				highlight = 0;
+			}
+		} else {
+			highlight = 0;
+		}
+
+		highlightObject(g_topCatBlinkEntry, highlight);
+
+		g_topCatBlinkEntry = 0;
+		g_topCatBlinkTimer = 0;
+		highlightObject(g_topCatBlinkEntry, 1);
+		break;
+
+	case 3: // Was playing the question audio
+		if (g_topCatCycleStep >= 3) {
+			byte *prevEntry = getResolvedPtr(g_topCatButtonsPalette, (g_topCatCycleStep - 1) * 4);
+			highlightObject(prevEntry, 0);
+		}
+
+		highlightObject(g_topCatBackButton, 0);
+		break;
+
+	case 4: // Correct answer anim
+		break;
+
+	case 5: // Wrong answer anim
+	case 6: // Score lights
+		if (g_topCatScore != 6)
+			_xp->showCursor();
+
+		break;
+
+	default:
+		break;
+	}
+
+	// Setup the next state
+	switch (newType) {
+	case 1: // Idle
+		maintainIdleSound(0);
+		break;
+
+	case 2: // Help audio
+		break;
+
+	case 3: // Question audio
+		highlightObject(g_topCatBackButton, 1);
+		break;
+
+	case 4: // Correct answer anim
+	case 5: // Wrong answer anim
+		_xp->hideCursor();
+		break;
+
+	default:
+		break;
+	}
+
+	g_topCatCurrentAnimType = newType;
+	g_topCatAnimStateMachineStep = 0;
+}
+
+void BoltEngine::highlightObject(byte *entry, int16 highlight) {
+	if (!entry)
+		return;
+
+	if (highlight) {
+		_xp->setPalette(READ_UINT16(entry + 0x10), READ_UINT16(entry + 0x0E), entry + 0x12);
+		WRITE_UINT16(entry + 0x0C, READ_UINT16(entry + 0x0C) | 1);
+	} else {
+		_xp->setPalette(READ_UINT16(entry + 0x10), READ_UINT16(entry + 0x0E), entry + 0x2E);
+		WRITE_UINT16(entry + 0x0C, READ_UINT16(entry + 0x0C) & ~1);
+	}
+
+	_system->updateScreen();
+}
+
+bool BoltEngine::setupNextQuestion() {
+	_xp->stopCycle();
+
+	int16 attempts = 0;
+	while (attempts < 60) {
+		g_topCatShuffledQuestionsArrayIdx++;
+		if (g_topCatShuffledQuestionsArrayIdx >= 60)
+			g_topCatShuffledQuestionsArrayIdx = 0;
+
+		g_topCatDrawnQuestionId = (int8)g_topCatShuffledQuestions[g_topCatShuffledQuestionsArrayIdx];
+
+		if ((g_topCatDrawnQuestionId & 0x80) == 0)
+			break; // Found a question which hasn't been answered yet...
+
+		attempts++;
+	}
+
+	if (attempts >= 60) {
+		g_topCatShuffledQuestionsArrayIdx = -1;
+		shuffleTopCatQuestions();
+		shuffleTopCatPermutations();
+
+		for (int16 i = 0; i < 60; i++) {
+			g_topCatShuffledQuestions[i] &= 0x7F; // Remove "already answered" flag from all questions...
+		}
+
+		return setupNextQuestion();
+	}
+
+	g_topCatShuffledAnswers[g_topCatDrawnQuestionId]++;
+	if (g_topCatShuffledAnswers[g_topCatDrawnQuestionId] >= 3)
+		g_topCatShuffledAnswers[g_topCatDrawnQuestionId] = 0;
+
+	int16 qIdx = g_topCatDrawnQuestionId;
+	int16 permutation = (int8)g_topCatShuffledAnswers[g_topCatDrawnQuestionId];
+	g_topCatCurrentAnswerIdx = (int8)g_topCatAnswersPermutations[qIdx * 3 + permutation];
+
+	int16 groupId = g_topCatDrawnQuestionId * 0x100 + 0x100;
+	if (!getBOLTGroup(g_topCatBoltLib, groupId, 1))
+		return false;
+
+	byte *questionsAnswersGfxTable = memberAddr(g_topCatBoltLib, g_topCatDrawnQuestionId * 0x100 + 0x104);
+
+	// Display question pic...
+	byte *questionPic = getResolvedPtr(questionsAnswersGfxTable, 0);
+	displayPic(questionPic, 50, 38, stBack);
+
+	// Display the three answer choices...
+	int16 tableOff = 0;
+	for (int16 i = 0; i < 3; i++) {
+		int8 answerIdx = (int8)g_topCatAnswers[g_topCatCurrentAnswerIdx * 3 + i];
+
+		int16 x = READ_UINT16(g_topCatAnswersScreenPositions + answerIdx * 4);
+		int16 y = READ_UINT16(g_topCatAnswersScreenPositions + answerIdx * 4 + 2);
+
+		int16 colorShift = (answerIdx - i) * 3;
+		byte *answerPic = getResolvedPtr(questionsAnswersGfxTable, tableOff + 4);
+		adjustColors(answerPic, colorShift);
+		displayPic(answerPic, x, y, stBack);
+
+		tableOff += 4;
+	}
+
+	freeBOLTGroup(g_topCatBoltLib, groupId, 1);
+
+	return true;
+}
+
+void BoltEngine::adjustColors(byte *pic, int8 shift) {
+	int16 count = READ_UINT16(pic + 0x0C);
+	byte *data = getResolvedPtr(pic, 0x12);
+
+	while (count) {
+		byte val = *data;
+
+		if (val & 0x7F) {
+			*data += shift;
+		}
+
+		data++;
+
+		if (val & 0x80) {
+			if (*data == 0)
+				count--;
+			data++;
+		}
+	}
+}
+
+void BoltEngine::shuffleTopCatQuestions() {
+	int16 answerOff = 0;
+
+	for (int16 i = 0; i < 60; i++) {
+		int16 randIdx = _xp->getRandom(60);
+
+		byte tmp = g_topCatShuffledQuestions[i];
+		g_topCatShuffledQuestions[i] = g_topCatShuffledQuestions[randIdx];
+		g_topCatShuffledQuestions[randIdx] = tmp;
+
+		for (int16 j = 0; j < 3; j++) {
+			int16 randJ = _xp->getRandom(3);
+
+			byte aTmp = g_topCatAnswersPermutations[answerOff + j];
+			g_topCatAnswersPermutations[answerOff + j] = g_topCatAnswersPermutations[answerOff + randJ];
+			g_topCatAnswersPermutations[answerOff + randJ] = aTmp;
+		}
+
+		answerOff += 3;
+	}
+}
+
+void BoltEngine::shuffleTopCatPermutations() {
+	// Shuffle first permutation table...
+	for (int16 i = 0; i < 3; i++) {
+		int16 randIdx = _xp->getRandom(3);
+
+		int16 tmp = g_topCatPermTableA[i];
+		g_topCatPermTableA[i] = g_topCatPermTableA[randIdx];
+		g_topCatPermTableA[randIdx] = tmp;
+	}
+
+	// Shuffle second permutation table...
+	for (int16 i = 0; i < 3; i++) {
+		int16 randIdx = _xp->getRandom(3);
+
+		int16 tmp = g_topCatPermTableB[i];
+		g_topCatPermTableB[i] = g_topCatPermTableB[randIdx];
+		g_topCatPermTableB[randIdx] = tmp;
+	}
+}
+
+void BoltEngine::getTopCatSoundInfo(BOLTLib *lib, int16 memberId, FredSoundInfo *soundInfo) {
+	soundInfo->data = memberAddr(lib, memberId);
+	soundInfo->size = memberSize(lib, memberId);
+}
+
+void BoltEngine::setScoreLights(int16 litMask) {
+	int16 lightIdx = 0;
+	int16 tableOff = 0;
+
+	while (tableOff < 0x18) {
+		byte *lightEntry = getResolvedPtr(g_topCatLightsPalette, tableOff);
+
+		int16 palStart = READ_UINT16(lightEntry) + 0x80;
+		int16 colorCount = READ_UINT16(lightEntry + 4);
+
+		int16 palDataIdx;
+		if (litMask & (1 << lightIdx)) {
+			palDataIdx = 0;
+		} else {
+			palDataIdx = colorCount;
+		}
+
+		byte *palData = lightEntry + (palDataIdx + 2) * 4;
+		_xp->setPalette(colorCount, palStart, palData);
+
+		tableOff += 4;
+		lightIdx++;
+	}
+
+	_system->updateScreen();
+}
+
+void BoltEngine::swapTopCatHelpEntry() {
+	byte *data = g_boltCurrentMemberEntry->dataPtr;
+	uint32 decompSize = g_boltCurrentMemberEntry->decompSize;
+	int16 offset = 0;
+
+	while (offset < (int32)decompSize) {
+		WRITE_UINT32(data + offset + 0x00, READ_BE_UINT32(data + offset + 0x00));
+		WRITE_UINT16(data + offset + 0x04, READ_BE_UINT16(data + offset + 0x04));
+		WRITE_UINT16(data + offset + 0x06, READ_BE_UINT16(data + offset + 0x06));
+		WRITE_UINT16(data + offset + 0x08, READ_BE_UINT16(data + offset + 0x08));
+		WRITE_UINT16(data + offset + 0x0A, READ_BE_UINT16(data + offset + 0x0A));
+		WRITE_UINT16(data + offset + 0x0C, READ_BE_UINT16(data + offset + 0x0C));
+		WRITE_UINT16(data + offset + 0x0E, READ_BE_UINT16(data + offset + 0x0E));
+		WRITE_UINT16(data + offset + 0x10, READ_BE_UINT16(data + offset + 0x10));
+
+		offset += 0x4A;
+	}
+}
 
 } // End of namespace Bolt
diff --git a/engines/bolt/resource.cpp b/engines/bolt/resource.cpp
index c84fac76c80..29e024c643e 100644
--- a/engines/bolt/resource.cpp
+++ b/engines/bolt/resource.cpp
@@ -647,8 +647,12 @@ BOLTCallback BoltEngine::g_defaultGroupFreeCallbacks[25];
 BOLTCallback BoltEngine::g_fredTypeLoadCallbacks[28];
 BOLTCallback BoltEngine::g_fredTypeFreeCallbacks[28];
 
+BOLTCallback BoltEngine::g_topCatTypeLoadCallbacks[26];
+BOLTCallback BoltEngine::g_topCatTypeFreeCallbacks[26];
+
 void BoltEngine::noOpCb() {}
 void BoltEngine::swapAllWordsCb() { ((BoltEngine *)g_engine)->swapAllWords(); }
+void BoltEngine::swapAllLongsCb() { ((BoltEngine *)g_engine)->swapAllLongs(); }
 void BoltEngine::swapPicHeaderCb() { ((BoltEngine *)g_engine)->swapPicHeader(); }
 void BoltEngine::swapAndResolvePicDescCb() { ((BoltEngine *)g_engine)->swapAndResolvePicDesc(); }
 void BoltEngine::swapFirstWordCb() { ((BoltEngine *)g_engine)->swapFirstWord(); }
@@ -662,6 +666,8 @@ void BoltEngine::swapFredAnimEntryCb() { ((BoltEngine *)g_engine)->swapFredAnimE
 void BoltEngine::swapFredAnimDescCb() { ((BoltEngine *)g_engine)->swapFredAnimDesc(); }
 void BoltEngine::swapFredLevelDescCb() { ((BoltEngine *)g_engine)->swapFredLevelDesc(); }
 
+void BoltEngine::swapTopCatHelpEntryCb() { ((BoltEngine *)g_engine)->swapTopCatHelpEntry(); }
+
 void BoltEngine::initCallbacks() {
 	// --- BOOTHS ---
 
@@ -734,6 +740,33 @@ void BoltEngine::initCallbacks() {
 	g_fredBoltCallbacks.memberFreeCallbacks = g_defaultMemberFreeCallbacks;
 	g_fredBoltCallbacks.groupLoadCallbacks = g_defaultGroupLoadCallbacks;
 	g_fredBoltCallbacks.groupFreeCallbacks = g_defaultGroupFreeCallbacks;
+
+	// --- TOPCAT ---
+	for (int i = 0; i < ARRAYSIZE(g_topCatTypeLoadCallbacks); i++) {
+		g_topCatTypeLoadCallbacks[i] = noOpCb;
+	}
+
+	g_topCatTypeLoadCallbacks[2] = swapAllWordsCb;
+	g_topCatTypeLoadCallbacks[4] = swapAllLongsCb;
+	g_topCatTypeLoadCallbacks[6] = resolveAllRefsCb;
+	g_topCatTypeLoadCallbacks[8] = swapSpriteHeaderCb;
+	g_topCatTypeLoadCallbacks[10] = swapPicHeaderCb;
+	g_topCatTypeLoadCallbacks[11] = swapAndResolvePicDescCb;
+	g_topCatTypeLoadCallbacks[12] = swapFirstTwoWordsCb;
+	g_topCatTypeLoadCallbacks[25] = swapTopCatHelpEntryCb;
+
+	for (int i = 0; i < ARRAYSIZE(g_topCatTypeFreeCallbacks); i++) {
+		g_topCatTypeFreeCallbacks[i] = noOpCb;
+	}
+
+	g_topCatTypeFreeCallbacks[8] = freeSpriteCleanUpCb;
+
+	g_topCatBoltCallbacks.typeLoadCallbacks = g_topCatTypeLoadCallbacks;
+	g_topCatBoltCallbacks.typeFreeCallbacks = g_topCatTypeFreeCallbacks;
+	g_topCatBoltCallbacks.memberLoadCallbacks = g_defaultMemberLoadCallbacks;
+	g_topCatBoltCallbacks.memberFreeCallbacks = g_defaultMemberFreeCallbacks;
+	g_topCatBoltCallbacks.groupLoadCallbacks = g_defaultGroupLoadCallbacks;
+	g_topCatBoltCallbacks.groupFreeCallbacks = g_defaultGroupFreeCallbacks;
 }
 
 } // End of namespace Bolt
diff --git a/engines/bolt/xplib/events.cpp b/engines/bolt/xplib/events.cpp
index 873bd4e509e..c4e7c39683f 100644
--- a/engines/bolt/xplib/events.cpp
+++ b/engines/bolt/xplib/events.cpp
@@ -53,7 +53,7 @@ void XpLib::shutdownEvents() {
 	}
 }
 
-int16 XpLib::getEvent(int16 filter, uint32 *outData) {
+int16 XpLib::getEvent(int16 filter, uint32 *outData, byte **outPtrData) {
 	pumpMessages();
 
 	// Search event queue for matching event type...
@@ -75,6 +75,8 @@ int16 XpLib::getEvent(int16 filter, uint32 *outData) {
 
 	// Copy event data to caller...
 	*outData = node->payload;
+	if (outPtrData)
+		*outPtrData = node->payloadPtr;
 
 	// Return node to free list...
 	node->next = g_eventFreeList;
@@ -83,7 +85,7 @@ int16 XpLib::getEvent(int16 filter, uint32 *outData) {
 	return node->type;
 }
 
-int16 XpLib::peekEvent(int16 filter, uint32 *outData) {
+int16 XpLib::peekEvent(int16 filter, uint32 *outData, byte **outPtrData) {
 	pumpMessages();
 
 	XPEvent *node = g_eventQueueHead;
@@ -140,13 +142,14 @@ void XpLib::enqueueEvent(XPEvent *node) {
 
 void XpLib::pumpMessages() {
 	Common::Event event;
-	uint32 timerData;
+	uint32 dummy = 0;
+	byte *soundDataPtr;
 	bool mouseMoved = false;
 
 	// Drain any pending sound events...
-	while (pollSound(&timerData) && !_bolt->shouldQuit()) {
+	while (pollSound(&soundDataPtr) && !_bolt->shouldQuit()) {
 		updateTimers();
-		postEvent(etSound, timerData);
+		postEvent(etSound, dummy, soundDataPtr);
 	}
 
 	cycleColors();
@@ -347,7 +350,7 @@ void XpLib::postJoystickEvent(int16 source, int16 dx, int16 dy) {
 	}
 }
 
-void XpLib::postEvent(XPEventTypes type, uint32 data) {
+void XpLib::postEvent(XPEventTypes type, uint32 data, byte *ptrData) {
 	if (type == etMouseMove) {
 		if (g_lastMouseEventData == data)
 			return;
@@ -387,6 +390,11 @@ void XpLib::postEvent(XPEventTypes type, uint32 data) {
 	node->type = type;
 	node->payload = data;
 	node->next = nullptr;
+
+	if (type == etSound) {
+		node->payloadPtr = ptrData;
+	}
+
 	enqueueEvent(node);
 }
 
diff --git a/engines/bolt/xplib/sound.cpp b/engines/bolt/xplib/sound.cpp
index 53e8c8d38cb..0d0af282a8b 100644
--- a/engines/bolt/xplib/sound.cpp
+++ b/engines/bolt/xplib/sound.cpp
@@ -47,8 +47,9 @@ bool XpLib::initSound() {
 	return true;
 }
 
-bool XpLib::pollSound(uint32 *outData) {
-	*outData = 0;
+bool XpLib::pollSound(byte **outData) {
+	if (outData)
+		*outData = nullptr;
 
 	if (_sndQueued == 0 || _sndPaused)
 		return false;
@@ -92,9 +93,16 @@ bool XpLib::pollSound(uint32 *outData) {
 	if (!ready)
 		return false;
 
-	*outData = 1; // The original puts the elapsed buffer data in here...
-
 	// Buffer completed!
+	byte *sourcePtr = nullptr;
+	if (!_bufferSourceQueue.empty()) {
+		sourcePtr = _bufferSourceQueue.front();
+		_bufferSourceQueue.pop();
+	}
+
+	if (outData)
+		*outData = sourcePtr;
+
 	_sndCompletedCount--;
 	_sndQueued--;
 
@@ -136,9 +144,10 @@ bool XpLib::playSound(byte *data, uint32 size, int16 sampleRate) {
 	// Drain completed buffers if full...
 	int retries = 0;
 	while (_audioStream->numQueuedStreams() >= 50) {
-		uint32 eventData;
+		byte *eventData;
+		uint32 dummy = 0;
 		if (pollSound(&eventData))
-			postEvent(etSound, eventData);
+			postEvent(etSound, dummy, eventData);
 
 		_bolt->_system->delayMillis(1);
 		if (++retries > 5000) {
@@ -168,6 +177,7 @@ bool XpLib::playSound(byte *data, uint32 size, int16 sampleRate) {
 
 	_sndQueued++;
 	_durationQueue.push(durationMs);
+	_bufferSourceQueue.push(data);
 
 	return true;
 }
@@ -207,6 +217,9 @@ bool XpLib::stopSound() {
 	while (!_durationQueue.empty())
 		_durationQueue.pop();
 
+	while (!_bufferSourceQueue.empty())
+		_bufferSourceQueue.pop();
+
 	uint32 dummy;
 	while (getEvent(etSound, &dummy) == etSound);
 
diff --git a/engines/bolt/xplib/xplib.h b/engines/bolt/xplib/xplib.h
index bc30e2012f2..85a034f5e61 100644
--- a/engines/bolt/xplib/xplib.h
+++ b/engines/bolt/xplib/xplib.h
@@ -66,13 +66,13 @@ struct XPCycleState {
 };
 
 typedef struct XPPicDesc {
-	byte *pixelData;     // +0x00: pixel buffer (8bpp)
-	int16 width;         // +0x04: image width
-	int16 height;        // +0x06: image height
-	byte *palette;       // +0x08: RGB palette data
-	int16 paletteStart;  // +0x0C: first palette index
-	int16 paletteCount;  // +0x0E: number of palette entries
-	int16 flags;         // +0x10: bit 0 = transparent, bit 1 = RLE
+	byte *pixelData;
+	int16 width;
+	int16 height;
+	byte *palette;
+	int16 paletteStart;
+	int16 paletteCount;
+	int16 flags;
 
 	XPPicDesc() {
 		pixelData = nullptr;
@@ -93,14 +93,14 @@ enum XPSurfaceType : int {
 typedef struct XPSurface {
 	XPPicDesc mainPic;
 	XPPicDesc overlayPic;
-	int16 dirtyPalStart; // +0x24
-	int16 dirtyPalEnd;   // +0x26
+	int16 dirtyPalStart;
+	int16 dirtyPalEnd;
 
 	XPSurface() {
 		dirtyPalStart = 0;
 		dirtyPalEnd = 0;
 	}
-} XPSurface;             // total: 0x28 = 40 bytes
+} XPSurface;
 
 enum XPEventTypes : int16 {
 	etEmpty      = 0,
@@ -129,12 +129,14 @@ typedef struct XPEvent {
 	XPEvent *next;
 	XPEventTypes type;
 	uint32 payload;
+	byte *payloadPtr;
 
 	XPEvent() {
 		prev = nullptr;
 		next = nullptr;
 		type = etEmpty;
 		payload = 0;
+		payloadPtr = nullptr;
 	}
 } XPEvent;
 
@@ -182,9 +184,9 @@ public:
 	void updateCursorPosition();
 
 	// Events
-	int16 getEvent(int16 filter, uint32 *outData);
-	int16 peekEvent(int16 filter, uint32 *outData);
-	void postEvent(XPEventTypes type, uint32 data);
+	int16 getEvent(int16 filter, uint32 *outData, byte **outPtrData = nullptr);
+	int16 peekEvent(int16 filter, uint32 *outData, byte **outPtrData = nullptr);
+	void postEvent(XPEventTypes type, uint32 data, byte *ptrData = nullptr);
 	int16 setInactivityTimer(int16 seconds);
 	int16 setScreenSaverTimer(int16 seconds);
 	bool enableController();
@@ -213,7 +215,6 @@ public:
 	void freeMem(void *mem);
 
 	// Sound
-	void waveCb();
 	bool playSound(byte *data, uint32 size, int16 sampleRate);
 	bool pauseSound();
 	bool resumeSound();
@@ -346,7 +347,6 @@ protected:
 	Common::Rect g_prevOverlayCursorRect;
 
 	byte *g_vgaFramebuffer = nullptr;
-	//Graphics::Surface *g_videoSurface = nullptr;
 	byte *g_rowDirtyFlags = nullptr;
 	byte g_cursorBackgroundSaveBuffer[16 * 16];
 
@@ -357,7 +357,7 @@ protected:
 	void fileError(const char *message);
 
 	// Sound
-	bool pollSound(uint32 *outData);
+	bool pollSound(byte **outData);
 	bool initSound();
 	void shutdownSound();
 
@@ -373,6 +373,7 @@ protected:
 	int16 _sndSampleRate = 22050;
 	uint32 _sndNextDeadline = 0;
 	uint32 _sndBufferQueueTime = 0;
+	Common::Queue<byte *> _bufferSourceQueue;
 
 	// Timer
 	bool initTimer();


Commit: 1ee07b6140949dc6d417c4ee60bb5f30e2d4a474
    https://github.com/scummvm/scummvm/commit/1ee07b6140949dc6d417c4ee60bb5f30e2d4a474
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-04-07T01:11:14+02:00

Commit Message:
BOLT: CARNIVAL: Add support for full Italian version

Changed paths:
    engines/bolt/detection_tables.h


diff --git a/engines/bolt/detection_tables.h b/engines/bolt/detection_tables.h
index 7453d983699..6f20dde520f 100644
--- a/engines/bolt/detection_tables.h
+++ b/engines/bolt/detection_tables.h
@@ -63,6 +63,42 @@ const ADGameDescription gameDescriptions[] = {
 		ADGF_UNSTABLE,
 		GUIO1(GAMEOPTION_EXTEND_SCREEN)
 	},
+	{
+		"carnival",
+		nullptr,
+		{
+			{"BOOTHS.BLT", 0, "c4d7ce39d1dd754a50bd909c1c2ed695", 1328201},
+			{"FRED.BLT",   0, "2491420b98c2421e19a751422c223cc8", 302133 },
+			{"GEORGE.BLT", 0, "b86116c9dc113d4bb7d6744d378c805c", 1017533},
+			{"HUCK.BLT",   0, "394407bb42a1f0abbe7db73fd0e9b873", 1225354},
+			{"SCOOBY.BLT", 0, "acf03553bc48c070adf57679307c900c", 3733392},
+			{"TOPCAT.BLT", 0, "116facccce1982111a1edf962bec3771", 567135 },
+			{"YOGI.BLT",   0, "ba3d148cbd10cd51f323be7895f52145", 5533358},
+			AD_LISTEND
+		},
+		Common::IT_ITA,
+		Common::kPlatformWindows,
+		ADGF_UNSTABLE,
+		GUIO1(GAMEOPTION_EXTEND_SCREEN)
+	},
+	{
+		"carnival",
+		nullptr,
+		{
+			{"ASSETS/BOOTHS.BLT", 0, "c4d7ce39d1dd754a50bd909c1c2ed695", 1328201},
+			{"ASSETS/FRED.BLT",   0, "2491420b98c2421e19a751422c223cc8", 302133 },
+			{"ASSETS/GEORGE.BLT", 0, "b86116c9dc113d4bb7d6744d378c805c", 1017533},
+			{"ASSETS/HUCK.BLT",   0, "394407bb42a1f0abbe7db73fd0e9b873", 1225354},
+			{"ASSETS/SCOOBY.BLT", 0, "acf03553bc48c070adf57679307c900c", 3733392},
+			{"ASSETS/TOPCAT.BLT", 0, "116facccce1982111a1edf962bec3771", 567135 },
+			{"ASSETS/YOGI.BLT",   0, "ba3d148cbd10cd51f323be7895f52145", 5533358},
+			AD_LISTEND
+		},
+		Common::IT_ITA,
+		Common::kPlatformWindows,
+		ADGF_UNSTABLE,
+		GUIO1(GAMEOPTION_EXTEND_SCREEN)
+	},
 	{
 		"carnival",
 		"Demo",
@@ -95,6 +131,7 @@ const ADGameDescription gameDescriptions[] = {
 	},
 
 	AD_TABLE_END_MARKER
+
 };
 
 } // End of namespace Bolt


Commit: cc069232f44fa0bdb0110c62b78f3eb74771bdc4
    https://github.com/scummvm/scummvm/commit/cc069232f44fa0bdb0110c62b78f3eb74771bdc4
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-04-07T01:11:14+02:00

Commit Message:
BOLT: CARNIVAL: Implement Scooby-Doo's minigame

Changed paths:
    engines/bolt/bolt.h
    engines/bolt/booths/fred.cpp
    engines/bolt/booths/scooby.cpp
    engines/bolt/booths/topcat.cpp
    engines/bolt/resource.cpp
    engines/bolt/rtf.cpp
    engines/bolt/ssprite.cpp


diff --git a/engines/bolt/bolt.h b/engines/bolt/bolt.h
index 20605cacff7..5c9f70f4025 100644
--- a/engines/bolt/bolt.h
+++ b/engines/bolt/bolt.h
@@ -225,17 +225,88 @@ typedef struct RTFPacket {
 	}
 } RTFPacket;
 
+struct SSprite;
+
+typedef void (*SSpriteUpdateFunc)(void);
+typedef void (*SSpritePathCallback)(SSprite *);
+
+typedef struct SSprite {
+	int16 flags;
+	int16 x;
+	int16 y;
+	int16 frameCount;
+	int16 currentFrame;
+	byte **frameData;
+	SSpriteUpdateFunc updateFunc;
+	SSpritePathCallback pathCallback;
+	int16 frameTimer;
+	int16 frameRate;
+	int16 startFrame;
+	int16 stopFrame;
+	int16 velocityX;
+	int16 velocityY;
+	int16 xLimitLow;
+	int16 xLimitHigh;
+	int16 yLimitLow;
+	int16 yLimitHigh;
+	int16 accelX;
+	int16 accelY;
+	int16 dragX;
+	int16 dragY;
+	byte *pathData;
+	int16 pathOffset;
+	int16 pathLength;
+	int16 collX;
+	int16 collY;
+	int16 collW;
+	int16 collH;
+	int16 userInfo;
+
+	SSprite() {
+		flags = 0;
+		x = 0;
+		y = 0;
+		frameCount = 0;
+		currentFrame = 0;
+		frameData = nullptr;
+		updateFunc = nullptr;
+		pathCallback = nullptr;
+		frameTimer = 0;
+		frameRate = 0;
+		startFrame = 0;
+		stopFrame = 0;
+		velocityX = 0;
+		velocityY = 0;
+		xLimitLow = 0;
+		xLimitHigh = 0;
+		yLimitLow = 0;
+		yLimitHigh = 0;
+		accelX = 0;
+		accelY = 0;
+		dragX = 0;
+		dragY = 0;
+		pathData = nullptr;
+		pathOffset = 0;
+		pathLength = 0;
+		collX = 0;
+		collY = 0;
+		collW = 0;
+		collH = 0;
+		userInfo = 0;
+	}
+} SSprite;
+
 // FRED GAME
 
-typedef struct FredSoundInfo {
+typedef struct SoundInfo {
 	byte *data;
 	uint32 size;
 
-	FredSoundInfo() {
+	SoundInfo() {
 		data = nullptr;
 		size = 0;
 	}
-} FredSoundInfo;
+} SoundInfo;
 
 typedef struct FredEntityState {
 	uint16 flags;
@@ -269,6 +340,83 @@ typedef struct FredEntityState {
 	}
 } FredEntityState;
 
+// SCOOBY GAME
+
+typedef struct ScoobyState {
+	int16 levelNumber;
+	int16 slotIndex[10];
+	int16 levelComplete;
+	int16 wallStates[25][4];
+	int16 scoobyCell;
+	int16 scoobySavedCell;
+	int16 leftNeighbor;
+	int16 rightNeighbor;
+	int16 upNeighbor;
+	int16 downNeighbor;
+	int16 activeLevel;
+	int16 scoobyX;
+	int16 scoobyY;
+	int16 velocityX;
+	int16 velocityY;
+	int16 targetVelocityX;
+	int16 targetVelocityY;
+	int16 transitionTarget;
+	int16 currentAnim;
+	int16 direction;
+	int16 spriteFrameCount;
+	byte *frameData[6];
+
+	ScoobyState() {
+		levelNumber = 0;
+
+		for (int i = 0; i < 10; i++)
+			slotIndex[i] = 0;
+
+		levelComplete = 0;
+
+		for (int i = 0; i < 25; i++)
+			for (int j = 0; j < 4; j++)
+				wallStates[i][j] = 0;
+
+		scoobyCell = 0;
+		scoobySavedCell = 0;
+		leftNeighbor = 0;
+		rightNeighbor = 0;
+		upNeighbor = 0;
+		downNeighbor = 0;
+		activeLevel = 0;
+		scoobyX = 0;
+		scoobyY = 0;
+		velocityX = 0;
+		velocityY = 0;
+		targetVelocityX = 0;
+		targetVelocityY = 0;
+		transitionTarget = 0;
+		currentAnim = 0;
+		direction = 0;
+		spriteFrameCount = 0;
+
+		for (int i = 0; i < 6; i++)
+			frameData[i] = nullptr;
+	}
+} ScoobyState;
+
+typedef struct ScoobyRect {
+	int16 left;
+	int16 right;
+	int16 top;
+	int16 bottom;
+
+	ScoobyRect() {
+		left = 0;
+		right = 0;
+		top = 0;
+		bottom = 0;
+	}
+} ScoobyRect;
+
+// TOPCAT GAME
+
 typedef struct TopCatAnim {
 	int16 animType; // + 00
 	int16 animIndex;// + 02
@@ -628,6 +776,49 @@ protected:
 	byte *g_animRingBuffer = nullptr;
 	Common::File *g_animFileHandle = nullptr;
 
+	// SSprite
+	void setUpSSprite(SSprite *sprite, int16 frameCount, byte **frameData, int16 frameRate, int16 velocityX, int16 velocityY);
+	void animateSSprite(SSprite *sprite, int16 page);
+	void displaySSprite(SSprite *sprite, int16 x, int16 y);
+	void eraseSSprite(SSprite *sprite);
+	void setSSpriteFrames(SSprite *sprite, int16 frameCount, byte **frameData, int16 frameRate);
+	void setSSpriteDrag(SSprite *sprite, int16 dragX, int16 dragY);
+	void setSSpriteAccel(SSprite *sprite, int16 accelX, int16 accelY);
+	void reverseSSpriteAccel(SSprite *sprite);
+	void addSSpriteAccel(SSprite *sprite, int16 dx, int16 dy);
+	void setSSpriteVelocity(SSprite *sprite, int16 vx, int16 vy);
+	void reverseSSpriteVelocity(SSprite *sprite);
+	void setSSpriteStart(SSprite *sprite, int16 startFrame, int16 x, int16 y);
+	void setSSpriteStop(SSprite *sprite, int16 stopFrame);
+	void setSSpritePath(SSprite *sprite, byte *pathData, int16 pathCount, SSpritePathCallback callback);
+	bool inSSprite(SSprite *sprite, int16 x, int16 y);
+	bool sSpriteCollide(SSprite *spriteA, SSprite *spriteB);
+	void setSSpriteCollision(SSprite *sprite, int16 *bounds);
+	bool sSpriteAlive(SSprite *sprite);
+	void getSSpriteLoc(SSprite *sprite, Common::Point *out);
+	void getSSpriteAccel(SSprite *sprite, int16 *out);
+	void getSSpriteVelocity(SSprite *sprite, int16 *out);
+	void getSSpriteDrag(SSprite *sprite, int16 *out);
+	void setSSpriteXLimit(SSprite *sprite, int16 high, int16 low);
+	void setSSpriteYLimit(SSprite *sprite, int16 high, int16 low);
+	void setSSpriteInfo(SSprite *sprite, int16 info);
+	int16 getSSpriteInfo(SSprite *sprite);
+	void freezeSSprite(SSprite *sprite);
+	void unfreezeSSprite(SSprite *sprite);
+
+	int16 g_spriteCollTempX = 0;
+	int16 g_spriteCollTempY = 0;
+	int16 g_spriteCollTempW = 0;
+	int16 g_spriteCollTempH = 0;
+	int16 g_spriteScreenX = 0;
+	int16 g_spriteScreenY = 0;
+	int16 g_spriteCollTempA[4] = { 0 };
+	int16 g_spriteCollTempB[4] = { 0 };
+	int16 g_spriteScreenAX = 0;
+	int16 g_spriteScreenAY = 0;
+	int16 g_spriteScreenBX = 0;
+	int16 g_spriteScreenBY = 0;
+
 	// --- MINIGAMES ---
 
 	// --- FRED ---
@@ -648,8 +839,8 @@ protected:
 	int16 selectBalloonRow();
 	void setFredAnimMode(FredEntityState *state, int16 mode);
 	void renderFredScene();
-	void getFredSoundInfo(BOLTLib *lib, int16 memberId, FredSoundInfo *soundInfo);
-	void playFredSound(FredSoundInfo *oneShot, FredSoundInfo *loop);
+	void getFredSoundInfo(BOLTLib *lib, int16 memberId, SoundInfo *soundInfo);
+	void playFredSound(SoundInfo *oneShot, SoundInfo *loop);
 	void updateFredSound();
 
 	static void resolveAllRefsCb();
@@ -684,12 +875,12 @@ protected:
 	byte *g_fredCurrentHelpObject = nullptr;
 	byte *g_fredHoveredEntry = nullptr;
 
-	FredSoundInfo g_fredSounds[4];
+	SoundInfo g_fredSounds[4];
 
-	FredSoundInfo *g_fredCurrentSound = nullptr;
-	FredSoundInfo *g_fredLoopSound = nullptr;
-	FredSoundInfo *g_fredPendingOneShot = nullptr;
-	FredSoundInfo *g_fredPendingLoop = nullptr;
+	SoundInfo *g_fredCurrentSound = nullptr;
+	SoundInfo *g_fredLoopSound = nullptr;
+	SoundInfo *g_fredPendingOneShot = nullptr;
+	SoundInfo *g_fredPendingLoop = nullptr;
 
 	FredEntityState g_fredSprite;
 
@@ -713,7 +904,79 @@ protected:
 	int16 huckGame(int16 prevBooth) { return 0; }
 
 	// --- SCOOBY ---
-	int16 scoobyGame(int16 prevBooth) { return 0; }
+	bool loadScoobyBaseAssets();
+	void cleanUpScoobyBaseAssets();
+	void displayPicClipHack(byte *pic, int16 offsetX, int16 offsetY, int16 *clipRect, int16 displayMode);
+	void drawMovingWalls(int16 cellIdx, int16 direction, int16 picFrame, int16 bgFrame);
+	void drawAllMovingWalls();
+	void animateTransition(int16 memberIdx);
+	void clearPictMSb(byte *pic);
+	void initScoobyLevelGraphics();
+	bool initScoobyLevelAssets();
+	void cleanUpScoobyLevelGraphics();
+	void setScoobySpriteDirection(int16 startMember);
+	void playSoundMapScooby(int16 memberIdx);
+	void playWallSound();
+	void animateWalls();
+	void decideDirection();
+	void updateScoobySound();
+	void setScoobySound(int16 mode);
+	void updateScoobyLocation();
+	void updateScoobyWalls();
+	void updateScoobyDirection(int16 inputDir);
+	void updateScoobyTransition();
+	bool initScoobyLevel();
+	bool resumeScoobyLevel();
+	bool initScooby();
+	void cleanUpScooby();
+	int16 helpScooby();
+	void hiliteScoobyHelpObject(byte *entry, int16 highlight);
+	int16 xpDirToBOLTDir(uint32 xpDir);
+	int16 playScooby();
+	int16 scoobyGame(int16 prevBooth);
+	void swapScoobyHelpEntry();
+	void swapScoobyWordArray();
+
+	static void swapScoobyHelpEntryCb();
+	static void swapScoobyWordArrayCb();
+
+	BOLTLib *g_scoobyBoltLib = nullptr;
+	BOLTCallbacks g_scoobyBoltCallbacks;
+
+	static BOLTCallback g_scoobyTypeLoadCallbacks[28];
+	static BOLTCallback g_scoobyTypeFreeCallbacks[28];
+
+	int16 g_scoobySoundMode = 0;
+	int16 g_scoobySoundPlaying = 0;
+	int16 g_scoobyShowHelp = 1;
+
+	int16 g_scoobyLastInputDir = 0;
+	int16 g_scoobyTransitionFrom = 0;
+	int16 g_scoobyTransitionTarget = 0;
+	int16 g_scoobyTransitionVelX = 0;
+	int16 g_scoobyTransitionVelY = 0;
+	XPPicDesc g_scoobyTempPic;
+	byte g_scoobyGlobalSaveData[0x3C] = { 0 };
+	ScoobyState g_scoobyGameState;
+	SSprite g_scoobySprite;
+	byte *g_scoobyBaseData = nullptr;
+	byte *g_scoobyBgPic = nullptr;
+	byte *g_scoobyWallPicsA[4] = { nullptr };
+	byte *g_scoobyWallPicsB[5] = { nullptr };
+	ScoobyRect g_scoobyCellBounds[25];
+	Common::Point g_scoobyLevelStartXY[25];
+	byte *g_scoobyLevelData = nullptr;
+	int16 g_scoobySelectedGraphicsGroup = 0;
+	int16 g_scoobyDifficulty = 0;
+	int16 g_scoobyLevelCount = 0;
+	int16 g_scoobyMoveRequested = 0;
+	int16 g_scoobyTransitioning = 0;
+	int16 g_scoobyDesiredDir = 0;
+	int16 g_scoobyInputHoldCount = 0;
+	int16 g_scoobyWallAnimating = 0;
+	int16 g_scoobyWallAnimStep = 0;
+	int16 g_scoobyWallsToOpen = 0;
+	int16 g_scoobyWallsToClose = 0;
 
 	// --- TOPCAT ---
 	int16 topCatGame(int16 prevBooth);
@@ -731,7 +994,7 @@ protected:
 	void adjustColors(byte *pic, int8 shift);
 	void shuffleTopCatQuestions();
 	void shuffleTopCatPermutations();
-	void getTopCatSoundInfo(BOLTLib *lib, int16 memberId, FredSoundInfo *soundInfo);
+	void getTopCatSoundInfo(BOLTLib *lib, int16 memberId, SoundInfo *soundInfo);
 	void setScoreLights(int16 litMask);
 	void swapTopCatHelpEntry();
 
@@ -775,7 +1038,7 @@ protected:
 	int16 g_topCatScore = 0;
 	int16 g_topCatShuffledQuestionsArrayIdx = 0;
 	uint32 g_topCatBlinkTimer = 0;
-	FredSoundInfo g_topCatSoundInfo;
+	SoundInfo g_topCatSoundInfo;
 	byte g_topCatSavedShuffledQuestions[60] = { 0 };
 	byte g_topCatSavedShuffledAnswers[60] = { 0 };
 	byte g_topCatSavedAnswersPermutations[60 * 3] = { 0 };
diff --git a/engines/bolt/booths/fred.cpp b/engines/bolt/booths/fred.cpp
index e4a4a69c534..2622d3ec7ee 100644
--- a/engines/bolt/booths/fred.cpp
+++ b/engines/bolt/booths/fred.cpp
@@ -1206,12 +1206,12 @@ void BoltEngine::renderFredScene() {
 	}
 }
 
-void BoltEngine::getFredSoundInfo(BOLTLib *lib, int16 memberId, FredSoundInfo *soundInfo) {
+void BoltEngine::getFredSoundInfo(BOLTLib *lib, int16 memberId, SoundInfo *soundInfo) {
 	soundInfo->data = memberAddr(lib, memberId);
 	soundInfo->size = memberSize(lib, memberId);
 }
 
-void BoltEngine::playFredSound(FredSoundInfo *oneShot, FredSoundInfo *loop) {
+void BoltEngine::playFredSound(SoundInfo *oneShot, SoundInfo *loop) {
 	g_fredPendingOneShot = oneShot;
 	g_fredPendingLoop = loop; 
 
@@ -1253,12 +1253,12 @@ void BoltEngine::updateFredSound() {
 
 	if (g_fredCurrentSound != nullptr) {
 		// Play the current sound...
-		FredSoundInfo *snd = g_fredCurrentSound;
+		SoundInfo *snd = g_fredCurrentSound;
 		_xp->playSound(snd->data, snd->size, 22050);
 
 		if (startLoop) {
 			// Queue loop sound twice...
-			FredSoundInfo *loopSnd = g_fredLoopSound;
+			SoundInfo *loopSnd = g_fredLoopSound;
 			_xp->playSound(loopSnd->data, loopSnd->size, 22050);
 			_xp->playSound(loopSnd->data, loopSnd->size, 22050);
 		}
diff --git a/engines/bolt/booths/scooby.cpp b/engines/bolt/booths/scooby.cpp
index d0e5b38e0bf..011bcbb5d1a 100644
--- a/engines/bolt/booths/scooby.cpp
+++ b/engines/bolt/booths/scooby.cpp
@@ -21,8 +21,1956 @@
 
 #include "bolt/bolt.h"
 
+#include "common/memstream.h"
+
 namespace Bolt {
 
+bool BoltEngine::loadScoobyBaseAssets() {
+	if (!getBOLTGroup(g_scoobyBoltLib, 0, 1))
+		return false;
+
+	g_scoobyBaseData = memberAddr(g_scoobyBoltLib, 0);
+	return true;
+}
+
+void BoltEngine::cleanUpScoobyBaseAssets() {
+	freeBOLTGroup(g_scoobyBoltLib, 0, 1);
+}
+
+void BoltEngine::displayPicClipHack(byte *pic, int16 offsetX, int16 offsetY, int16 *clipRect, int16 displayMode) {
+	int16 clipW = clipRect[2];
+	int16 clipH = clipRect[3];
+
+	int16 picWidth = READ_UINT16(pic + 0x0A);
+	byte *pixelData = getResolvedPtr(pic, 0x12);
+
+	int32 srcOffset = (clipRect[0] - g_displayX) + picWidth * (clipRect[1] - g_displayY);
+
+	_xp->blit(pixelData + srcOffset, picWidth, g_scoobyTempPic.pixelData, clipW, clipW, clipH);
+
+	g_scoobyTempPic.width = clipW;
+	g_scoobyTempPic.height = clipH;
+	g_scoobyTempPic.palette = nullptr;
+	g_scoobyTempPic.paletteStart = 0;
+	g_scoobyTempPic.paletteCount = 0;
+	g_scoobyTempPic.flags = 0;
+
+	if (*pic & 2)
+		g_scoobyTempPic.flags |= 2;
+
+	_xp->displayPic(&g_scoobyTempPic,
+					READ_UINT16(pic + 6) + offsetX + clipRect[0],
+					READ_UINT16(pic + 8) + offsetY + clipRect[1],
+					displayMode);
+}
+
+void BoltEngine::drawMovingWalls(int16 cellIdx, int16 direction, int16 picFrame, int16 bgFrame) {
+	// Clamp frames to valid range
+	if (picFrame > 5)
+		picFrame = 5;
+
+	if (bgFrame > 4)
+		bgFrame = 4;
+
+	if (picFrame < 0)
+		picFrame = 0;
+
+	if (bgFrame < 0)
+		bgFrame = 0;
+
+	int16 wallMarginX = READ_UINT16(g_scoobyLevelData + 0x0E);
+	int16 wallMarginY = READ_UINT16(g_scoobyLevelData + 0x10);
+
+	int16 posX = 0, posY = 0;
+
+	// Compute position along wall's primary axis
+	switch (direction) {
+	case 0: // up wall
+		posY = g_scoobyCellBounds[cellIdx].top - 1 - wallMarginY;
+		break;
+	case 1: // right wall
+		posX = g_scoobyCellBounds[cellIdx].right - wallMarginX;
+		break;
+	case 2: // down wall
+		posY = g_scoobyCellBounds[cellIdx].bottom - wallMarginY;
+		break;
+	case 3: // left wall
+		posX = g_scoobyCellBounds[cellIdx].left - 1 - wallMarginX;
+		break;
+	default:
+		break;
+	}
+
+	// Draw wall based on orientation
+	switch (direction) {
+	case 1: // right wall (vertical)
+	case 3: // left wall (vertical)
+	{
+		posY = g_scoobyCellBounds[cellIdx].top + wallMarginY + 1;
+
+		byte *basePic = g_scoobyWallPicsA[3];
+		int16 picW = READ_UINT16(basePic + 0x0A);
+		int16 picH = READ_UINT16(basePic + 0x0C);
+
+		int16 clipRect[4];
+		clipRect[0] = posX;
+		clipRect[1] = posY;
+		clipRect[2] = picW;
+		clipRect[3] = picH;
+
+		displayPicClipHack(g_scoobyBgPic, 0, 0, clipRect, 1);
+
+		if (bgFrame != 0) {
+			displayPic(g_scoobyWallPicsA[bgFrame - 1], posX, posY, 1);
+		}
+
+		break;
+	}
+
+	case 0: // up wall (horizontal)
+	case 2: // down wall (horizontal)
+	{
+		posX = g_scoobyCellBounds[cellIdx].left + wallMarginX + 1;
+
+		byte *basePic = g_scoobyWallPicsB[4];
+		int16 picW = READ_UINT16(basePic + 0x0A);
+		int16 picH = READ_UINT16(basePic + 0x0C);
+
+		int16 clipRect[4];
+		clipRect[0] = posX;
+		clipRect[1] = posY;
+		clipRect[2] = picW;
+		clipRect[3] = picH;
+
+		displayPicClipHack(g_scoobyBgPic, 0, 0, clipRect, 1);
+
+		if (picFrame != 0) {
+			displayPic(g_scoobyWallPicsB[picFrame - 1], posX, posY, 1);
+		}
+
+		break;
+	}
+
+	default:
+		break;
+	}
+}
+
+void BoltEngine::drawAllMovingWalls() {
+	ScoobyState *state = &g_scoobyGameState;
+
+	// Phase 1: Draw open passages between adjacent cells (both sides state 2)
+	for (int16 i = 0; i < 25; i++) {
+		int16 col = i % 5;
+
+		// Left wall: this cell's left and left neighbor's right both open
+		if (col != 0) {
+			if (state->wallStates[i][3] == 2 &&
+				state->wallStates[i - 1][1] == 2) {
+				drawMovingWalls(i, 3, 5, 4);
+			}
+		}
+
+		// Right wall: this cell's right and right neighbor's left both open
+		if (col != 4) {
+			if (state->wallStates[i][1] == 2 &&
+				state->wallStates[i + 1][3] == 2) {
+				drawMovingWalls(i, 1, 5, 4);
+			}
+		}
+
+		// Up wall: this cell's up and upper neighbor's down both open
+		if (i > 4) {
+			if (state->wallStates[i][0] == 2 &&
+				state->wallStates[i - 5][2] == 2) {
+				drawMovingWalls(i, 0, 5, 4);
+			}
+		}
+
+		// Down wall: this cell's down and lower neighbor's up both open
+		if (i < 20) {
+			if (state->wallStates[i][2] == 2 &&
+				state->wallStates[i + 5][0] == 2) {
+				drawMovingWalls(i, 2, 5, 4);
+			}
+		}
+	}
+
+	// Phase 2: For non-current cells, draw closed moveable walls (state 1)
+	// Skip if wall connects to current cell and matching border is also 1
+	for (int16 i = 0; i < 25; i++) {
+		if (i == state->scoobyCell)
+			continue;
+
+		for (int16 dir = 0; dir < 4; dir++) {
+			if (state->wallStates[i][dir] != 1)
+				continue;
+
+			bool shouldDraw = true;
+
+			if (i == state->leftNeighbor && dir == 1) {
+				if (state->wallStates[state->scoobyCell][3] == 1)
+					shouldDraw = false;
+			} else if (i == state->rightNeighbor && dir == 3) {
+				if (state->wallStates[state->scoobyCell][1] == 1)
+					shouldDraw = false;
+			} else if (i == state->upNeighbor && dir == 2) {
+				if (state->wallStates[state->scoobyCell][0] == 1)
+					shouldDraw = false;
+			} else if (i == state->downNeighbor && dir == 0) {
+				if (state->wallStates[state->scoobyCell][2] == 1)
+					shouldDraw = false;
+			}
+
+			if (shouldDraw)
+				drawMovingWalls(i, dir, 5, 4);
+		}
+	}
+
+	// Phase 3: Draw dynamic walls (state 3) for the current cell
+	for (int16 dir = 0; dir < 4; dir++) {
+		if (state->wallStates[state->scoobyCell][dir] == 3) {
+			drawMovingWalls(state->scoobyCell, dir, 5, 4);
+		}
+	}
+}
+
+void BoltEngine::animateTransition(int16 memberIdx) {
+	g_scoobyGameState.spriteFrameCount = 1;
+
+	g_scoobyGameState.frameData[0] = memberAddr(g_scoobyBoltLib, memberIdx - 1);
+
+	setSSpriteFrames(&g_scoobySprite, g_scoobyGameState.spriteFrameCount, g_scoobyGameState.frameData, 1);
+
+	g_scoobyGameState.velocityX = 0;
+	g_scoobyGameState.velocityY = 0;
+}
+
+void BoltEngine::clearPictMSb(byte *pic) {
+	// Check if bit 8 of flags word at pic+0 is set (byte at pic+1, bit 0)
+	if (READ_UINT16(pic) & 0x100)
+		return;
+
+	byte *pixelData = getResolvedPtr(pic, 0x12);
+	uint32 size = (uint32)READ_UINT16(pic + 0x0A) * (uint32)READ_UINT16(pic + 0x0C);
+
+	for (uint32 i = 0; i <= size; i++) {
+		pixelData[i] &= 0x7F;
+	}
+}
+
+void BoltEngine::initScoobyLevelGraphics() {
+	// Get palette from level data
+	byte *palette = memberAddr(g_scoobyBoltLib, READ_UINT16(g_scoobyLevelData));
+
+	// Get background pic based on display mode
+	int16 picMember;
+	if (g_displayMode != 0) {
+		picMember = READ_UINT16(g_scoobyLevelData + 4);
+	} else {
+		picMember = READ_UINT16(g_scoobyLevelData + 2);
+	}
+
+	g_scoobyBgPic = memberAddr(g_scoobyBoltLib, picMember);
+	clearPictMSb(g_scoobyBgPic);
+
+	// Drain timer events
+	uint32 dummy;
+	while (_xp->getEvent(etTimer, &dummy) != etTimer);
+
+	_xp->stopCycle();
+	_xp->setTransparency(false);
+
+	// Display palette to front surface
+	displayColors(palette, stBack, 0);
+
+	// Display background to front surface
+	displayPic(g_scoobyBgPic, g_displayX, g_displayY, stFront);
+	_xp->updateDisplay();
+
+	_xp->setTransparency(true);
+
+	// Display palette to both back surfaces
+	displayColors(palette, stFront, 0);
+	displayColors(palette, stBack, 1);
+
+	// Display background to back surface
+	displayPic(g_scoobyBgPic, g_displayX, g_displayY, stBack);
+
+	drawAllMovingWalls();
+
+	// Setup Scooby sprite
+	g_scoobyGameState.spriteFrameCount = 1;
+
+	int16 spriteMember;
+	if (g_scoobyGameState.direction == 6) {
+		spriteMember = 0x23;
+	} else {
+		spriteMember = 0x22;
+	}
+
+	g_scoobyGameState.frameData[0] = memberAddr(g_scoobyBoltLib, spriteMember);
+
+	setUpSSprite(&g_scoobySprite, g_scoobyGameState.spriteFrameCount, g_scoobyGameState.frameData, 1, 0, 0);
+	setSSpriteFrames(&g_scoobySprite, g_scoobyGameState.spriteFrameCount, g_scoobyGameState.frameData, 1);
+	setSSpriteVelocity(&g_scoobySprite, g_scoobyGameState.velocityX, g_scoobyGameState.velocityY);
+	displaySSprite(&g_scoobySprite, g_scoobyGameState.scoobyX, g_scoobyGameState.scoobyY);
+	animateSSprite(&g_scoobySprite, 0);
+
+	// Setup palette cycle
+	XPCycleState cycleData[4];
+	byte *cycleMember = memberAddr(g_scoobyBoltLib, READ_UINT16(g_scoobyLevelData + 0x0A));
+	boltCycleToXPCycle(cycleMember, cycleData);
+	_xp->startCycle(cycleData);
+
+	_xp->updateDisplay();
+}
+
+bool BoltEngine::initScoobyLevelAssets() {
+	uint32 maxPicSize = 0;
+
+	// Compute graphics group index from level number
+	g_scoobySelectedGraphicsGroup = (g_scoobyGameState.levelNumber - 1) * 0x100 + 0x100;
+
+	if (!getBOLTGroup(g_scoobyBoltLib, g_scoobySelectedGraphicsGroup, 1))
+		return false;
+
+	g_scoobyLevelData = memberAddr(g_scoobyBoltLib, g_scoobySelectedGraphicsGroup);
+
+	// Load wall pic set A (4 pics at level data offset +6)
+	for (int16 i = 0; i < 4; i++) {
+		int16 memberIdx = READ_UINT16(g_scoobyLevelData + 6) + i;
+		g_scoobyWallPicsA[i] = memberAddr(g_scoobyBoltLib, memberIdx);
+		clearPictMSb(g_scoobyWallPicsA[i]);
+
+		uint32 picSize = (uint32)READ_UINT16(g_scoobyWallPicsA[i] + 0x0A) *
+						 (uint32)READ_UINT16(g_scoobyWallPicsA[i] + 0x0C);
+		if (picSize > maxPicSize)
+			maxPicSize = picSize;
+	}
+
+	// Load wall pic set B (5 pics at level data offset +8)
+	for (int16 i = 0; i < 5; i++) {
+		int16 memberIdx = READ_UINT16(g_scoobyLevelData + 8) + i;
+		g_scoobyWallPicsB[i] = memberAddr(g_scoobyBoltLib, memberIdx);
+		clearPictMSb(g_scoobyWallPicsB[i]);
+
+		uint32 picSize = (uint32)READ_UINT16(g_scoobyWallPicsB[i] + 0x0A) *
+						 (uint32)READ_UINT16(g_scoobyWallPicsB[i] + 0x0C);
+		if (picSize > maxPicSize)
+			maxPicSize = picSize;
+	}
+
+	// Allocate temp buffer for largest pic
+	g_scoobyTempPic.pixelData = (byte *)_xp->allocMem(maxPicSize);
+	if (!g_scoobyTempPic.pixelData)
+		return false;
+
+	// Determine effective level count (capped at 10)
+	if (g_scoobyGameState.levelNumber > 10) {
+		g_scoobyLevelCount = 10;
+	} else {
+		g_scoobyLevelCount = g_scoobyGameState.levelNumber;
+	}
+
+	// Look up difficulty from global save data
+	int16 levelIdx = g_scoobyLevelCount - 1;
+	int16 globalOffset = levelIdx * 6;
+	int16 saveSlot = g_scoobyGameState.slotIndex[levelIdx];
+	g_scoobyDifficulty = g_scoobyGlobalSaveData[globalOffset + saveSlot * 2];
+
+	// Load wall states for all 25 cells based on difficulty
+	for (int16 i = 0; i < 25; i++) {
+		int16 srcOffset;
+		switch (g_scoobyDifficulty) {
+		case 0:
+			srcOffset = 0x1C;
+			break;
+		case 1:
+			srcOffset = 0xE6;
+			break;
+		case 2:
+			srcOffset = 0x1B0;
+			break;
+		default:
+			continue;
+		}
+
+		for (int16 j = 0; j < 4; j++) {
+			g_scoobyGameState.wallStates[i][j] =
+				READ_UINT16(g_scoobyLevelData + i * 8 + j * 2 + srcOffset);
+		}
+	}
+
+	// Compute cell bounds and start positions for 5x5 grid
+	int16 cellW = READ_UINT16(g_scoobyLevelData + 0x16);
+	int16 cellH = READ_UINT16(g_scoobyLevelData + 0x18);
+
+	int16 yPos = READ_UINT16(g_scoobyLevelData + 0x14);
+	for (int16 row = 0; row < 5; row++) {
+		int16 xPos = READ_UINT16(g_scoobyLevelData + 0x12);
+
+		for (int16 col = 0; col < 5; col++) {
+			int16 cellIdx = row * 5 + col;
+
+			g_scoobyCellBounds[cellIdx].left = xPos;
+			g_scoobyCellBounds[cellIdx].right = xPos + cellW - 1;
+			g_scoobyCellBounds[cellIdx].top = yPos;
+			g_scoobyCellBounds[cellIdx].bottom = yPos + cellH - 1;
+
+			g_scoobyLevelStartXY[cellIdx].x = xPos + ((uint16)cellW >> 1);
+			g_scoobyLevelStartXY[cellIdx].y = yPos + ((uint16)cellH >> 1);
+
+			xPos += cellW;
+		}
+
+		yPos += cellH;
+	}
+
+	return true;
+}
+
+void BoltEngine::cleanUpScoobyLevelGraphics() {
+	_xp->stopCycle();
+	freeBOLTGroup(g_scoobyBoltLib, g_scoobySelectedGraphicsGroup, 1);
+}
+
+void BoltEngine::setScoobySpriteDirection(int16 startMember) {
+	g_scoobyGameState.spriteFrameCount = 6;
+
+	int16 memberIdx = startMember;
+	for (int16 i = 0; i < 6; i++) {
+		g_scoobyGameState.frameData[i] = memberAddr(g_scoobyBoltLib, memberIdx + i);
+	}
+
+	setSSpriteFrames(&g_scoobySprite, g_scoobyGameState.spriteFrameCount, g_scoobyGameState.frameData, 1);
+}
+
+void BoltEngine::playSoundMapScooby(int16 memberIdx) {
+	byte *soundData = getBOLTMember(g_scoobyBoltLib, memberIdx);
+	uint32 soundSize = memberSize(g_scoobyBoltLib, memberIdx);
+
+	if (soundData) {
+		_xp->playSound(soundData, soundSize, 22050);
+	}
+}
+
+void BoltEngine::playWallSound() {
+	if (g_scoobySoundPlaying == 0)
+		return;
+
+	_xp->stopSound();
+
+	int16 soundMember;
+	switch (g_scoobySoundPlaying) {
+	case 1:
+		soundMember = READ_UINT16(g_scoobyLevelData + 0x27C);
+		break;
+	case 2:
+		soundMember = READ_UINT16(g_scoobyLevelData + 0x27E);
+		break;
+	case 3:
+		soundMember = READ_UINT16(g_scoobyLevelData + 0x278);
+		break;
+	case 4:
+		soundMember = READ_UINT16(g_scoobyLevelData + 0x27A);
+		break;
+	case 5:
+		soundMember = READ_UINT16(g_scoobyLevelData + 0x284);
+		break;
+	case 6:
+		soundMember = READ_UINT16(g_scoobyLevelData + 0x286);
+		break;
+	case 7:
+		soundMember = READ_UINT16(g_scoobyLevelData + 0x280);
+		break;
+	case 8:
+		soundMember = READ_UINT16(g_scoobyLevelData + 0x282);
+		break;
+	case 9:
+		soundMember = READ_UINT16(g_scoobyLevelData + 0x288);
+		break;
+	case 10:
+		soundMember = READ_UINT16(g_scoobyLevelData + 0x28A);
+		break;
+	default:
+		return;
+	}
+
+	playSoundMapScooby(soundMember);
+}
+
+void BoltEngine::animateWalls() {
+	// Phase 1: Animate current cell's walls
+	int16 curCell = g_scoobyGameState.scoobyCell;
+
+	for (int16 dir = 0; dir < 4; dir++) {
+		if (g_scoobyGameState.wallStates[curCell][dir] == 1) {
+			// Closed wall, opening animation
+			bool shouldDraw = true;
+
+			switch (dir) {
+			case 0: // up neighbor
+				if (curCell - 5 == g_scoobyGameState.scoobySavedCell) {
+					if (g_scoobyGameState.wallStates[g_scoobyGameState.scoobySavedCell][2] == 1)
+						shouldDraw = false;
+				}
+
+				if (shouldDraw)
+					g_scoobySoundPlaying = 5;
+
+				break;
+			case 1: // right neighbor
+				if (curCell + 1 == g_scoobyGameState.scoobySavedCell) {
+					if (g_scoobyGameState.wallStates[g_scoobyGameState.scoobySavedCell][3] == 1)
+						shouldDraw = false;
+				}
+
+				if (shouldDraw)
+					g_scoobySoundPlaying = 1;
+
+				break;
+			case 2: // down neighbor
+				if (curCell + 5 == g_scoobyGameState.scoobySavedCell) {
+					if (g_scoobyGameState.wallStates[g_scoobyGameState.scoobySavedCell][0] == 1)
+						shouldDraw = false;
+				}
+
+				if (shouldDraw)
+					g_scoobySoundPlaying = 5;
+
+				break;
+			case 3: // left neighbor
+				if (curCell - 1 == g_scoobyGameState.scoobySavedCell) {
+					if (g_scoobyGameState.wallStates[g_scoobyGameState.scoobySavedCell][1] == 1)
+						shouldDraw = false;
+				}
+
+				if (shouldDraw)
+					g_scoobySoundPlaying = 1;
+
+				break;
+			}
+
+			if (shouldDraw) {
+				drawMovingWalls(curCell, dir, 5 - g_scoobyWallAnimStep, 4 - g_scoobyWallAnimStep);
+			}
+		} else if (g_scoobyGameState.wallStates[curCell][dir] == 3) {
+			// Dynamic wall, closing animation
+			if (dir == 3 || dir == 1) {
+				g_scoobySoundPlaying = 4;
+			} else {
+				g_scoobySoundPlaying = 8;
+			}
+
+			drawMovingWalls(curCell, dir, g_scoobyWallAnimStep, g_scoobyWallAnimStep);
+		}
+	}
+
+	// Phase 2: Animate saved cell's walls
+	int16 savedCell = g_scoobyGameState.scoobySavedCell;
+
+	for (int16 dir = 0; dir < 4; dir++) {
+		if (g_scoobyGameState.wallStates[savedCell][dir] == 1) {
+			// Closed wall in saved cell, closing animation
+			bool shouldDraw = true;
+
+			switch (dir) {
+			case 0: // up neighbor
+				if (savedCell - 5 == g_scoobyGameState.scoobyCell) {
+					if (g_scoobyGameState.wallStates[g_scoobyGameState.scoobyCell][2] == 1)
+						shouldDraw = false;
+				}
+
+				if (shouldDraw)
+					g_scoobySoundPlaying = 6;
+
+				break;
+			case 1: // right neighbor
+				if (savedCell + 1 == g_scoobyGameState.scoobyCell) {
+					if (g_scoobyGameState.wallStates[g_scoobyGameState.scoobyCell][3] == 1)
+						shouldDraw = false;
+				}
+
+				if (shouldDraw)
+					g_scoobySoundPlaying = 2;
+
+				break;
+			case 2: // down neighbor
+				if (savedCell + 5 == g_scoobyGameState.scoobyCell) {
+					if (g_scoobyGameState.wallStates[g_scoobyGameState.scoobyCell][0] == 1)
+						shouldDraw = false;
+				}
+
+				if (shouldDraw)
+					g_scoobySoundPlaying = 6;
+
+				break;
+			case 3: // left neighbor
+				if (savedCell - 1 == g_scoobyGameState.scoobyCell) {
+					if (g_scoobyGameState.wallStates[g_scoobyGameState.scoobyCell][1] == 1)
+						shouldDraw = false;
+				}
+
+				if (shouldDraw)
+					g_scoobySoundPlaying = 2;
+
+				break;
+			}
+
+			if (shouldDraw) {
+				drawMovingWalls(savedCell, dir, g_scoobyWallAnimStep, g_scoobyWallAnimStep);
+			}
+		} else if (g_scoobyGameState.wallStates[savedCell][dir] == 3) {
+			// Dynamic wall in saved cell, opening animation
+			if (dir == 3 || dir == 1) {
+				g_scoobySoundPlaying = 3;
+			} else {
+				g_scoobySoundPlaying = 7;
+			}
+
+			drawMovingWalls(savedCell, dir, 5 - g_scoobyWallAnimStep, 4 - g_scoobyWallAnimStep);
+		}
+	}
+
+	// Determine combined sound on first step
+	if (g_scoobyWallAnimStep == 1) {
+		if (g_scoobyWallsToClose == 0 && g_scoobyWallsToOpen > 1) {
+			g_scoobySoundPlaying = 9;
+		} else if (g_scoobyWallsToClose > 1 || (g_scoobyWallsToClose == 1 && g_scoobyWallsToOpen >= 1)) {
+			g_scoobySoundPlaying = 10;
+		}
+
+		playWallSound();
+	}
+}
+
+void BoltEngine::decideDirection() {
+	int16 cell = g_scoobyGameState.scoobyCell;
+	int16 targetDir = g_scoobyGameState.transitionTarget;
+
+	int16 primaryPos, secondaryPos, primaryCenter, secondaryCenter;
+	int16 wallDir;
+
+	switch (targetDir) {
+	case 2: // right
+	case 6: // left
+		primaryPos = g_scoobyGameState.scoobyX;
+		secondaryPos = g_scoobyGameState.scoobyY;
+		primaryCenter = g_scoobyLevelStartXY[cell].x;
+		secondaryCenter = g_scoobyLevelStartXY[cell].y;
+		wallDir = (targetDir == 6) ? 3 : 1;
+
+		break;
+	case 0: // up
+	case 4: // down
+		primaryPos = g_scoobyGameState.scoobyY;
+		secondaryPos = g_scoobyGameState.scoobyX;
+		primaryCenter = g_scoobyLevelStartXY[cell].y;
+		secondaryCenter = g_scoobyLevelStartXY[cell].x;
+		wallDir = (targetDir == 0) ? 0 : 2;
+
+		break;
+	default:
+		return;
+	}
+
+	if (secondaryPos == secondaryCenter) {
+		// Aligned on secondary axis, can move in target direction
+		g_scoobyMoveRequested = 1;
+
+		if (primaryPos == primaryCenter) {
+			// At cell center, check wall
+			if (g_scoobyGameState.wallStates[cell][wallDir] == 2 ||
+				g_scoobyGameState.wallStates[cell][wallDir] == 3) {
+				// Open passage or dynamic wall, stop
+				g_scoobyGameState.targetVelocityX = 0;
+				g_scoobyGameState.targetVelocityY = 0;
+				return;
+			}
+		}
+
+		// Wall closed, set velocity in target direction
+		switch (targetDir) {
+		case 0: // up
+			g_scoobyGameState.targetVelocityX = 0;
+			g_scoobyGameState.targetVelocityY = -3;
+			return;
+		case 2: // right
+			g_scoobyGameState.targetVelocityX = 3;
+			g_scoobyGameState.targetVelocityY = 0;
+			return;
+		case 4: // down
+			g_scoobyGameState.targetVelocityX = 0;
+			g_scoobyGameState.targetVelocityY = 3;
+			return;
+		case 6: // left
+			g_scoobyGameState.targetVelocityX = -3;
+			g_scoobyGameState.targetVelocityY = 0;
+			return;
+		default:
+			return;
+		}
+	} else {
+		// Not aligned on secondary axis, slide toward center
+		g_scoobyMoveRequested = 1;
+
+		if (g_scoobyGameState.wallStates[cell][wallDir] == 2) {
+			g_scoobyGameState.targetVelocityX = 0;
+			g_scoobyGameState.targetVelocityY = 0;
+			return;
+		}
+
+		switch (targetDir) {
+		case 2: // right, slide vertically
+		case 6: // left
+			g_scoobyGameState.targetVelocityX = 0;
+			if (g_scoobyLevelStartXY[cell].y < g_scoobyGameState.scoobyY) {
+				g_scoobyGameState.transitionTarget = 0;
+				g_scoobyGameState.targetVelocityY = -3;
+			} else {
+				g_scoobyGameState.transitionTarget = 4;
+				g_scoobyGameState.targetVelocityY = 3;
+			}
+
+			return;
+		case 0: // up, slide horizontally
+		case 4: // down
+			g_scoobyGameState.targetVelocityY = 0;
+			if (g_scoobyLevelStartXY[cell].x < g_scoobyGameState.scoobyX) {
+				g_scoobyGameState.transitionTarget = 6;
+				g_scoobyGameState.targetVelocityX = -3;
+			} else {
+				g_scoobyGameState.transitionTarget = 2;
+				g_scoobyGameState.targetVelocityX = 3;
+			}
+
+			return;
+		default:
+			return;
+		}
+	}
+}
+
+void BoltEngine::updateScoobySound() {
+	switch (g_scoobySoundMode) {
+	case 0:
+		if (g_scoobySoundPlaying == 0) {
+			_xp->stopSound();
+		}
+
+		break;
+	case 1:
+		playSoundMapScooby(0x24);
+		break;
+	case 2:
+		playSoundMapScooby(0x25);
+		break;
+	default:
+		break;
+	}
+}
+
+void BoltEngine::setScoobySound(int16 mode) {
+	if (mode != g_scoobySoundMode) {
+		g_scoobySoundMode = mode;
+		updateScoobySound();
+	}
+}
+
+void BoltEngine::updateScoobyLocation() {
+	Common::Point loc;
+	getSSpriteLoc(&g_scoobySprite, &loc);
+	g_scoobyGameState.scoobyX = loc.x;
+	g_scoobyGameState.scoobyY = loc.y;
+
+	// Find which cell Scooby is in
+	g_scoobyGameState.scoobyCell = -1;
+	for (int16 i = 0; i < 25; i++) {
+		if (g_scoobyCellBounds[i].left > g_scoobyGameState.scoobyX ||
+			g_scoobyCellBounds[i].right < g_scoobyGameState.scoobyX ||
+			g_scoobyCellBounds[i].top > g_scoobyGameState.scoobyY ||
+			g_scoobyCellBounds[i].bottom < g_scoobyGameState.scoobyY) {
+			continue;
+		}
+
+		// Found cell
+		g_scoobyGameState.scoobyCell = i;
+
+		int16 col = i % 5;
+		g_scoobyGameState.leftNeighbor = (col == 0) ? -1 : i - 1;
+		g_scoobyGameState.rightNeighbor = (col == 4) ? -1 : i + 1;
+		g_scoobyGameState.upNeighbor = (i < 5) ? -1 : i - 5;
+		g_scoobyGameState.downNeighbor = (i > 19) ? -1 : i + 5;
+		break;
+	}
+
+	if (g_scoobyGameState.scoobyCell != -1)
+		return;
+
+	// Scooby is outside all cells, force movement back toward grid
+	int16 gridStartX = READ_UINT16(g_scoobyLevelData + 0x12);
+	int16 cellW = READ_UINT16(g_scoobyLevelData + 0x16);
+	int16 gridStartY = READ_UINT16(g_scoobyLevelData + 0x14);
+	int16 cellH = READ_UINT16(g_scoobyLevelData + 0x18);
+
+	if (gridStartX > g_scoobyGameState.scoobyX) {
+		g_scoobyGameState.velocityX = -3;
+		g_scoobyGameState.velocityY = 0;
+		setScoobySpriteDirection(6);
+	} else if (gridStartX + cellW * 5 - 1 < g_scoobyGameState.scoobyX) {
+		g_scoobyGameState.velocityX = 3;
+		g_scoobyGameState.velocityY = 0;
+		setScoobySpriteDirection(0);
+	} else if (gridStartY > g_scoobyGameState.scoobyY) {
+		g_scoobyGameState.velocityX = 0;
+		g_scoobyGameState.velocityY = -3;
+		setScoobySpriteDirection(0x0C);
+	} else if (gridStartY + (cellH - 1) * 5 - 1 < g_scoobyGameState.scoobyY) {
+		g_scoobyGameState.velocityX = 0;
+		g_scoobyGameState.velocityY = 3;
+		setScoobySpriteDirection(0x12);
+	}
+
+	// Scroll Scooby off-screen
+	int16 halfW = READ_UINT16(g_scoobyBaseData + 0x0A) / 2;
+
+	while (true) {
+		if (g_displayX - halfW >= g_scoobyGameState.scoobyX)
+			break;
+
+		if (g_displayX + g_displayWidth + halfW <= g_scoobyGameState.scoobyX)
+			break;
+
+		int16 halfH = READ_UINT16(g_scoobyBaseData + 0x0C) / 2;
+		if (g_displayY - halfH >= g_scoobyGameState.scoobyY)
+			break;
+
+		if (g_displayY + g_displayHeight + halfH <= g_scoobyGameState.scoobyY)
+			break;
+
+		// Still visible, animate one frame
+		setSSpriteVelocity(&g_scoobySprite, g_scoobyGameState.velocityX, g_scoobyGameState.velocityY);
+		animateSSprite(&g_scoobySprite, 0);
+		_xp->updateDisplay();
+
+		uint32 dummy;
+		if (_xp->getEvent(etSound, &dummy) == etSound)
+			updateScoobySound();
+
+		getSSpriteLoc(&g_scoobySprite, &loc);
+		g_scoobyGameState.scoobyX = loc.x;
+		g_scoobyGameState.scoobyY = loc.y;
+	}
+
+	// Scooby left the screen, level complete
+	_xp->stopSound();
+	g_scoobyGameState.levelComplete = 1;
+
+	g_scoobyGameState.levelNumber++;
+	if (g_scoobyGameState.levelNumber > 12)
+		g_scoobyGameState.levelNumber = 10;
+
+	// Cycle slot for current level
+	int16 slotIdx = g_scoobyLevelCount - 1;
+	g_scoobyGameState.slotIndex[slotIdx]++;
+	if (g_scoobyGameState.slotIndex[slotIdx] == 3)
+		g_scoobyGameState.slotIndex[slotIdx] = 0;
+}
+
+void BoltEngine::updateScoobyWalls() {
+	if (g_scoobyWallAnimating != 0) {
+		g_scoobyWallAnimStep++;
+		if (g_scoobyWallAnimStep > 5) {
+			g_scoobyWallAnimating = 0;
+			g_scoobyWallAnimStep = 5;
+		}
+
+		animateWalls();
+
+		if (g_scoobyWallAnimating == 0) {
+			g_scoobyGameState.scoobySavedCell = g_scoobyGameState.scoobyCell;
+		}
+
+		return;
+	}
+
+	// Not animating, check if Scooby is at cell center
+	int16 curCell = g_scoobyGameState.scoobyCell;
+	if (g_scoobyLevelStartXY[curCell].x != g_scoobyGameState.scoobyX)
+		return;
+
+	if (g_scoobyLevelStartXY[curCell].y != g_scoobyGameState.scoobyY)
+		return;
+
+	// At cell center, update active level
+	g_scoobyGameState.activeLevel = g_scoobyGameState.scoobyCell;
+
+	// If same cell as saved, no wall changes needed
+	if (g_scoobyGameState.scoobyCell == g_scoobyGameState.scoobySavedCell)
+		return;
+
+	// Detect wall changes between current and saved cells
+	g_scoobyWallsToOpen = 0;
+	g_scoobyWallsToClose = 0;
+
+	int16 savedCell = g_scoobyGameState.scoobySavedCell;
+
+	int16 curUp = curCell - 5;
+	int16 curLeft = curCell - 1;
+	int16 curRight = curCell + 1;
+	int16 curDown = curCell + 5;
+
+	int16 savedUp = savedCell - 5;
+	int16 savedLeft = savedCell - 1;
+	int16 savedRight = savedCell + 1;
+	int16 savedDown = savedCell + 5;
+
+	for (int16 dir = 0; dir <= 3; dir++) {
+		// Check current cell wall
+		if (g_scoobyGameState.wallStates[curCell][dir] == 3) {
+			g_scoobyWallAnimating = 1;
+			g_scoobyWallAnimStep = 1;
+			g_scoobyWallsToClose++;
+		} else if (g_scoobyGameState.wallStates[curCell][dir] == 1) {
+			g_scoobyWallAnimating = 1;
+			g_scoobyWallAnimStep = 1;
+			g_scoobyWallsToOpen++;
+
+			bool cancelOpen = false;
+			switch (dir) {
+			case 0:
+				if (curUp == savedCell &&
+					g_scoobyGameState.wallStates[savedCell][2] == 1)
+					cancelOpen = true;
+
+				break;
+			case 1:
+				if (curRight == savedCell &&
+					g_scoobyGameState.wallStates[savedCell][3] == 1)
+					cancelOpen = true;
+
+				break;
+			case 2:
+				if (curDown == savedCell &&
+					g_scoobyGameState.wallStates[savedCell][0] == 1)
+					cancelOpen = true;
+
+				break;
+			case 3:
+				if (curLeft == savedCell &&
+					g_scoobyGameState.wallStates[savedCell][1] == 1)
+					cancelOpen = true;
+
+				break;
+			default:
+				break;
+			}
+
+			if (cancelOpen)
+				g_scoobyWallsToOpen--;
+		}
+
+		// Check saved cell wall
+		if (g_scoobyGameState.wallStates[savedCell][dir] == 3) {
+			g_scoobyWallAnimating = 1;
+			g_scoobyWallAnimStep = 1;
+			g_scoobyWallsToOpen++;
+		} else if (g_scoobyGameState.wallStates[savedCell][dir] == 1) {
+			g_scoobyWallAnimating = 1;
+			g_scoobyWallAnimStep = 1;
+			g_scoobyWallsToClose++;
+
+			bool cancelClose = false;
+			switch (dir) {
+			case 0:
+				if (savedUp == curCell &&
+					g_scoobyGameState.wallStates[curCell][2] == 1)
+					cancelClose = true;
+
+				break;
+			case 1:
+				if (savedRight == curCell &&
+					g_scoobyGameState.wallStates[curCell][3] == 1)
+					cancelClose = true;
+
+				break;
+			case 2:
+				if (savedDown == curCell &&
+					g_scoobyGameState.wallStates[curCell][0] == 1)
+					cancelClose = true;
+
+				break;
+			case 3:
+				if (savedLeft == curCell &&
+					g_scoobyGameState.wallStates[curCell][1] == 1)
+					cancelClose = true;
+
+				break;
+			default:
+				break;
+			}
+
+			if (cancelClose)
+				g_scoobyWallsToClose--;
+		}
+	}
+
+	if (g_scoobyWallAnimating != 0) {
+		animateWalls();
+	}
+}
+
+void BoltEngine::updateScoobyDirection(int16 inputDir) {
+	int16 cell = g_scoobyGameState.scoobyCell;
+
+	// If at cell center, wall passable, and no transition active then accept immediately
+	if (g_scoobyLevelStartXY[cell].x == g_scoobyGameState.scoobyX &&
+		g_scoobyLevelStartXY[cell].y == g_scoobyGameState.scoobyY &&
+		g_scoobyGameState.wallStates[cell][inputDir] != 2 &&
+		g_scoobyTransitioning == 0) {
+		g_scoobyDesiredDir = inputDir;
+		g_scoobyInputHoldCount = 3;
+		g_scoobyLastInputDir = inputDir; 
+	} else {
+		if (g_scoobyLastInputDir == inputDir) {
+			g_scoobyInputHoldCount++;
+			if (g_scoobyInputHoldCount >= 3 && g_scoobyTransitioning == 0) {
+				g_scoobyDesiredDir = g_scoobyLastInputDir;
+			}
+		} else {
+			g_scoobyInputHoldCount = 0;
+			g_scoobyLastInputDir = inputDir;
+		}
+	}
+
+	// Resolve diagonal inputs into cardinal directions
+	int16 resolvedDir = 0;
+
+#define WALL_OPEN(c, d) (g_scoobyGameState.wallStates[c][d] == 2 || g_scoobyGameState.wallStates[c][d] == 3)
+
+	switch (g_scoobyDesiredDir) {
+	case 0:
+	case 2:
+	case 4:
+	case 6:
+		resolvedDir = g_scoobyDesiredDir;
+		break;
+	case 1: // up-right
+		if (WALL_OPEN(cell, 0)) {
+			resolvedDir = WALL_OPEN(cell, 1) ? (g_scoobyDesiredDir & 0xFE) : 2;
+		} else {
+			resolvedDir = WALL_OPEN(cell, 1) ? 0 : (g_scoobyDesiredDir & 0xFE);
+		}
+
+		break;
+	case 3: // down-right
+		if (WALL_OPEN(cell, 2)) {
+			resolvedDir = WALL_OPEN(cell, 1) ? (g_scoobyDesiredDir & 0xFE) : 2;
+		} else {
+			resolvedDir = WALL_OPEN(cell, 1) ? 4 : (g_scoobyDesiredDir & 0xFE);
+		}
+
+		break;
+	case 5: // down-left
+		if (WALL_OPEN(cell, 2)) {
+			resolvedDir = WALL_OPEN(cell, 3) ? (g_scoobyDesiredDir & 0xFE) : 6;
+		} else {
+			resolvedDir = WALL_OPEN(cell, 3) ? 4 : (g_scoobyDesiredDir & 0xFE);
+		}
+
+		break;
+	case 7: // up-left
+		if (WALL_OPEN(cell, 0)) {
+			resolvedDir = WALL_OPEN(cell, 3) ? (g_scoobyDesiredDir & 0xFE) : 6;
+		} else {
+			resolvedDir = WALL_OPEN(cell, 3) ? 0 : (g_scoobyDesiredDir & 0xFE);
+		}
+
+		break;
+	default:
+		break;
+	}
+
+#undef WALL_OPEN
+
+	g_scoobyMoveRequested = 0;
+
+	if (g_scoobyDesiredDir == -1) {
+		g_scoobyMoveRequested = 1;
+
+		if (g_scoobyGameState.direction == 6) {
+			g_scoobyGameState.transitionTarget = -3;
+		} else {
+			g_scoobyGameState.transitionTarget = -2;
+		}
+
+		g_scoobyGameState.targetVelocityX = 0;
+		g_scoobyGameState.targetVelocityY = 0;
+	} else {
+		g_scoobyGameState.transitionTarget = resolvedDir;
+		decideDirection();
+	}
+}
+
+void BoltEngine::updateScoobyTransition() {
+	if (g_scoobyTransitioning != 0) {
+		// Active transition in progress
+		switch (g_scoobyTransitionTarget) {
+		case -3:
+			animateTransition(0x24);
+			g_scoobyTransitioning = 0;
+			break;
+		case -2:
+			animateTransition(0x23);
+			g_scoobyTransitioning = 0;
+			break;
+		case 0:
+			setScoobySpriteDirection(0x0C);
+			g_scoobyTransitioning = 0;
+			break;
+		case 2:
+			if (g_scoobyTransitionFrom == 4) {
+				animateTransition(0x1C);
+				g_scoobyTransitionFrom = 0;
+			} else {
+				setScoobySpriteDirection(0x0);
+				g_scoobyTransitioning = 0;
+			}
+			break;
+		case 4:
+			if (g_scoobyTransitionFrom == 0) {
+				animateTransition(0x19);
+				g_scoobyTransitionFrom = 4;
+			} else {
+				setScoobySpriteDirection(0x12);
+				g_scoobyTransitioning = 0;
+			}
+			break;
+		case 6:
+			setScoobySpriteDirection(6);
+			g_scoobyTransitioning = 0;
+			break;
+		default:
+			break;
+		}
+
+		if (g_scoobyTransitioning == 0) {
+			// Transition complete, commit state
+			switch (g_scoobyTransitionTarget) {
+			case 0:
+			case 2:
+			case 4:
+			case 6:
+				g_scoobyGameState.velocityX = g_scoobyTransitionVelX;
+				g_scoobyGameState.velocityY = g_scoobyTransitionVelY;
+				break;
+			default:
+				break;
+			}
+
+			g_scoobyGameState.currentAnim = g_scoobyTransitionTarget;
+
+			if (g_scoobyTransitionTarget == 6) {
+				g_scoobyGameState.direction = 6;
+			} else if (g_scoobyGameState.currentAnim == 2) {
+				g_scoobyGameState.direction = 2;
+			}
+		}
+	} else {
+		// No transition active, check if one should start
+		if (g_scoobyMoveRequested == 0)
+			goto epilogue;
+
+		g_scoobyMoveRequested = 0;
+
+		if (g_scoobyGameState.transitionTarget == g_scoobyGameState.currentAnim) {
+			// Same direction, just update velocity
+			g_scoobyGameState.velocityX = g_scoobyGameState.targetVelocityX;
+			g_scoobyGameState.velocityY = g_scoobyGameState.targetVelocityY;
+			goto epilogue;
+		}
+
+		// Start new transition
+		g_scoobyTransitioning = 1;
+		g_scoobyTransitionTarget = g_scoobyGameState.transitionTarget;
+		g_scoobyTransitionFrom = g_scoobyGameState.currentAnim;
+		g_scoobyTransitionVelX = g_scoobyGameState.targetVelocityX;
+		g_scoobyTransitionVelY = g_scoobyGameState.targetVelocityY;
+
+		// Pick transition animation based on current -> target direction.
+		switch (g_scoobyGameState.currentAnim) {
+		case -3:
+		case -2:
+			switch (g_scoobyGameState.transitionTarget) {
+			case 0:
+				if (g_scoobyGameState.currentAnim == -2)
+					animateTransition(0x1B);
+				else
+					animateTransition(0x1C);
+				break;
+			case 2:
+				animateTransition(0x1D);
+				break;
+			case 4:
+				if (g_scoobyGameState.currentAnim == -2)
+					animateTransition(0x19);
+				else
+					animateTransition(0x1A);
+				break;
+			case 6:
+				animateTransition(0x1E);
+				break;
+
+			default:
+				break;
+			}
+			break;
+		case 0:
+			switch (g_scoobyGameState.transitionTarget) {
+			case -3:
+				animateTransition(0x1C);
+				break;
+			case -2:
+				animateTransition(0x1B);
+				break;
+			case 2:
+				animateTransition(0x1F);
+				break;
+			case 4:
+				animateTransition(0x1B);
+				break;
+			case 6:
+				animateTransition(0x20);
+				break;
+			default:
+				break;
+			}
+			break;
+
+		case 2:
+			switch (g_scoobyGameState.transitionTarget) {
+			case -3:
+				animateTransition(0x1D);
+				break;
+			case -2:
+				animateTransition(0x1D);
+				break;
+			case 0:
+				animateTransition(0x1F);
+				break;
+			case 4:
+				animateTransition(0x21);
+				break;
+			case 6:
+				animateTransition(0x19);
+				break;
+			default:
+				break;
+			}
+			break;
+
+		case 4:
+			switch (g_scoobyGameState.transitionTarget) {
+			case -3:
+				animateTransition(0x1A);
+				break;
+			case -2:
+				animateTransition(0x19);
+				break;
+			case 0:
+				animateTransition(0x1A);
+				break;
+			case 2:
+				animateTransition(0x21);
+				break;
+			case 6:
+				animateTransition(0x22);
+				break;
+			default:
+				break;
+			}
+			break;
+
+		case 6:
+			switch (g_scoobyGameState.transitionTarget) {
+			case -3:
+				animateTransition(0x1E);
+				break;
+			case -2:
+				animateTransition(0x1E);
+				break;
+			case 0:
+				animateTransition(0x20);
+				break;
+			case 2:
+
+				animateTransition(0x1A);
+				break;
+			case 4:
+				animateTransition(0x22);
+				break;
+
+			default:
+				break;
+			}
+			break;
+
+		default:
+			break;
+		}
+	}
+
+epilogue:
+	setSSpriteVelocity(&g_scoobySprite, g_scoobyGameState.velocityX, g_scoobyGameState.velocityY);
+	animateSSprite(&g_scoobySprite, 0);
+
+	int16 soundMode;
+	if (g_scoobyGameState.currentAnim == -2 || g_scoobyGameState.currentAnim == -3) {
+		soundMode = 0;
+	} else if (g_scoobyGameState.velocityX == 0 && g_scoobyGameState.velocityY == 0 && g_scoobyTransitioning == 0) {
+		soundMode = 2;
+	} else {
+		soundMode = 1;
+	}
+
+	setScoobySound(soundMode);
+}
+
+bool BoltEngine::initScoobyLevel() {
+	if (!initScoobyLevelAssets())
+		return false;
+
+	// Set current level based on difficulty
+	int16 level;
+	switch (g_scoobyDifficulty) {
+	case 0:
+		level = READ_UINT16(g_scoobyLevelData + 0x1A);
+		break;
+	case 1:
+		level = READ_UINT16(g_scoobyLevelData + 0xE4);
+		break;
+	case 2:
+		level = READ_UINT16(g_scoobyLevelData + 0x1AE);
+		break;
+	default:
+		goto skipLevelSet;
+	}
+
+	g_scoobyGameState.scoobyCell = level;
+	g_scoobyGameState.scoobySavedCell = level;
+
+skipLevelSet:
+	g_scoobyGameState.activeLevel = g_scoobyGameState.scoobyCell;
+
+	// Compute neighbor levels in a 5-wide grid
+	int16 col = g_scoobyGameState.scoobyCell % 5;
+
+	g_scoobyGameState.leftNeighbor = (col == 0) ? -1 : g_scoobyGameState.scoobyCell - 1;
+	g_scoobyGameState.rightNeighbor = (col == 4) ? -1 : g_scoobyGameState.scoobyCell + 1;
+	g_scoobyGameState.upNeighbor = (g_scoobyGameState.scoobyCell < 5) ? -1 : g_scoobyGameState.scoobyCell - 5;
+	g_scoobyGameState.downNeighbor = (g_scoobyGameState.scoobyCell > 19) ? -1 : g_scoobyGameState.scoobyCell + 5;
+
+	g_scoobyGameState.levelComplete = 0;
+
+	// Starting position from level table
+	g_scoobyGameState.scoobyX = g_scoobyLevelStartXY[g_scoobyGameState.activeLevel].x;
+	g_scoobyGameState.scoobyY = g_scoobyLevelStartXY[g_scoobyGameState.activeLevel].y;
+
+	g_scoobyGameState.velocityX = 0;
+	g_scoobyGameState.velocityY = 0;
+	g_scoobyGameState.targetVelocityX = 0;
+	g_scoobyGameState.targetVelocityY = 0;
+	g_scoobyGameState.currentAnim = -2;
+	g_scoobyGameState.transitionTarget = -2;
+	g_scoobyGameState.direction = 2;
+
+	initScoobyLevelGraphics();
+	return true;
+}
+
+bool BoltEngine::resumeScoobyLevel() {
+	if (!initScoobyLevelAssets())
+		return false;
+
+	// Restore level from saved level
+	g_scoobyGameState.scoobyCell = g_scoobyGameState.scoobySavedCell;
+
+	// Compute neighbor levels
+	int16 col = g_scoobyGameState.scoobyCell % 5;
+
+	g_scoobyGameState.leftNeighbor = (col == 0) ? -1 : g_scoobyGameState.scoobyCell - 1;
+	g_scoobyGameState.rightNeighbor = (col == 4) ? -1 : g_scoobyGameState.scoobyCell + 1;
+	g_scoobyGameState.upNeighbor = (g_scoobyGameState.scoobyCell < 5) ? -1 : g_scoobyGameState.scoobyCell - 5;
+	g_scoobyGameState.downNeighbor = (g_scoobyGameState.scoobyCell > 19) ? -1 : g_scoobyGameState.scoobyCell + 5;
+
+	// Starting position from level table
+	g_scoobyGameState.scoobyX = g_scoobyLevelStartXY[g_scoobyGameState.activeLevel].x;
+	g_scoobyGameState.scoobyY = g_scoobyLevelStartXY[g_scoobyGameState.activeLevel].y;
+
+	initScoobyLevelGraphics();
+	return true;
+}
+
+bool BoltEngine::initScooby() {
+	_xp->randomize();
+
+	g_scoobyMoveRequested = 0;
+	g_scoobyTransitioning = 0;
+	g_scoobyWallAnimating = 0;
+	g_scoobyDesiredDir = -1;
+	g_scoobyInputHoldCount = 0;
+
+	if (!loadScoobyBaseAssets())
+		return false;
+
+	if (!vLoad(&g_scoobyGlobalSaveData, "ScoobyGlobal")) {
+		// Initialize global save data: 10 levels, 3 slots each
+		for (int16 level = 0; level < 10; level++) {
+			g_scoobyGameState.slotIndex[level] = 0;
+
+			int16 baseOff = level * 6;
+			for (int16 j = 0; j < 3; j++) {
+				g_scoobyGlobalSaveData[baseOff + j * 2] = 3;
+			}
+
+			// Randomly assign difficulty values 0, 1, 2 to the 3 slots
+			for (int16 j = 0; j < 3; j++) {
+				int16 slot = _xp->getRandom(3);
+
+				while (g_scoobyGlobalSaveData[baseOff + slot * 2] != 3) {
+					slot++;
+					if (slot >= 3)
+						slot = 0;
+				}
+
+				g_scoobyGlobalSaveData[baseOff + slot * 2] = j;
+			}
+		}
+	}
+
+	byte scoobyStateBuf[0x11A];
+	if (!vLoad(scoobyStateBuf, "Scooby")) {
+		g_scoobyGameState.levelNumber = 1;
+
+		if (!initScoobyLevel())
+			return false;
+	} else {
+		Common::SeekableReadStream *scoobyStateReadStream = new Common::MemoryReadStream(scoobyStateBuf, sizeof(scoobyStateBuf), DisposeAfterUse::NO);
+
+		g_scoobyGameState.levelNumber = scoobyStateReadStream->readSint16BE(); // +0x00
+
+		for (int i = 0; i < 10; i++) // +0x02
+			g_scoobyGameState.slotIndex[i] = scoobyStateReadStream->readSint16BE();
+
+		g_scoobyGameState.levelComplete = scoobyStateReadStream->readSint16BE(); // +0x16
+
+		for (int i = 0; i < 25; i++) // +0x18
+			for (int j = 0; j < 4; j++)
+				g_scoobyGameState.wallStates[i][j] = scoobyStateReadStream->readSint16BE();
+
+		g_scoobyGameState.scoobyCell = scoobyStateReadStream->readSint16BE();       // +0xE0
+		g_scoobyGameState.scoobySavedCell = scoobyStateReadStream->readSint16BE();  // +0xE2
+		g_scoobyGameState.leftNeighbor = scoobyStateReadStream->readSint16BE();     // +0xE4
+		g_scoobyGameState.rightNeighbor = scoobyStateReadStream->readSint16BE();    // +0xE6
+		g_scoobyGameState.upNeighbor = scoobyStateReadStream->readSint16BE();       // +0xE8
+		g_scoobyGameState.downNeighbor = scoobyStateReadStream->readSint16BE();     // +0xEA
+		g_scoobyGameState.activeLevel = scoobyStateReadStream->readSint16BE();      // +0xEC
+		g_scoobyGameState.scoobyX = scoobyStateReadStream->readSint16BE();          // +0xEE
+		g_scoobyGameState.scoobyY = scoobyStateReadStream->readSint16BE();          // +0xF0
+		g_scoobyGameState.velocityX = scoobyStateReadStream->readSint16BE();        // +0xF2
+		g_scoobyGameState.velocityY = scoobyStateReadStream->readSint16BE();        // +0xF4
+		g_scoobyGameState.targetVelocityX = scoobyStateReadStream->readSint16BE();  // +0xF6
+		g_scoobyGameState.targetVelocityY = scoobyStateReadStream->readSint16BE();  // +0xF8
+		g_scoobyGameState.transitionTarget = scoobyStateReadStream->readSint16BE(); // +0xFA
+		g_scoobyGameState.currentAnim = scoobyStateReadStream->readSint16BE();      // +0xFC
+		g_scoobyGameState.direction = scoobyStateReadStream->readSint16BE();        // +0xFE
+		g_scoobyGameState.spriteFrameCount = scoobyStateReadStream->readSint16BE(); // +0x100
+
+		for (int i = 0; i < 6; i++) { // +0x102
+			scoobyStateReadStream->readUint32BE(); // dummy values
+			g_scoobyGameState.frameData[i] = nullptr;
+		}
+
+		assert(scoobyStateReadStream->pos() == 0x11A);
+		delete scoobyStateReadStream;
+
+		if (g_scoobyGameState.levelComplete != 0) {
+			if (!initScoobyLevel())
+				return false;
+		} else {
+			if (!resumeScoobyLevel())
+				return false;
+		}
+	}
+
+	_xp->setFrameRate(12);
+	return true;
+}
+
+void BoltEngine::cleanUpScooby() {
+	vSave(&g_scoobyGlobalSaveData, 0x3C, "ScoobyGlobal");
+
+	// Serialize the game state into a flat 0x11A-byte BE buffer
+	byte scoobyStateBuf[0x11A] = {0};
+	Common::MemoryWriteStream *scoobyStateWriteStream = new Common::MemoryWriteStream(scoobyStateBuf, sizeof(scoobyStateBuf));
+
+	scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.levelNumber); // +0x00
+
+	for (int i = 0; i < 10; i++) // +0x02
+		scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.slotIndex[i]);
+
+	scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.levelComplete); // +0x16
+
+	for (int i = 0; i < 25; i++) // +0x18
+		for (int j = 0; j < 4; j++)
+			scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.wallStates[i][j]);
+
+	scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.scoobyCell);       // +0xE0
+	scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.scoobySavedCell);  // +0xE2
+	scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.leftNeighbor);     // +0xE4
+	scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.rightNeighbor);    // +0xE6
+	scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.upNeighbor);       // +0xE8
+	scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.downNeighbor);     // +0xEA
+	scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.activeLevel);      // +0xEC
+	scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.scoobyX);          // +0xEE
+	scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.scoobyY);          // +0xF0
+	scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.velocityX);        // +0xF2
+	scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.velocityY);        // +0xF4
+	scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.targetVelocityX);  // +0xF6
+	scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.targetVelocityY);  // +0xF8
+	scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.transitionTarget); // +0xFA
+	scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.currentAnim);      // +0xFC
+	scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.direction);        // +0xFE
+	scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.spriteFrameCount); // +0x100
+
+	for (int i = 0; i < 6; i++) // +0x102
+		scoobyStateWriteStream->writeUint32BE(0); // pointers saved as zero
+
+	// Sanity check: should be exactly 0x11A
+	assert(scoobyStateWriteStream->pos() == 0x11A);
+
+	vSave(scoobyStateBuf, 0x11A, "Scooby");
+
+	cleanUpScoobyLevelGraphics();
+	cleanUpScoobyBaseAssets();
+
+	if (g_scoobyTempPic.pixelData) {
+		_xp->freeMem(g_scoobyTempPic.pixelData);
+		g_scoobyTempPic.pixelData = nullptr;
+		g_scoobyTempPic.width = 0;
+		g_scoobyTempPic.height = 0;
+	}
+
+	_xp->setFrameRate(0);
+	_xp->fillDisplay(0, 0);
+	_xp->updateDisplay();
+}
+
+int16 BoltEngine::helpScooby() {
+	int16 selection = 2;
+	int16 isPlaying = 0;
+	int16 soundParam = 0;
+	byte *blinkEntry = nullptr;
+	uint32 blinkTimer = 0;
+	byte *hoveredEntry = nullptr;
+	int16 animFrameIdx = -1;
+	byte *backButton = nullptr;
+	byte *helpButton = nullptr;
+
+	byte *helpEntries[3];
+	byte *helpPics[3];
+
+	// If Scooby is mid-movement, stop and face idle direction
+	if (g_scoobyGameState.currentAnim != -2 && g_scoobyGameState.currentAnim != -3) {
+		g_scoobyGameState.velocityX = 0;
+		g_scoobyGameState.velocityY = 0;
+
+		if (g_scoobyGameState.direction == 2)
+			g_scoobyGameState.transitionTarget = -2;
+		else
+			g_scoobyGameState.transitionTarget = -3;
+
+		g_scoobyDesiredDir = -1;
+		g_scoobyInputHoldCount = 0;
+		g_scoobyMoveRequested = 1;
+
+		do {
+			updateScoobyTransition();
+			updateScoobyWalls();
+			_xp->updateDisplay();
+		} while (g_scoobyTransitioning != 0);
+
+		updateScoobyTransition();
+		updateScoobyWalls();
+		_xp->updateDisplay();
+
+		updateScoobyTransition();
+		updateScoobyWalls();
+		_xp->updateDisplay();
+	}
+
+	_xp->setFrameRate(0);
+	_xp->setInactivityTimer(0);
+	animateSSprite(&g_scoobySprite, 0);
+
+	// Load 3 help screen entries
+	for (int16 i = 0; i < 3; i++) {
+		int16 memberIdx = READ_UINT16(g_scoobyLevelData + 0x0C) + i;
+		helpEntries[i] = memberAddr(g_scoobyBoltLib, memberIdx);
+
+		byte *entry = helpEntries[i];
+		if (READ_UINT32(entry) == 2)
+			backButton = entry;
+		if (READ_UINT32(entry) == 1)
+			helpButton = entry;
+
+		// Clear alive flag
+		WRITE_UINT32(entry + 6, READ_UINT32(entry + 6) & ~1);
+
+		// Load and display pic
+		int16 picMember = READ_UINT16(entry + 4);
+		helpPics[i] = memberAddr(g_scoobyBoltLib, picMember);
+		displayPic(helpPics[i], 0, 0, 0);
+	}
+
+	_xp->updateDisplay();
+
+	// Save normal palettes from each help entry
+	for (int16 i = 0; i < 3; i++) {
+		byte *entry = helpEntries[i];
+		_xp->getPalette(READ_UINT16(entry + 0x0A), READ_UINT16(entry + 0x0C), entry + 0x42);
+	}
+
+	// Position cursor at bottom-right of first help pic
+	int16 cursorX = READ_UINT16(helpPics[0] + 6) + READ_UINT16(helpPics[0] + 0x0A) - 10;
+	int16 cursorY = READ_UINT16(helpPics[0] + 8) + READ_UINT16(helpPics[0] + 0x0C) - 10;
+	_xp->setCursorPos(cursorX, cursorY);
+	_xp->setCursorColor(0, 127, 127);
+	_xp->disableController();
+	_xp->showCursor();
+
+	hoveredEntry = helpButton;
+	hiliteScoobyHelpObject(helpButton, 1);
+
+	// Event loop
+	while (true) {
+		// Handle ongoing audio playback
+		if (isPlaying != 0) {
+			if (!maintainAudioPlay(soundParam)) {
+				int16 hl = (backButton == hoveredEntry) ? 1 : 0;
+				hiliteScoobyHelpObject(backButton, hl);
+				isPlaying = 0;
+			}
+			soundParam = 0;
+		}
+
+		uint32 eventData;
+		int16 eventType = _xp->getEvent(0, &eventData);
+
+		switch (eventType) {
+		case etTimer: {
+			if (blinkEntry == nullptr)
+				break;
+			if (eventData != blinkTimer)
+				break;
+
+			blinkTimer = _xp->startTimer(500);
+
+			int16 hl = (READ_UINT32(blinkEntry + 6) & 1) ? 0 : 1;
+			hiliteScoobyHelpObject(blinkEntry, hl);
+			break;
+		}
+
+		case etMouseMove: {
+			int16 mouseX = (int32)eventData >> 16;
+			int16 mouseY = (int16)eventData;
+
+			byte *hitEntry = nullptr;
+			for (int16 i = 0; i < 3; i++) {
+				byte *pic = helpPics[i];
+				int16 picX = READ_UINT16(pic + 6);
+				int16 picY = READ_UINT16(pic + 8);
+				int16 picW = READ_UINT16(pic + 0x0A);
+				int16 picH = READ_UINT16(pic + 0x0C);
+
+				if (mouseX <= picX || mouseX >= picX + picW)
+					continue;
+				if (mouseY <= picY || mouseY >= picY + picH)
+					continue;
+
+				hitEntry = helpEntries[i];
+				break;
+			}
+
+			if (hoveredEntry != hitEntry) {
+				// Un-highlight old (unless null, blinking, or back button)
+				if (hoveredEntry != nullptr &&
+					hoveredEntry != blinkEntry &&
+					hoveredEntry != backButton) {
+					hiliteScoobyHelpObject(hoveredEntry, 0);
+				}
+
+				hoveredEntry = hitEntry;
+
+				// Highlight new (unless null or blinking)
+				if (hitEntry != nullptr && hitEntry != blinkEntry) {
+					hiliteScoobyHelpObject(hoveredEntry, 1);
+				}
+			}
+
+			// Un-highlight back button if not actively needed
+			if (backButton != nullptr) {
+				if (READ_UINT32(backButton + 6) & 1) {
+					if (hoveredEntry != backButton &&
+						blinkEntry != backButton &&
+						isPlaying == 0) {
+						hiliteScoobyHelpObject(backButton, 0);
+					}
+				}
+			}
+			break;
+		}
+
+		case etMouseDown: {
+			int16 wasPlaying = isPlaying;
+
+			if (isPlaying != 0) {
+				if (blinkTimer != 0) {
+					_xp->killTimer(blinkTimer);
+					blinkTimer = 0;
+				}
+
+				int16 hl = (backButton == hoveredEntry) ? 1 : 0;
+				hiliteScoobyHelpObject(backButton, hl);
+
+				hl = (blinkEntry == hoveredEntry) ? 1 : 0;
+				hiliteScoobyHelpObject(blinkEntry, hl);
+
+				blinkEntry = nullptr;
+				stopAnimation();
+				isPlaying = 0;
+			}
+
+			if (hoveredEntry == nullptr)
+				break;
+
+			selection = READ_UINT16(hoveredEntry);
+
+			if (READ_UINT32(hoveredEntry) != 2)
+				break;
+			if (wasPlaying != 0)
+				break;
+
+			if (startAnimation(g_rtfHandle, 0x1C)) {
+				animFrameIdx = 0;
+				isPlaying = 1;
+			}
+			break;
+		}
+
+		case etSound:
+			soundParam = 1;
+			break;
+
+		case etTrigger: {
+			if (isPlaying == 0)
+				break;
+			if (animFrameIdx < 0)
+				break;
+
+			int16 hl = (blinkEntry == hoveredEntry) ? 1 : 0;
+			hiliteScoobyHelpObject(blinkEntry, hl);
+
+			if (animFrameIdx == 0)
+				blinkEntry = helpEntries[1];
+			else if (animFrameIdx == 2)
+				blinkEntry = helpEntries[2];
+			else
+				blinkEntry = nullptr;
+
+			if (blinkEntry != nullptr) {
+				blinkTimer = _xp->startTimer(500);
+			} else {
+				if (blinkTimer != 0) {
+					_xp->killTimer(blinkTimer);
+					blinkTimer = 0;
+				}
+			}
+
+			hiliteScoobyHelpObject(blinkEntry, 1);
+			animFrameIdx++;
+			break;
+		}
+
+		default:
+			break;
+		}
+
+		// Exit conditions
+		if (selection == 0 || selection == 1)
+			break;
+	}
+
+	// Clean-up
+	_xp->hideCursor();
+	_xp->enableController();
+
+	hiliteScoobyHelpObject(helpEntries[0], 0);
+
+	for (int16 i = 0; i < 3; i++) {
+		hiliteScoobyHelpObject(helpEntries[i], 0);
+	}
+
+	animateSSprite(&g_scoobySprite, 0);
+	_xp->updateDisplay();
+	_xp->setFrameRate(12);
+
+	return selection;
+}
+
+void BoltEngine::hiliteScoobyHelpObject(byte *entry, int16 highlight) {
+	if (!entry)
+		return;
+
+	if (highlight != 0) {
+		// Set highlighted palette
+		_xp->setPalette(READ_UINT16(entry + 0x0C), READ_UINT16(entry + 0x0A), entry + 0x0E);
+		WRITE_UINT32(entry + 6, READ_UINT32(entry + 6) | 1);
+	} else {
+		// Set normal palette
+		_xp->setPalette(READ_UINT16(entry + 0x0C), READ_UINT16(entry + 0x0A), entry + 0x42);
+		WRITE_UINT32(entry + 6, READ_UINT32(entry + 6) & ~1);
+	}
+}
+
+int16 BoltEngine::xpDirToBOLTDir(uint32 xpDir) {
+	// xpDir packs (X << 16 | Y & 0xFFFF) where X,Y are -1/0/1
+	// Returns BOLT direction:
+	//   0=up,   1=up-right,  2=right, 3=down-right,
+	//   4=down, 5=down-left, 6=left,  7=up-left,    -1=none
+
+	static const uint32 dirTable[] = {
+		0xFFFF0000, // X=-1, Y= 0 -> 6 (left)
+		0xFFFF0001, // X=-1, Y= 1 -> 5 (down-left)
+		0xFFFFFFFF, // X=-1, Y=-1 -> 7 (up-left)
+		0x00000000, // X= 0, Y= 0 -> -1 (none)
+		0x00000001, // X= 0, Y= 1 -> 4 (down)
+		0x0000FFFF, // X= 0, Y=-1 -> 0 (up)
+		0x00010000, // X= 1, Y= 0 -> 2 (right)
+		0x00010001, // X= 1, Y= 1 -> 3 (down-right)
+		0x0001FFFF, // X= 1, Y=-1 -> 1 (up-right)
+	};
+
+	static const int16 dirMap[] = {6, 5, 7, -1, 4, 0, 2, 3, 1};
+
+	for (int i = 0; i < 9; i++) {
+		if (dirTable[i] == xpDir)
+			return dirMap[i];
+	}
+
+	return 0;
+}
+
+int16 BoltEngine::playScooby() {
+	int16 inputDir = -1;
+
+	// Check if help screen should show on startup
+	if (g_scoobyShowHelp != 0) {
+		int16 helpResult = helpScooby();
+		if (helpResult == 0)
+			return 5; // exit/quit
+	}
+
+	g_scoobyShowHelp = 0;
+	_xp->enableController();
+
+	// Main game loop
+	while (true) {
+		updateScoobyLocation();
+
+		// Level complete?
+		if (g_scoobyGameState.levelComplete != 0)
+			return 16;
+
+		// Poll for event
+		uint32 eventData;
+		int16 eventType = _xp->getEvent(etEmpty, &eventData);
+
+		switch (eventType) {
+		case etJoystick:
+			if (eventData != 0)
+				_xp->setInactivityTimer(30);
+
+			inputDir = xpDirToBOLTDir(eventData);
+			break;
+
+		case etMouseDown:
+		case etInactivity:
+			if (helpScooby() == 0)
+				return 5;
+			g_scoobyMoveRequested = 0;
+			break;
+
+		case etSound:
+			g_scoobySoundPlaying = 0;
+			updateScoobySound();
+			break;
+
+		default:
+			break;
+		}
+
+		updateScoobyDirection(inputDir);
+		updateScoobyTransition();
+		updateScoobyWalls();
+		_xp->updateDisplay();
+	}
+}
+
+int16 BoltEngine::scoobyGame(int16 prevBooth) {
+	int16 result = 5;
+
+	if (!openBOLTLib(&g_scoobyBoltLib, &g_scoobyBoltCallbacks, assetPath("scooby.blt")))
+		return result;
+
+	int16 prevInactivity = _xp->setInactivityTimer(30);
+
+	if (initScooby()) {
+		result = playScooby();
+	}
+
+	cleanUpScooby();
+	_xp->setInactivityTimer(prevInactivity);
+	closeBOLTLib(&g_scoobyBoltLib);
+
+	return result;
+}
+
+void BoltEngine::swapScoobyHelpEntry() {
+	byte *data = g_boltCurrentMemberEntry->dataPtr;
+	if (data == nullptr)
+		return;
+
+	WRITE_UINT32(data, READ_BE_UINT32(data));
+	WRITE_UINT16(data + 4, READ_BE_UINT16(data + 4));
+	WRITE_UINT32(data + 6, READ_BE_UINT32(data + 6));
+	WRITE_UINT16(data + 0x0A, READ_BE_UINT16(data + 0x0A));
+	WRITE_UINT16(data + 0x0C, READ_BE_UINT16(data + 0x0C));
+	unpackColors(13, data + 0x0E);
+}
+
+void BoltEngine::swapScoobyWordArray() {
+	byte *data = g_boltCurrentMemberEntry->dataPtr;
+	if (data == nullptr)
+		return;
+
+	int16 count = g_boltCurrentMemberEntry->decompSize >> 1;
 
+	for (int16 i = 0; i < count; i++) {
+		WRITE_UINT16(data, READ_BE_UINT16(data));
+		data += 2;
+	}
+}
 
 } // End of namespace Bolt
diff --git a/engines/bolt/booths/topcat.cpp b/engines/bolt/booths/topcat.cpp
index 133a89ca72d..bd6233d20ae 100644
--- a/engines/bolt/booths/topcat.cpp
+++ b/engines/bolt/booths/topcat.cpp
@@ -1100,7 +1100,7 @@ void BoltEngine::shuffleTopCatPermutations() {
 	}
 }
 
-void BoltEngine::getTopCatSoundInfo(BOLTLib *lib, int16 memberId, FredSoundInfo *soundInfo) {
+void BoltEngine::getTopCatSoundInfo(BOLTLib *lib, int16 memberId, SoundInfo *soundInfo) {
 	soundInfo->data = memberAddr(lib, memberId);
 	soundInfo->size = memberSize(lib, memberId);
 }
diff --git a/engines/bolt/resource.cpp b/engines/bolt/resource.cpp
index 29e024c643e..facf68567ec 100644
--- a/engines/bolt/resource.cpp
+++ b/engines/bolt/resource.cpp
@@ -647,6 +647,9 @@ BOLTCallback BoltEngine::g_defaultGroupFreeCallbacks[25];
 BOLTCallback BoltEngine::g_fredTypeLoadCallbacks[28];
 BOLTCallback BoltEngine::g_fredTypeFreeCallbacks[28];
 
+BOLTCallback BoltEngine::g_scoobyTypeLoadCallbacks[28];
+BOLTCallback BoltEngine::g_scoobyTypeFreeCallbacks[28];
+
 BOLTCallback BoltEngine::g_topCatTypeLoadCallbacks[26];
 BOLTCallback BoltEngine::g_topCatTypeFreeCallbacks[26];
 
@@ -666,11 +669,13 @@ void BoltEngine::swapFredAnimEntryCb() { ((BoltEngine *)g_engine)->swapFredAnimE
 void BoltEngine::swapFredAnimDescCb() { ((BoltEngine *)g_engine)->swapFredAnimDesc(); }
 void BoltEngine::swapFredLevelDescCb() { ((BoltEngine *)g_engine)->swapFredLevelDesc(); }
 
+void BoltEngine::swapScoobyHelpEntryCb() { ((BoltEngine *)g_engine)->swapScoobyHelpEntry(); }
+void BoltEngine::swapScoobyWordArrayCb() { ((BoltEngine *)g_engine)->swapScoobyWordArray(); }
+
 void BoltEngine::swapTopCatHelpEntryCb() { ((BoltEngine *)g_engine)->swapTopCatHelpEntry(); }
 
 void BoltEngine::initCallbacks() {
 	// --- BOOTHS ---
-
 	for (int i = 0; i < ARRAYSIZE(g_defaultTypeLoadCallbacks); i++) {
 		g_defaultTypeLoadCallbacks[i] = noOpCb;
 	}
@@ -712,7 +717,6 @@ void BoltEngine::initCallbacks() {
 	g_boothsBoltCallbacks.groupFreeCallbacks = g_defaultGroupFreeCallbacks;
 
 	// --- FRED ---
-
 	for (int i = 0; i < ARRAYSIZE(g_fredTypeLoadCallbacks); i++) {
 		g_fredTypeLoadCallbacks[i] = noOpCb;
 	}
@@ -741,6 +745,31 @@ void BoltEngine::initCallbacks() {
 	g_fredBoltCallbacks.groupLoadCallbacks = g_defaultGroupLoadCallbacks;
 	g_fredBoltCallbacks.groupFreeCallbacks = g_defaultGroupFreeCallbacks;
 
+	// --- SCOOBY ---
+	for (int i = 0; i < ARRAYSIZE(g_scoobyTypeLoadCallbacks); i++) {
+		g_scoobyTypeLoadCallbacks[i] = noOpCb;
+	}
+
+	g_scoobyTypeLoadCallbacks[8] = swapSpriteHeaderCb;
+	g_scoobyTypeLoadCallbacks[10] = swapPicHeaderCb;
+	g_scoobyTypeLoadCallbacks[11] = swapAndResolvePicDescCb;
+	g_scoobyTypeLoadCallbacks[12] = swapFirstTwoWordsCb;
+	g_scoobyTypeLoadCallbacks[26] = swapScoobyHelpEntryCb;
+	g_scoobyTypeLoadCallbacks[27] = swapScoobyWordArrayCb;
+
+	for (int i = 0; i < ARRAYSIZE(g_scoobyTypeFreeCallbacks); i++) {
+		g_scoobyTypeFreeCallbacks[i] = noOpCb;
+	}
+
+	g_scoobyTypeFreeCallbacks[8] = freeSpriteCleanUpCb;
+
+	g_scoobyBoltCallbacks.typeLoadCallbacks = g_scoobyTypeLoadCallbacks;
+	g_scoobyBoltCallbacks.typeFreeCallbacks = g_scoobyTypeFreeCallbacks;
+	g_scoobyBoltCallbacks.memberLoadCallbacks = g_defaultMemberLoadCallbacks;
+	g_scoobyBoltCallbacks.memberFreeCallbacks = g_defaultMemberFreeCallbacks;
+	g_scoobyBoltCallbacks.groupLoadCallbacks = g_defaultGroupLoadCallbacks;
+	g_scoobyBoltCallbacks.groupFreeCallbacks = g_defaultGroupFreeCallbacks;
+
 	// --- TOPCAT ---
 	for (int i = 0; i < ARRAYSIZE(g_topCatTypeLoadCallbacks); i++) {
 		g_topCatTypeLoadCallbacks[i] = noOpCb;
diff --git a/engines/bolt/rtf.cpp b/engines/bolt/rtf.cpp
index e6c15d95a09..e12b741aa19 100644
--- a/engines/bolt/rtf.cpp
+++ b/engines/bolt/rtf.cpp
@@ -68,6 +68,8 @@ RTFResource *BoltEngine::openRTF(const char *fileName) {
 								rtf->indexTable[i] = indexDataStream->readUint32BE();
 							}
 
+							delete indexDataStream;
+
 							return rtf;
 						}
 					}
diff --git a/engines/bolt/ssprite.cpp b/engines/bolt/ssprite.cpp
index d0e5b38e0bf..90829dbdd60 100644
--- a/engines/bolt/ssprite.cpp
+++ b/engines/bolt/ssprite.cpp
@@ -23,6 +23,325 @@
 
 namespace Bolt {
 
+void BoltEngine::setUpSSprite(SSprite *sprite, int16 frameCount, byte **frameData, int16 frameRate, int16 velocityX, int16 velocityY) {
+	sprite->frameCount = frameCount;
+	sprite->frameData = frameData;
+	sprite->frameRate = frameRate;
+	sprite->frameTimer = frameRate;
+	sprite->velocityX = velocityX;
+	sprite->velocityY = velocityY;
 
+	// Read collision bounds from first frame's pic descriptor
+	byte *firstFrame = sprite->frameData[0];
+	sprite->collX = READ_UINT16(firstFrame + 6);
+	sprite->collY = READ_UINT16(firstFrame + 8);
+	sprite->collW = READ_UINT16(firstFrame + 0x0A);
+	sprite->collH = READ_UINT16(firstFrame + 0x0C);
+
+	sprite->xLimitHigh = 0;
+	sprite->xLimitLow = 0;
+	sprite->yLimitHigh = 0;
+	sprite->yLimitLow = 0;
+}
+
+void BoltEngine::animateSSprite(SSprite *sprite, int16 page) {
+	if (!(sprite->flags & 0x01))
+		return;
+
+	// Frozen: call update func or display and return
+	if (sprite->flags & 0x80) {
+		if (sprite->updateFunc) {
+			sprite->updateFunc();
+			return;
+		}
+
+		_xp->fillDisplay(0, stFront);
+		byte *frame = sprite->frameData[sprite->currentFrame];
+		displayPic(frame, sprite->x, sprite->y, page);
+		return;
+	}
+
+	// Advance frame timer
+	sprite->frameTimer--;
+	if (sprite->frameTimer == 0) {
+		sprite->frameTimer = sprite->frameRate;
+		sprite->currentFrame++;
+		if (sprite->currentFrame == sprite->frameCount)
+			sprite->currentFrame = 0;
+	}
+
+	// Movement: path or velocity
+	if (sprite->flags & 0x08) {
+		// Path mode
+		if (sprite->pathOffset >= sprite->pathLength) {
+			// Path complete
+			sprite->flags &= 0xFFF6; // clear alive + path bits
+			if (sprite->pathCallback) {
+				sprite->pathCallback(sprite);
+			}
+		} else {
+			// Read next x,y from path data
+			int16 idx = sprite->pathOffset;
+			sprite->pathOffset++;
+			int16 *pathWords = (int16 *)sprite->pathData;
+			sprite->x = pathWords[idx];
+
+			idx = sprite->pathOffset;
+			sprite->pathOffset++;
+			sprite->y = pathWords[idx];
+		}
+	} else {
+		// Velocity mode
+		int16 vx = sprite->velocityX;
+		int16 vy = sprite->velocityY;
+
+		sprite->x += vx;
+		sprite->y += vy;
+
+		sprite->velocityX += sprite->accelX;
+		sprite->velocityY += sprite->accelY;
+	}
+
+	// X limit clamping
+	if (sprite->flags & 0x20) {
+		if (sprite->x < sprite->xLimitHigh) {
+			sprite->x = sprite->xLimitHigh;
+			sprite->accelX = 0;
+			sprite->velocityX = 0;
+		} else if (sprite->x > sprite->xLimitLow) {
+			sprite->x = sprite->xLimitLow;
+			sprite->accelX = 0;
+			sprite->velocityX = 0;
+		}
+	}
+
+	// Y limit clamping
+	if (sprite->flags & 0x40) {
+		if (sprite->y < sprite->yLimitHigh) {
+			sprite->y = sprite->yLimitHigh;
+			sprite->accelY = 0;
+			sprite->velocityY = 0;
+		} else if (sprite->y > sprite->yLimitLow) {
+			sprite->y = sprite->yLimitLow;
+			sprite->accelY = 0;
+			sprite->velocityY = 0;
+		}
+	}
+
+	// Call update func or display
+	if (sprite->updateFunc) {
+		sprite->updateFunc();
+	} else {
+		_xp->fillDisplay(0, stFront);
+		byte *frame = sprite->frameData[sprite->currentFrame];
+		displayPic(frame, sprite->x, sprite->y, page);
+	}
+
+	// Stop countdown
+	if (sprite->flags & 0x04) {
+		if (sprite->stopFrame != 0) {
+			sprite->stopFrame--;
+			return;
+		}
+
+		sprite->flags &= ~0x01; // not alive
+		sprite->flags &= ~0x04; // no stop
+		return;
+	}
+
+	// Start countdown
+	if (sprite->flags & 0x02) {
+		if (sprite->startFrame != 0) {
+			sprite->startFrame--;
+			return;
+		}
+
+		sprite->flags |= 0x01;  // alive
+		sprite->flags &= ~0x02; // no longer starting
+	}
+}
+
+void BoltEngine::displaySSprite(SSprite *sprite, int16 x, int16 y) {
+	sprite->x = x;
+	sprite->y = y;
+	sprite->flags |= 0x01; // alive
+}
+
+void BoltEngine::eraseSSprite(SSprite *sprite) {
+	sprite->flags &= ~0x01; // not alive
+}
+
+void BoltEngine::setSSpriteFrames(SSprite *sprite, int16 frameCount, byte **frameData, int16 frameRate) {
+	sprite->frameCount = frameCount;
+	sprite->frameData = frameData;
+	sprite->currentFrame = 0;
+	sprite->frameRate = frameRate;
+}
+
+void BoltEngine::setSSpriteDrag(SSprite *sprite, int16 dragX, int16 dragY) {
+	sprite->dragX = dragX;
+	sprite->dragY = dragY;
+}
+
+void BoltEngine::setSSpriteAccel(SSprite *sprite, int16 accelX, int16 accelY) {
+	sprite->accelX = accelX;
+	sprite->accelY = accelY;
+}
+
+void BoltEngine::reverseSSpriteAccel(SSprite *sprite) {
+	sprite->accelX = -sprite->accelX;
+	sprite->accelY = -sprite->accelY;
+}
+
+void BoltEngine::addSSpriteAccel(SSprite *sprite, int16 dx, int16 dy) {
+	sprite->accelX += dx;
+	sprite->accelY += dy;
+}
+
+void BoltEngine::setSSpriteVelocity(SSprite *sprite, int16 vx, int16 vy) {
+	sprite->velocityX = vx;
+	sprite->velocityY = vy;
+}
+
+void BoltEngine::reverseSSpriteVelocity(SSprite *sprite) {
+	sprite->velocityX = -sprite->velocityX;
+	sprite->velocityY = -sprite->velocityY;
+}
+
+void BoltEngine::setSSpriteStart(SSprite *sprite, int16 startFrame, int16 x, int16 y) {
+	sprite->startFrame = startFrame;
+	sprite->x = x;
+	sprite->y = y;
+	sprite->flags |= 0x02;  // started
+	sprite->flags &= ~0x01; // not alive
+	sprite->flags &= ~0x04; // no stop
+}
+
+void BoltEngine::setSSpriteStop(SSprite *sprite, int16 stopFrame) {
+	sprite->stopFrame = stopFrame;
+	sprite->flags |= 0x04; // hasStop
+}
+
+void BoltEngine::setSSpritePath(SSprite *sprite, byte *pathData, int16 pathCount, SSpritePathCallback callback) {
+	sprite->pathLength = pathCount * 2;
+	sprite->pathData = pathData;
+	sprite->pathOffset = 0;
+	sprite->pathCallback = callback;
+	sprite->flags |= 0x09; // alive + hasPath
+}
+
+bool BoltEngine::inSSprite(SSprite *sprite, int16 x, int16 y) {
+	g_spriteCollTempX = sprite->collX;
+	g_spriteCollTempY = sprite->collY;
+	g_spriteCollTempW = sprite->collW;
+	g_spriteCollTempH = sprite->collH;
+
+	g_spriteScreenX = sprite->x + g_spriteCollTempX;
+	g_spriteScreenY = sprite->y + g_spriteCollTempY;
+
+	if ((uint16)x < (uint16)g_spriteScreenX)
+		return false;
+
+	if ((uint16)y < (uint16)g_spriteScreenY)
+		return false;
+
+	if ((uint16)(g_spriteScreenX + sprite->collW) < (uint16)x)
+		return false;
+
+	if ((uint16)(g_spriteScreenY + sprite->collH) < (uint16)y)
+		return false;
+
+	return true;
+}
+
+bool BoltEngine::sSpriteCollide(SSprite *spriteA, SSprite *spriteB) {
+	g_spriteCollTempA[0] = spriteA->collX;
+	g_spriteCollTempA[1] = spriteA->collY;
+	g_spriteCollTempA[2] = spriteA->collW;
+	g_spriteCollTempA[3] = spriteA->collH;
+
+	g_spriteCollTempB[0] = spriteB->collX;
+	g_spriteCollTempB[1] = spriteB->collY;
+	g_spriteCollTempB[2] = spriteB->collW;
+	g_spriteCollTempB[3] = spriteB->collH;
+
+	g_spriteScreenAX = spriteA->x + g_spriteCollTempA[0];
+	g_spriteScreenBX = spriteB->x + g_spriteCollTempB[0];
+	g_spriteScreenAY = spriteA->y + g_spriteCollTempA[1];
+	g_spriteScreenBY = spriteB->y + g_spriteCollTempB[1];
+
+	if ((uint16)(g_spriteScreenAX + g_spriteCollTempA[2]) < (uint16)g_spriteScreenBX)
+		return false;
+	if ((uint16)(g_spriteScreenAY + g_spriteCollTempA[3]) < (uint16)g_spriteScreenBY)
+		return false;
+	if ((uint16)(g_spriteScreenBX + g_spriteCollTempB[2]) < (uint16)g_spriteScreenAX)
+		return false;
+	if ((uint16)(g_spriteScreenBY + g_spriteCollTempB[3]) < (uint16)g_spriteScreenAY)
+		return false;
+
+	return true;
+}
+
+void BoltEngine::setSSpriteCollision(SSprite *sprite, int16 *bounds) {
+	// bounds is a 4-word array: [offsetX, offsetY, width, height]
+	byte *firstFrame = sprite->frameData[0];
+
+	sprite->collX = READ_UINT16(firstFrame + 6) + bounds[0];
+	sprite->collY = bounds[1] + READ_UINT16(firstFrame + 8);
+	sprite->collW = bounds[2];
+	sprite->collH = bounds[3];
+}
+
+bool BoltEngine::sSpriteAlive(SSprite *sprite) {
+	return (sprite->flags & 0x01) != 0;
+}
+
+void BoltEngine::getSSpriteLoc(SSprite *sprite, Common::Point *out) {
+	(*out).x = sprite->x;
+	(*out).y = sprite->y;
+}
+
+void BoltEngine::getSSpriteAccel(SSprite *sprite, int16 *out) {
+	out[0] = sprite->accelX;
+	out[1] = sprite->accelY;
+}
+
+void BoltEngine::getSSpriteVelocity(SSprite *sprite, int16 *out) {
+	out[0] = sprite->velocityX;
+	out[1] = sprite->velocityY;
+}
+
+void BoltEngine::getSSpriteDrag(SSprite *sprite, int16 *out) {
+	out[0] = sprite->dragX;
+	out[1] = sprite->dragY;
+}
+
+void BoltEngine::setSSpriteXLimit(SSprite *sprite, int16 high, int16 low) {
+	sprite->xLimitHigh = high;
+	sprite->xLimitLow = low;
+	sprite->flags |= 0x20;
+}
+
+void BoltEngine::setSSpriteYLimit(SSprite *sprite, int16 high, int16 low) {
+	sprite->yLimitHigh = high;
+	sprite->yLimitLow = low;
+	sprite->flags |= 0x40;
+}
+
+void BoltEngine::setSSpriteInfo(SSprite *sprite, int16 info) {
+	sprite->userInfo = info;
+}
+
+int16 BoltEngine::getSSpriteInfo(SSprite *sprite) {
+	return sprite->userInfo;
+}
+
+void BoltEngine::freezeSSprite(SSprite *sprite) {
+	sprite->flags |= 0x80;
+}
+
+void BoltEngine::unfreezeSSprite(SSprite *sprite) {
+	sprite->flags &= 0x0F7F;
+}
 
 } // End of namespace Bolt


Commit: bb96b5c22d29cb187fd6d94c0376114e64f0f387
    https://github.com/scummvm/scummvm/commit/bb96b5c22d29cb187fd6d94c0376114e64f0f387
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-04-07T01:11:14+02:00

Commit Message:
BOLT: CARNIVAL: Fix Fred's minigame regression

Changed paths:
    engines/bolt/booths/fred.cpp


diff --git a/engines/bolt/booths/fred.cpp b/engines/bolt/booths/fred.cpp
index 2622d3ec7ee..ceaa40d8ebe 100644
--- a/engines/bolt/booths/fred.cpp
+++ b/engines/bolt/booths/fred.cpp
@@ -1131,7 +1131,7 @@ void BoltEngine::setFredAnimMode(FredEntityState *state, int16 mode) {
 		int16 typeIdx = mode - 0x0B;
 		state->animTable = getResolvedPtr(g_fredBalloonSprite, typeIdx * 4);
 		state->frameIndex = 0;
-		state->frameCountdown = READ_UINT16(g_fredBalloonSprite + typeIdx * 4 + 4);
+		state->frameCountdown = READ_UINT16(state->animTable + 4);
 		return;
 	}
 
@@ -1144,12 +1144,10 @@ void BoltEngine::setFredAnimMode(FredEntityState *state, int16 mode) {
 
 	// Standard Fred animation...
 	state->animMode = mode;
-
-	byte *animTable = g_fredSprites[mode];
-	state->animTable = animTable;
+	state->animTable = g_fredSprites[mode];
 
 	state->frameIndex = 0;
-	state->frameCountdown = READ_UINT16(animTable + 4);
+	state->frameCountdown = READ_UINT16(state->animTable + 4);
 
 	// Play sound based on mode...
 	switch (mode) {


Commit: 4ae8a3557ac6f46e2b24d7b9f9a71a3204892e8c
    https://github.com/scummvm/scummvm/commit/4ae8a3557ac6f46e2b24d7b9f9a71a3204892e8c
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-04-07T01:11:14+02:00

Commit Message:
BOLT: CARNIVAL: Implement George's minigame

Changed paths:
    engines/bolt/bolt.h
    engines/bolt/booths/george.cpp
    engines/bolt/booths/scooby.cpp
    engines/bolt/resource.cpp


diff --git a/engines/bolt/bolt.h b/engines/bolt/bolt.h
index 5c9f70f4025..6e4235aa41d 100644
--- a/engines/bolt/bolt.h
+++ b/engines/bolt/bolt.h
@@ -301,10 +301,14 @@ typedef struct SSprite {
 typedef struct SoundInfo {
 	byte *data;
 	uint32 size;
+	byte priority;
+	byte channel;
 
 	SoundInfo() {
 		data = nullptr;
 		size = 0;
+		priority = 0;
+		channel = 0;
 	}
 } SoundInfo;
 
@@ -340,6 +344,48 @@ typedef struct FredEntityState {
 	}
 } FredEntityState;
 
+// GEORGE GAME
+
+typedef struct GeorgeEntityState {
+	uint16 flags;
+	int16 frameCountdown;
+	int16 animMode;
+	int16 variant;
+	int16 frameIndex;
+	byte *animTable;
+	int32 joyInput;
+	int32 x;
+	int32 y;
+	int32 prevX;
+	int32 prevY;
+	int32 velX;
+	int32 velY;
+	int32 accelX;
+	int32 accelY;
+	byte *pathTable;
+	int16 pathIndex;
+
+	GeorgeEntityState() {
+		flags = 0;
+		frameCountdown = 0;
+		animMode = 0;
+		variant = 0;
+		frameIndex = 0;
+		animTable = nullptr;
+		joyInput = 0;
+		x = 0;
+		y = 0;
+		prevX = 0;
+		prevY = 0;
+		velX = 0;
+		velY = 0;
+		accelX = 0;
+		accelY = 0;
+		pathTable = nullptr;
+		pathIndex = 0;
+	}
+} GeorgeEntityState;
+
 // SCOOBY GAME
 
 typedef struct ScoobyState {
@@ -418,10 +464,10 @@ typedef struct ScoobyRect {
 // TOPCAT GAME
 
 typedef struct TopCatAnim {
-	int16 animType; // + 00
-	int16 animIndex;// + 02
-	int16 transitionToNextQuestionFlag;// + 04
-	int16 *seqPtr;  // + 04
+	int16 animType;
+	int16 animIndex;
+	int16 transitionToNextQuestionFlag;
+	int16 *seqPtr;
 
 	TopCatAnim() {
 		animType = 0;
@@ -898,7 +944,92 @@ protected:
 	const char *g_fredSaveFile = "FredBC";
 
 	// --- GEORGE ---
-	int16 georgeGame(int16 prevBooth) { return 0; }
+	int16 georgeGame(int16 prevBooth);
+	bool initGeorge();
+	void cleanUpGeorge();
+	bool initGeorgeLevel(int16 level, int16 variant);
+	void termGeorgeLevel(int16 level, int16 variant);
+	void swapGeorgeFrameArray();
+	void swapGeorgeHelpEntry();
+	void swapGeorgeThresholds();
+	int16 playGeorge();
+	int16 helpGeorge();
+	void hiliteGeorgeHelpObject(byte *entry, int16 highlight);
+	void advanceHelpAnimation();
+	bool spawnSatellite();
+	int16 getRandomSatelliteWait();
+	int16 getRandomAsteroidRow();
+	bool confirmAsteroidHitTest();
+	bool spawnAsteroid();
+	int16 getRandomAsteroidWait();
+	int16 getAsteroidRow();
+	void setGeorgeAnimMode(GeorgeEntityState *sat, int16 mode);
+	void setSatelliteAnimMode(GeorgeEntityState *sat, int16 mode, int16 variant);
+	void setAsteroidAnimMode(GeorgeEntityState *sat, int16 mode, int16 variant);
+	void drawFlyingObjects();
+	void getGeorgeSoundInfo(BOLTLib *boltLib, int16 member, SoundInfo *outInfo, byte priority);
+	void playGeorgeSound(SoundInfo *newSound, SoundInfo *nextSound);
+	void updateGeorgeSound();
+
+	static void swapGeorgeFrameArrayCb();
+	static void swapGeorgeHelpEntryCb();
+	static void swapGeorgeThresholdsCb();
+
+	BOLTLib *g_georgeBoltLib = nullptr;
+	BOLTCallbacks g_georgeBoltCallbacks;
+
+	static BOLTCallback g_georgeTypeLoadCallbacks[28];
+	static BOLTCallback g_georgeTypeFreeCallbacks[28];
+
+	byte *g_georgePrevActiveHelpObject = nullptr;
+	byte *g_georgeActiveHelpObject = nullptr;
+	int16 g_georgeHelpStep = -1;
+	int16 g_georgeHelpActive = 1;
+	const char *g_georgeSaveFileName = "GeorgeBE";
+
+	GeorgeEntityState **g_georgeEntityList = nullptr;
+	int16 g_georgeCarIdx = 0;
+	int16 g_georgeNumSatellites = 0;
+	int16 g_georgeFirstAsteroidIdx = 0;
+	int16 g_georgeTotalSatellites = 0;
+	byte *g_georgeCarPics[3] = { nullptr };
+	byte *g_georgeHelpObjects = nullptr;
+	byte *g_georgeHelpSequence = nullptr;
+	uint32 g_georgeHelpTimer = 0;
+	byte *g_georgeSatelliteShuffleTable = nullptr;
+	byte *g_georgeAsteroidShuffleTable = nullptr;
+	int16 *g_georgeSatelliteThresholds = nullptr;
+	int16 *g_georgeAsteroidThresholds = nullptr;
+	byte *g_georgeSatellitePaths = nullptr;
+	byte *g_georgeAsteroidPaths = nullptr;
+	byte *g_georgeCollisionRect = nullptr;
+	byte *g_georgeSatelliteCollisionRects = nullptr;
+	byte *g_georgeAsteroidCollisionRects = nullptr;
+	byte *g_georgeSatelliteGfx = nullptr;
+	byte *g_georgeAsteroidGfx = nullptr;
+	int16 g_georgeSatelliteWait = 0;
+	int16 g_georgeAsteroidWait = 0;
+	int16 g_georgeSatelliteSearchIdx = 0;
+	int16 g_georgeAsteroidSearchIdx = 0;
+	int16 g_georgeHitSearchIdx = 0;
+	byte *g_georgeBgPic = nullptr;
+	byte *g_georgePalette = nullptr;
+	byte *g_georgePalCycleRawData = nullptr;
+	XPCycleState g_georgePalCycleSpecs[4];
+	int16 g_georgeCollectedSatellitesNum = 0;
+	SoundInfo g_georgeSoundCarStartUp;
+	SoundInfo g_georgeSoundCarGoesAway;
+	SoundInfo g_georgeSoundCarLoopHi;
+	SoundInfo g_georgeSoundCarLoopLo;
+	SoundInfo g_georgeSoundCarTumble;
+	SoundInfo **g_georgeSatelliteSoundList = nullptr;
+	byte g_georgeSoundChannelCounter = 0;
+	SoundInfo *g_georgeSoundToPlay = nullptr;
+	SoundInfo *g_georgeSoundCurrent = nullptr;
+	SoundInfo *g_georgeSoundQueued = nullptr;
+	SoundInfo *g_georgeSoundNext = nullptr;
+	int16 g_georgeSaveData[3] = { 0 };
+	int16 *g_georgeThresholds = nullptr;
 
 	// --- HUCK ---
 	int16 huckGame(int16 prevBooth) { return 0; }
diff --git a/engines/bolt/booths/george.cpp b/engines/bolt/booths/george.cpp
index d0e5b38e0bf..9fb7c82aec6 100644
--- a/engines/bolt/booths/george.cpp
+++ b/engines/bolt/booths/george.cpp
@@ -23,6 +23,1371 @@
 
 namespace Bolt {
 
+int16 BoltEngine::georgeGame(int16 prevBooth) {
+	int16 result;
+	int16 prevInactivity = _xp->setInactivityTimer(30);
 
+	if (!initGeorge()) {
+		cleanUpGeorge();
+		return 7;
+	}
+
+	result = playGeorge();
+	cleanUpGeorge();
+	_xp->setInactivityTimer(prevInactivity);
+	return result;
+}
+
+bool BoltEngine::initGeorge() {
+	if (!openBOLTLib(&g_georgeBoltLib, &g_georgeBoltCallbacks, assetPath("george.blt")))
+		return false;
+
+	if (!getBOLTGroup(g_georgeBoltLib, 0, 1))
+		return false;
+
+	g_georgeBgPic = memberAddr(g_georgeBoltLib, (g_displayMode != 0) ? 1 : 0);
+	g_georgeHelpObjects = memberAddr(g_georgeBoltLib, 0x1F);
+	g_georgeHelpSequence = memberAddr(g_georgeBoltLib, 0x20);
+
+	g_georgeCarPics[0] = memberAddr(g_georgeBoltLib, 0x10);
+	g_georgeCarPics[1] = memberAddr(g_georgeBoltLib, 0x11);
+	g_georgeCarPics[2] = memberAddr(g_georgeBoltLib, 0x12);
+	g_georgeCollisionRect = memberAddr(g_georgeBoltLib, 0x13);
+
+	getGeorgeSoundInfo(g_georgeBoltLib, 0x16, &g_georgeSoundCarTumble, 6);
+	getGeorgeSoundInfo(g_georgeBoltLib, 0x15, &g_georgeSoundCarLoopHi, 0);
+	getGeorgeSoundInfo(g_georgeBoltLib, 0x14, &g_georgeSoundCarLoopLo, 0);
+	getGeorgeSoundInfo(g_georgeBoltLib, 0x17, &g_georgeSoundCarStartUp, 3);
+	getGeorgeSoundInfo(g_georgeBoltLib, 0x18, &g_georgeSoundCarGoesAway, 3);
+
+	g_georgeSoundCurrent = nullptr;
+	g_georgeSoundToPlay = nullptr;
+	g_georgeSoundNext = nullptr;
+	g_georgeSoundQueued = nullptr;
+	g_georgeSoundChannelCounter = 0;
+
+	// Load save data...
+	if (!vLoad(&g_georgeSaveData, g_georgeSaveFileName)) {
+		g_georgeSaveData[0] = 0;
+		g_georgeSaveData[1] = 0;
+		g_georgeSaveData[2] = 0;
+	}
+
+	if (!initGeorgeLevel(g_georgeSaveData[1], g_georgeSaveData[2])) {
+		freeBOLTGroup(g_georgeBoltLib, 0, 1);
+		return false;
+	}
+
+	// Initialize satellites from list...
+	g_georgeCollectedSatellitesNum = 0;
+	while (g_georgeCollectedSatellitesNum < g_georgeSaveData[0]) {
+		setSatelliteAnimMode(g_georgeEntityList[g_georgeNumSatellites + g_georgeCollectedSatellitesNum], 4, 0);
+		g_georgeCollectedSatellitesNum++;
+	}
+
+	// Drain timer events...
+	uint32 dummy;
+	while (_xp->getEvent(etTimer, &dummy) != etTimer);
+
+	_xp->stopCycle();
+	_xp->setTransparency(false);
+
+	// Display palette and background to front...
+	displayColors(g_georgePalette, stBack, 0);
+	displayPic(g_georgeBgPic, g_displayX, g_displayY, stFront);
+	_xp->updateDisplay();
+
+	_xp->setTransparency(true);
+
+	// Display palette to both surfaces...
+	displayColors(g_georgePalette, stFront, 0);
+	displayColors(g_georgePalette, stBack, 1);
+
+	// Display background to back surface...
+	displayPic(g_georgeBgPic, g_displayX, g_displayY, stBack);
+
+	drawFlyingObjects();
+
+	_xp->updateDisplay();
+	_xp->startCycle(g_georgePalCycleSpecs);
+	_xp->setFrameRate(12);
+
+	return true;
+}
+
+void BoltEngine::cleanUpGeorge() {
+	// Save current progress...
+	int16 savedLevel = g_georgeSaveData[1];
+	int16 savedVariant = g_georgeSaveData[2];
+
+	g_georgeSaveData[0] = g_georgeCollectedSatellitesNum;
+
+	// Check if collection threshold for current level was met...
+	if (g_georgeCollectedSatellitesNum >= g_georgeThresholds[0]) {
+		// Advance level...
+		g_georgeSaveData[1]++;
+		if (g_georgeSaveData[1] >= 10)
+			g_georgeSaveData[1] = 9;
+
+		// Advance variant...
+		g_georgeSaveData[2]++;
+		if (g_georgeSaveData[2] >= 10)
+			g_georgeSaveData[2] = 0;
+
+		// Reset collected count for next level...
+		g_georgeSaveData[0] = 0;
+	}
+
+	vSave(g_georgeSaveData, sizeof(g_georgeSaveData), g_georgeSaveFileName);
+
+	_xp->stopCycle();
+
+	termGeorgeLevel(savedLevel, savedVariant);
+
+	freeBOLTGroup(g_georgeBoltLib, 0, 1);
+	closeBOLTLib(&g_georgeBoltLib);
+
+	_xp->setFrameRate(0);
+	_xp->fillDisplay(0, stFront);
+	_xp->updateDisplay();
+}
+
+bool BoltEngine::initGeorgeLevel(int16 level, int16 variant) {
+	int16 levelGroup = (level * 2) << 8 | 0x100;
+	int16 variantGroup = (variant * 2) << 8 | 0x200;
+
+	if (!getBOLTGroup(g_georgeBoltLib, levelGroup, 1))
+		return false;
+
+	if (!getBOLTGroup(g_georgeBoltLib, variantGroup, 1)) {
+		freeBOLTGroup(g_georgeBoltLib, levelGroup, 1);
+		return false;
+	}
+
+	g_georgeThresholds = (int16 *)memberAddr(g_georgeBoltLib, levelGroup);
+	g_georgePalette = memberAddr(g_georgeBoltLib, variantGroup);
+	g_georgePalCycleRawData = memberAddr(g_georgeBoltLib, variantGroup + 1);
+	boltCycleToXPCycle(g_georgePalCycleRawData, g_georgePalCycleSpecs);
+
+	g_georgeSatelliteGfx = memberAddr(g_georgeBoltLib, variantGroup + 0x12);
+	g_georgeAsteroidGfx = memberAddr(g_georgeBoltLib, variantGroup + 0x26);
+	g_georgeSatelliteThresholds = (int16 *)memberAddr(g_georgeBoltLib, levelGroup + 0x01);
+	g_georgeAsteroidThresholds = (int16 *)memberAddr(g_georgeBoltLib, levelGroup + 0x02);
+	g_georgeSatelliteCollisionRects = memberAddr(g_georgeBoltLib, variantGroup + 0x19);
+	g_georgeAsteroidCollisionRects = memberAddr(g_georgeBoltLib, variantGroup + 0x2A);
+	g_georgeSatellitePaths = memberAddr(g_georgeBoltLib, levelGroup + 0x27);
+	g_georgeAsteroidPaths = memberAddr(g_georgeBoltLib, levelGroup + 0x3C);
+
+	// Allocate and build asteroid shuffle table...
+	int16 numAsteroidRows = g_georgeThresholds[3];
+	int16 numAsteroidCols = g_georgeThresholds[4];
+	g_georgeSatelliteShuffleTable = (byte *)_xp->allocMem((uint32)(numAsteroidCols + 1) * (numAsteroidRows));
+	if (!g_georgeSatelliteShuffleTable) {
+		termGeorgeLevel(level, variant);
+		return false;
+	}
+
+	// Fill each row with sequential values 0..numAsteroidCols, then shuffle...
+	for (int16 row = 0; row < numAsteroidRows; row++) {
+		int16 rowBase = (numAsteroidCols + 1) * row;
+		g_georgeSatelliteShuffleTable[rowBase] = 0;
+
+		for (int16 col = 0; col < numAsteroidCols; col++)
+			g_georgeSatelliteShuffleTable[rowBase + col + 1] = (byte)col;
+
+		// Fisher-Yates shuffle...
+		for (int16 i = 0; i < numAsteroidCols; i++) {
+			int16 j = _xp->getRandom(numAsteroidCols);
+			byte tmp = g_georgeSatelliteShuffleTable[rowBase + i + 1];
+			g_georgeSatelliteShuffleTable[rowBase + i + 1] = g_georgeSatelliteShuffleTable[rowBase + j + 1];
+			g_georgeSatelliteShuffleTable[rowBase + j + 1] = tmp;
+		}
+	}
+
+	// Allocate and build satellite shuffle table...
+	int16 numSatelliteRows = g_georgeThresholds[9];
+	int16 numSatelliteCols = g_georgeThresholds[10];
+	g_georgeAsteroidShuffleTable = (byte *)_xp->allocMem((uint32)(numSatelliteCols + 1) * (numSatelliteRows));
+	if (!g_georgeAsteroidShuffleTable) {
+		termGeorgeLevel(level, variant);
+		return false;
+	}
+
+	for (int16 row = 0; row < numSatelliteRows; row++) {
+		int16 rowBase = (numSatelliteCols + 1) * row;
+		g_georgeAsteroidShuffleTable[rowBase] = 0;
+
+		for (int16 col = 0; col < numSatelliteCols; col++)
+			g_georgeAsteroidShuffleTable[rowBase + col + 1] = (byte)col;
+
+		for (int16 i = 0; i < numSatelliteCols; i++) {
+			int16 j = _xp->getRandom(numSatelliteCols);
+			byte tmp = g_georgeAsteroidShuffleTable[rowBase + i + 1];
+			g_georgeAsteroidShuffleTable[rowBase + i + 1] = g_georgeAsteroidShuffleTable[rowBase + j + 1];
+			g_georgeAsteroidShuffleTable[rowBase + j + 1] = tmp;
+		}
+	}
+
+	// Compute satellite list indices...
+	g_georgeNumSatellites = 0;
+	int16 numSatellites = g_georgeThresholds[0];
+	g_georgeFirstAsteroidIdx = numSatellites;
+
+	int16 numAsteroids = g_georgeThresholds[11];
+	if (numAsteroids < 1)
+		numAsteroids = 1;
+
+	g_georgeCarIdx = g_georgeFirstAsteroidIdx + numAsteroids;
+	g_georgeTotalSatellites = g_georgeCarIdx + 1;
+
+	// Allocate satellite list...
+	g_georgeEntityList = (GeorgeEntityState **)_xp->allocMem((g_georgeTotalSatellites + 1) * sizeof(GeorgeEntityState *));
+	if (!g_georgeEntityList) {
+		termGeorgeLevel(level, variant);
+		return false;
+	}
+
+	for (int16 i = 0; i < g_georgeTotalSatellites; i++) {
+		g_georgeEntityList[i] = new GeorgeEntityState();
+		if (!g_georgeEntityList[i]) {
+			termGeorgeLevel(level, variant);
+			return false;
+		}
+
+		g_georgeEntityList[i]->flags = 0;
+	}
+
+	g_georgeEntityList[g_georgeTotalSatellites] = nullptr;
+
+	GeorgeEntityState *carSat = g_georgeEntityList[g_georgeCarIdx];
+	assert(carSat);
+
+	carSat->flags = 1;
+	carSat->joyInput = 0;
+	carSat->x = 0x4000;  // fixed point = 64.0
+	carSat->y = 0x12C00; // fixed point = 300.0
+	carSat->velX = 0;
+	carSat->velY = 0;
+	carSat->accelX = 0;
+	carSat->accelY = 0;
+	carSat->pathTable = nullptr;
+	carSat->pathIndex = 0;
+	setGeorgeAnimMode(carSat, 0);
+
+	int32 numObjectTypes = g_georgeThresholds[6];
+	byte *objShuf = (byte *)_xp->allocMem(numObjectTypes);
+	if (!objShuf) {
+		termGeorgeLevel(level, variant);
+		return false;
+	}
+
+	for (int16 i = 0; i < numObjectTypes; i++)
+		objShuf[i] = (byte)i;
+
+	// Assign random anim modes to satellites...
+	for (int16 i = 0; i < numSatellites; i++) {
+		if ((i % numObjectTypes) == 0) {
+			// Re-shuffle every full cycle
+			for (int16 j = 0; j < numObjectTypes; j++) {
+				int16 k = _xp->getRandom(numObjectTypes);
+				byte tmp = objShuf[j];
+				objShuf[j] = objShuf[k];
+				objShuf[k] = tmp;
+			}
+		}
+
+		int16 animType = objShuf[i % numObjectTypes];
+		setSatelliteAnimMode(g_georgeEntityList[g_georgeNumSatellites + i], 3, animType);
+	}
+
+	_xp->freeMem(objShuf);
+
+	// Allocate asteroid shuffled list...
+	int16 numAsteroidTypes = g_georgeThresholds[11];
+	if (numAsteroidTypes < 1)
+		numAsteroidTypes = 1;
+
+	byte *astShuf = (byte *)_xp->allocMem(numAsteroidTypes);
+	if (!astShuf) {
+		termGeorgeLevel(level, variant);
+		return false;
+	}
+
+	for (int16 i = 0; i < numAsteroidTypes; i++)
+		astShuf[i] = (byte)i;
+
+	for (int16 i = 0; i < numAsteroids; i++) {
+		if ((i % numAsteroidTypes) == 0) {
+			for (int16 j = 0; j < numAsteroidTypes; j++) {
+				int16 k = _xp->getRandom(numAsteroidTypes);
+				byte tmp = astShuf[j];
+				astShuf[j] = astShuf[k];
+				astShuf[k] = tmp;
+			}
+		}
+
+		int16 animType = astShuf[i % numAsteroidTypes];
+		setAsteroidAnimMode(g_georgeEntityList[g_georgeFirstAsteroidIdx + i], 5, animType);
+	}
+
+	_xp->freeMem(astShuf);
+
+	// Allocate and load sound list for satellites...
+	g_georgeSatelliteSoundList = (SoundInfo **)_xp->allocMem(numObjectTypes * sizeof(SoundInfo *));
+	if (!g_georgeSatelliteSoundList) {
+		termGeorgeLevel(level, variant);
+		return false;
+	}
+
+	for (int16 i = 0; i < numObjectTypes; i++) {
+		g_georgeSatelliteSoundList[i] = new SoundInfo();
+		if (!g_georgeSatelliteSoundList[i]) {
+			termGeorgeLevel(level, variant);
+			return false;
+		}
+
+		int16 member = variantGroup | ((i + 0x1A) & 0xFF);
+		getGeorgeSoundInfo(g_georgeBoltLib, member, g_georgeSatelliteSoundList[i], 6);
+	}
+
+	g_georgeSatelliteWait = 1;
+	g_georgeSatelliteSearchIdx = 0;
+	g_georgeAsteroidSearchIdx = 0;
+	g_georgeAsteroidWait = getRandomAsteroidWait();
+	g_georgeHitSearchIdx = 0;
+	g_georgeCollectedSatellitesNum = 0;
+
+	return true;
+}
+
+void BoltEngine::termGeorgeLevel(int16 level, int16 variant) {
+	if (g_georgeEntityList) {
+		for (int16 i = 0; i < g_georgeTotalSatellites; i++) {
+			GeorgeEntityState *sat = g_georgeEntityList[i];
+			if (sat) {
+				delete sat;
+				g_georgeEntityList[i] = nullptr;
+			}
+		}
+
+		_xp->freeMem(g_georgeEntityList);
+		g_georgeEntityList = nullptr;
+	}
+
+	if (g_georgeSatelliteSoundList) {
+		int32 numObjectTypes = g_georgeThresholds[6];
+		for (int16 i = 0; i < numObjectTypes; i++) {
+			SoundInfo *snd = g_georgeSatelliteSoundList[i];
+			if (snd) {
+				delete snd;
+				g_georgeSatelliteSoundList[i] = nullptr;
+			}
+		}
+
+		_xp->freeMem(g_georgeSatelliteSoundList);
+		g_georgeSatelliteSoundList = nullptr;
+	}
+
+	if (g_georgeAsteroidShuffleTable) {
+		_xp->freeMem(g_georgeAsteroidShuffleTable);
+		g_georgeAsteroidShuffleTable = nullptr;
+	}
+
+	if (g_georgeSatelliteShuffleTable) {
+		_xp->freeMem(g_georgeSatelliteShuffleTable);
+		g_georgeSatelliteShuffleTable = nullptr;
+	}
+
+	freeBOLTGroup(g_georgeBoltLib, (variant * 2) << 8 | 0x200, 1);
+	freeBOLTGroup(g_georgeBoltLib, (level * 2) << 8 | 0x100, 1);
+}
+
+void BoltEngine::swapGeorgeFrameArray() {
+	byte *data = g_boltCurrentMemberEntry->dataPtr;
+	uint32 size = g_boltCurrentMemberEntry->decompSize;
+	uint32 off = 0;
+
+	while ((int32)(size - off) > 0) {
+		WRITE_UINT16(data + off + 4, READ_BE_UINT16(data + off + 4));
+		resolveIt((uint32 *)(data + off));
+		off += 6;
+	}
+}
+
+void BoltEngine::swapGeorgeHelpEntry() {
+	byte *data = g_boltCurrentMemberEntry->dataPtr;
+	uint32 size = g_boltCurrentMemberEntry->decompSize;
+	uint32 off = 0;
+	uint16 palCountOff = (uint16)(off + 0x10);
+
+	while ((int32)(size - off) > 0) {
+		WRITE_UINT32(data + off, READ_BE_UINT32(data + off));
+		WRITE_UINT32(data + off + 0x08, READ_BE_UINT32(data + off + 0x08));
+		WRITE_UINT16(data + off + 0x0C, READ_BE_UINT16(data + off + 0x0C));
+		WRITE_UINT16(data + off + 0x0E, READ_BE_UINT16(data + off + 0x0E));
+		WRITE_UINT16(data + palCountOff, READ_BE_UINT16(data + palCountOff));
+		unpackColors(READ_UINT16(data + palCountOff), data + off + 0x12);
+		unpackColors(READ_UINT16(data + palCountOff), data + off + 0x2E);
+		resolveIt((uint32 *)(data + off + 0x04));
+
+		off += 0x4A;
+		palCountOff += 0x4A;
+	}
+}
+
+void BoltEngine::swapGeorgeThresholds() {
+	byte *data = g_boltCurrentMemberEntry->dataPtr;
+	for (int16 i = 0; i < 12; i++)
+		WRITE_UINT16(data + i * 2, READ_BE_UINT16(data + i * 2));
+}
+
+int16 BoltEngine::playGeorge() {
+	int16 returnCode = 0;
+	int16 flyActive = 1;
+	int16 winSeq = 0;
+	int16 exitGame = 0;
+	int16 firstFrame = 1;
+	int32 joyY = 0;
+
+	if (g_georgeHelpActive) {
+		if (!helpGeorge())
+			return 7;
+	}
+
+	_xp->setInactivityTimer(30);
+	_xp->enableController();
+
+	while (!shouldQuit()) {
+		// ---- Event loop ----
+		while (!shouldQuit()) {
+			if (joyY)
+				_xp->setInactivityTimer(30);
+
+			uint32 eventData = 0;
+			int16 eventType = _xp->getEvent(etEmpty, &eventData);
+			if (!eventType)
+				break;
+
+			switch (eventType) {
+			case etJoystick:
+				joyY = (int16)eventData;
+				break;
+			case etMouseDown:
+			case etInactivity:
+				if (!winSeq) {
+					if (!helpGeorge()) {
+						returnCode = 7;
+						exitGame = 1;
+					}
+				}
+
+				break;
+			case etSound:
+				updateGeorgeSound();
+				break;
+			default:
+				break;
+			}
+		}
+
+		if (exitGame) {
+			if (!winSeq)
+				playGeorgeSound(nullptr, nullptr);
+
+			_xp->stopSound();
+			return returnCode;
+		}
+
+		drawFlyingObjects();
+		_xp->updateDisplay();
+
+		// ---- Car physics / input ----
+		if (flyActive) {
+			if (firstFrame) {
+				firstFrame = 0;
+				playGeorgeSound(&g_georgeSoundCarStartUp, &g_georgeSoundCarLoopHi);
+			}
+
+			if (joyY) {
+				flyActive = 0;
+			} else {
+				GeorgeEntityState *carSat = g_georgeEntityList[g_georgeCarIdx];
+				int16 carY = (int16)(carSat->y >> 8);
+
+				if (carY > 0xD0)
+					carSat->velY = 0xFFFFFA00;
+				else if (carY > 0xBC)
+					carSat->velY = 0xFFFFFB00;
+				else if (carY > 0xA8)
+					carSat->velY = 0xFFFFFC00;
+				else if (carY > 0x9E)
+					carSat->velY = 0xFFFFFD00;
+				else if (carY > 0x94)
+					carSat->velY = 0xFFFFFE00;
+				else if (carY > 0x86)
+					carSat->velY = 0xFFFFFF00;
+				else {
+					carSat->velY = 0;
+					flyActive = 0;
+				}
+			}
+		} else {
+			if (!winSeq) {
+				GeorgeEntityState *carSat = g_georgeEntityList[g_georgeCarIdx];
+				carSat->accelY = (joyY * 3) << 8;
+
+				if (--g_georgeSatelliteWait == 0) {
+					if (spawnSatellite())
+						g_georgeSatelliteWait = getRandomSatelliteWait();
+					else
+						g_georgeSatelliteWait = 1;
+				}
+
+				if (--g_georgeAsteroidWait == 0) {
+					if (spawnAsteroid())
+						g_georgeAsteroidWait = getRandomAsteroidWait();
+					else
+						g_georgeAsteroidWait = 1;
+				}
+			}
+		}
+
+		// ---- Pass 1: animate all satellites ----
+		for (int16 i = 0;; i++) {
+			GeorgeEntityState *sat = g_georgeEntityList[i];
+			if (!sat)
+				break;
+			if (!(sat->flags & 1))
+				continue;
+
+			sat->prevX = sat->x;
+			sat->prevY = sat->y;
+			sat->joyInput = joyY;
+			sat->flags &= ~0x06;
+
+			if (--sat->frameCountdown == 0) {
+				int16 nextFrame = sat->frameIndex + 1;
+				sat->frameIndex = nextFrame;
+
+				byte *entry = sat->animTable + nextFrame * 6;
+				if (!READ_UINT32(entry)) {
+					sat->frameIndex = 0;
+					sat->flags |= 4;
+				}
+
+				entry = sat->animTable + sat->frameIndex * 6;
+				sat->frameCountdown = READ_UINT16(entry + 0x04);
+				sat->flags |= 2;
+			}
+
+			if (sat->animMode == 1 || sat->animMode == 2) {
+				if (sat->flags & 4)
+					setGeorgeAnimMode(sat, 0);
+			}
+		}
+
+		// ---- Pass 2: physics ----
+		for (int16 i = 0;; i++) {
+			GeorgeEntityState *sat = g_georgeEntityList[i];
+			if (!sat)
+				break;
+			if (!(sat->flags & 1))
+				continue;
+
+			switch (sat->animMode) {
+			case 0:
+			case 1:
+			case 2: {
+				sat->x += sat->velX;
+				sat->y += sat->velY;
+				sat->velX += sat->accelX;
+				sat->velY += sat->accelY;
+
+				if (sat->x < 0x4000) {
+					sat->x = 0x4000;
+				} else {
+					int32 xMax = (int32)(winSeq ? 0x1C2 : 0x40) << 8;
+					if (sat->x > xMax)
+						sat->x = xMax;
+				}
+
+				if (sat->y < 0x2600) {
+					sat->y = 0x2600;
+				} else {
+					int32 yMax = (int32)(flyActive ? 0x12C : 0xD0) << 8;
+					if (sat->y > yMax)
+						sat->y = yMax;
+				}
+
+				if (sat->velY < (int32)0xFFFFFA00)
+					sat->velY = (int32)0xFFFFFA00;
+				else if (sat->velY > 0x600)
+					sat->velY = 0x600;
+
+				if (!winSeq) {
+					if (sat->velY <= 0) {
+						if (g_georgeSoundToPlay != &g_georgeSoundCarLoopHi)
+							playGeorgeSound(&g_georgeSoundCarLoopHi, &g_georgeSoundCarLoopHi);
+					} else {
+						if (g_georgeSoundToPlay != &g_georgeSoundCarLoopLo)
+							playGeorgeSound(&g_georgeSoundCarLoopLo, &g_georgeSoundCarLoopLo);
+					}
+				} else {
+					int32 xExit = (int32)0x1C2 << 8;
+					if (sat->x >= xExit)
+						exitGame = 1;
+				}
+				break;
+			}
+
+			case 3:
+			case 5: {
+				sat->pathIndex++;
+				byte *pathBase = sat->pathTable;
+				int16 pathLen = READ_UINT16(pathBase);
+
+				if (sat->pathIndex >= pathLen) {
+					sat->flags &= ~1;
+				} else {
+					byte *entry = pathBase + sat->pathIndex * 4;
+					sat->x = (int32)(int16)READ_UINT16(entry + 0x02) << 8;
+					sat->y = (int32)(int16)READ_UINT16(entry + 0x04) << 8;
+				}
+				break;
+			}
+
+			default:
+				break;
+			}
+		}
+
+		// ---- Pass 3: collision detection ----
+		if (!winSeq) {
+			GeorgeEntityState *carSat = g_georgeEntityList[g_georgeCarIdx];
+
+			byte *carFrameData = getResolvedPtr(carSat->animTable, carSat->frameIndex * 6);
+			int16 carX1 = (int16)(carSat->x >> 8) + READ_UINT16(g_georgeCollisionRect + 0x00) + READ_UINT16(carFrameData + 0x06);
+			int16 carY1 = (int16)(carSat->y >> 8) + READ_UINT16(g_georgeCollisionRect + 0x02) + READ_UINT16(carFrameData + 0x08);
+			int16 carW = READ_UINT16(g_georgeCollisionRect + 0x04);
+			int16 carH = READ_UINT16(g_georgeCollisionRect + 0x06);
+
+			for (int16 i = 0;; i++) {
+				GeorgeEntityState *sat = g_georgeEntityList[i];
+				if (!sat)
+					break;
+
+				if (!(sat->flags & 1))
+					continue;
+
+				if (sat->animMode == 3) {
+					if (carSat->animMode != 0)
+						continue;
+
+					byte *collRect = getResolvedPtr(g_georgeSatelliteCollisionRects, sat->variant * 4);
+					byte *satFrameData = getResolvedPtr(sat->animTable, sat->frameIndex * 6);
+
+					int16 sx1 = (int16)(sat->x >> 8) + READ_UINT16(collRect + 0x00) + READ_UINT16(satFrameData + 0x06);
+					int16 sy1 = (int16)(sat->y >> 8) + READ_UINT16(collRect + 0x02) + READ_UINT16(satFrameData + 0x08);
+					int16 sw = READ_UINT16(collRect + 0x04);
+					int16 sh = READ_UINT16(collRect + 0x06);
+
+					Common::Rect carRect(carX1, carY1, carX1 + carW, carY1 + carH);
+					Common::Rect satRect(sx1, sy1, sx1 + sw, sy1 + sh);
+					if (!carRect.intersects(satRect))
+						continue;
+
+					setGeorgeAnimMode(carSat, 1);
+					setSatelliteAnimMode(sat, 4, 0);
+					g_georgeCollectedSatellitesNum++;
+
+					if (spawnSatellite())
+						g_georgeSatelliteWait = getRandomSatelliteWait();
+					else
+						g_georgeSatelliteWait = 1;
+
+					playGeorgeSound(g_georgeSatelliteSoundList[sat->variant], nullptr);
+				} else if (sat->animMode == 5) {
+					if (carSat->animMode != 0 && carSat->animMode != 1)
+						continue;
+
+					byte *collRects = getResolvedPtr(g_georgeAsteroidCollisionRects, sat->variant * 4);
+					byte *satFrameData = getResolvedPtr(sat->animTable, sat->frameIndex * 6);
+
+					int16 sx1 = (int16)(sat->x >> 8) + READ_UINT16(collRects + 0x00) + READ_UINT16(satFrameData + 0x06);
+					int16 sy1 = (int16)(sat->y >> 8) + READ_UINT16(collRects + 0x02) + READ_UINT16(satFrameData + 0x08);
+					int16 sw = READ_UINT16(collRects + 0x04);
+					int16 sh = READ_UINT16(collRects + 0x06);
+
+					Common::Rect carRect(carX1, carY1, carX1 + carW, carY1 + carH);
+					Common::Rect astRect(sx1, sy1, sx1 + sw, sy1 + sh);
+					if (!carRect.intersects(astRect))
+						continue;
+
+					setGeorgeAnimMode(carSat, 2);
+
+					if (confirmAsteroidHitTest())
+						g_georgeCollectedSatellitesNum--;
+
+					playGeorgeSound(&g_georgeSoundCarTumble, nullptr);
+					break;
+				}
+			}
+		}
+
+		if (!winSeq) {
+			if (g_georgeCollectedSatellitesNum >= g_georgeThresholds[0]) {
+				if (g_georgeSoundToPlay &&
+					g_georgeSoundToPlay->priority > g_georgeSoundCarGoesAway.priority)
+					continue;
+
+				if (g_georgeSoundQueued &&
+					g_georgeSoundQueued->priority > g_georgeSoundCarGoesAway.priority)
+					continue;
+
+				bool allDone = true;
+				for (int16 i = 0;; i++) {
+					GeorgeEntityState *sat = g_georgeEntityList[i];
+					if (!sat)
+						break;
+
+					if (i == g_georgeCarIdx)
+						continue;
+
+					if (sat->flags & 1) {
+						allDone = false;
+						break;
+					}
+				}
+
+				if (allDone) {
+					// We've won!
+					winSeq = 1;
+					returnCode = 0x10;
+					GeorgeEntityState *carSat = g_georgeEntityList[g_georgeCarIdx];
+					carSat->accelX = 0x200;
+					carSat->accelY = 0;
+					playGeorgeSound(&g_georgeSoundCarGoesAway, nullptr);
+				}
+			}
+		}
+	}
+
+	if (!winSeq)
+		playGeorgeSound(nullptr, nullptr);
+
+	_xp->stopSound();
+	return returnCode;
+}
+
+int16 BoltEngine::helpGeorge() {
+	byte *firstObj = getResolvedPtr(g_georgeHelpObjects, 0);
+	byte *firstInfo = getResolvedPtr(firstObj, 0x04);
+	int16 curX = READ_UINT16(firstInfo + 0x06) + READ_UINT16(firstInfo + 0x0A) - 10;
+	int16 curY = READ_UINT16(firstInfo + 0x08) + READ_UINT16(firstInfo + 0x0C) - 10;
+
+	byte *hoveredObj = nullptr;
+	int16 exitCode = -1;
+	int16 animPlaying = 0;
+	int16 audioTick = 0;
+	int16 animReady = 0;
+
+	_xp->setFrameRate(0);
+	_xp->setInactivityTimer(0);
+	_xp->stopSound();
+
+	g_georgeSoundCurrent = g_georgeSoundToPlay = g_georgeSoundNext = g_georgeSoundQueued = nullptr;
+
+	drawFlyingObjects();
+
+	for (int16 i = 0;; i++) {
+		byte *obj = getResolvedPtr(g_georgeHelpObjects, i * 4);
+		if (!obj)
+			break;
+
+		byte *sprite = getResolvedPtr(obj, 0x04);
+		displayPic(sprite, 0, 0, 0);
+	}
+
+	_xp->updateDisplay();
+
+	for (int16 i = 0;; i++) {
+		byte *obj = getResolvedPtr(g_georgeHelpObjects, i * 4);
+		if (!obj)
+			break;
+
+		_xp->getPalette(READ_UINT16(obj + 0x0C), READ_UINT16(obj + 0x10), obj + 0x2E);
+		_xp->getPalette(READ_UINT16(obj + 0x0E), READ_UINT16(obj + 0x10), obj + 0x12);
+
+		WRITE_UINT32(obj + 0x08, READ_UINT32(obj + 0x08) & ~1u);
+		if (READ_UINT32(obj) == 2)
+			hoveredObj = obj;
+	}
+
+	// Show cursor, highlight "PLAY" object
+	_xp->setCursorPos(curX, curY);
+	_xp->setCursorColor(0, 0, 0xFF);
+	_xp->showCursor();
+	g_georgeActiveHelpObject = getResolvedPtr(g_georgeHelpObjects, 0);
+	hiliteGeorgeHelpObject(g_georgeActiveHelpObject, 1);
+
+	// ---- Event loop ----
+	while (!shouldQuit()) {
+		if (animPlaying) {
+			audioTick = maintainAudioPlay(audioTick);
+			if (!audioTick) {
+				bool sameAsHover = (hoveredObj == g_georgeActiveHelpObject);
+				hiliteGeorgeHelpObject(hoveredObj, sameAsHover ? 1 : 0);
+				animPlaying = 0;
+			}
+			audioTick = 0;
+		}
+
+		uint32 eventData = 0;
+		int16 eventType = _xp->getEvent(etEmpty, &eventData);
+
+		switch (eventType) {
+		case etTimer: {
+			if (!g_georgePrevActiveHelpObject)
+				break;
+
+			if (g_georgeHelpTimer != eventData)
+				break;
+
+			g_georgeHelpTimer = _xp->startTimer(500);
+			
+				bool lit = !(READ_UINT32(g_georgePrevActiveHelpObject + 0x08) & 1u);
+				hiliteGeorgeHelpObject(g_georgePrevActiveHelpObject, lit ? 1 : 0);
+			
+			break;
+		}
+
+		case etMouseMove: {
+			curX = (int16)((int32)eventData >> 16);
+			curY = (int16)eventData;
+
+			byte *hit = nullptr;
+			for (int16 i = 0;; i++) {
+				byte *obj = getResolvedPtr(g_georgeHelpObjects, i * 4);
+				if (!obj)
+					break;
+
+				byte *info = getResolvedPtr(obj, 0x04);
+				int16 ox = READ_UINT16(info + 0x06);
+				int16 ow = READ_UINT16(info + 0x0A);
+				int16 oy = READ_UINT16(info + 0x08);
+				int16 oh = READ_UINT16(info + 0x0C);
+				if (curX > ox && curX < ox + ow &&
+					curY > oy && curY < oy + oh) {
+					hit = obj;
+					break;
+				}
+			}
+
+			if (hit == g_georgeActiveHelpObject)
+				break;
+
+			if (g_georgeActiveHelpObject) {
+				if (g_georgeActiveHelpObject != g_georgePrevActiveHelpObject) {
+					bool keepLit = (g_georgeActiveHelpObject == hoveredObj && animPlaying);
+					if (!keepLit)
+						hiliteGeorgeHelpObject(g_georgeActiveHelpObject, 0);
+				}
+			}
+
+			g_georgeActiveHelpObject = hit;
+			if (!hit)
+				break;
+
+			if (hit == g_georgePrevActiveHelpObject)
+				break;
+
+			hiliteGeorgeHelpObject(g_georgeActiveHelpObject, 1);
+			break;
+		}
+
+		case etMouseDown: {
+			animReady = 0;
+
+			if (animPlaying) {
+				if (g_georgeHelpTimer) {
+					_xp->killTimer(g_georgeHelpTimer);
+					g_georgeHelpTimer = 0;
+				}
+
+				bool hovLit = (hoveredObj == g_georgeActiveHelpObject);
+				hiliteGeorgeHelpObject(hoveredObj, hovLit ? 1 : 0);
+				bool actLit = (g_georgePrevActiveHelpObject == g_georgeActiveHelpObject);
+				hiliteGeorgeHelpObject(g_georgePrevActiveHelpObject, actLit ? 1 : 0);
+
+				g_georgePrevActiveHelpObject = nullptr;
+				stopAnimation();
+				animPlaying = 0;
+				animReady = 1;
+			}
+
+			if (!g_georgeActiveHelpObject)
+				break;
+
+			switch (READ_UINT16(g_georgeActiveHelpObject)) {
+			case 0:
+			case 1:
+				if (READ_UINT16(g_georgeActiveHelpObject) == 1) {
+					g_georgeHelpActive = 0;
+				}
+
+				exitCode = READ_UINT16(g_georgeActiveHelpObject);
+				break;
+			case 2:
+				if (animPlaying)
+					break;
+
+				if (animReady)
+					break;
+
+				if (startAnimation(g_rtfHandle, 30)) {
+					g_georgeHelpStep = 0;
+					animPlaying = 1;
+				}
+
+				break;
+			}
+
+			break;
+		}
+
+		case etSound:
+			audioTick = 1;
+			break;
+
+		case etTrigger:
+			if (animPlaying)
+				advanceHelpAnimation();
+			break;
+
+		default:
+			break;
+		}
+
+		if (exitCode != -1) {
+			_xp->hideCursor();
+			_xp->enableController();
+
+			// Highlight hovered object, unhighlight the rest...
+			for (int16 i = 0;; i++) {
+				byte *obj = getResolvedPtr(g_georgeHelpObjects, i * 4);
+				if (!obj)
+					break;
+
+				bool isHovered = (obj == g_georgeActiveHelpObject);
+				hiliteGeorgeHelpObject(obj, isHovered ? 1 : 0);
+			}
+			_xp->updateDisplay();
+
+			// Unhighlight all...
+			for (int16 i = 0;; i++) {
+				byte *obj = getResolvedPtr(g_georgeHelpObjects, i * 4);
+				if (!obj)
+					break;
+
+				hiliteGeorgeHelpObject(obj, 0);
+			}
+
+			drawFlyingObjects();
+			_xp->updateDisplay();
+			_xp->setFrameRate(12);
+			_xp->setInactivityTimer(30);
+			updateGeorgeSound();
+
+			return exitCode;
+		}
+	}
+
+	return exitCode;
+}
+
+void BoltEngine::hiliteGeorgeHelpObject(byte *entry, int16 highlight) {
+	if (!entry)
+		return;
+
+	if (highlight) {
+		_xp->setPalette(READ_UINT16(entry + 0x10), READ_UINT16(entry + 0x0C), entry + 0x12);
+		WRITE_UINT32(entry + 0x08, READ_UINT32(entry + 0x08) | 1);
+	} else {
+		_xp->setPalette(READ_UINT16(entry + 0x10), READ_UINT16(entry + 0x0C), entry + 0x2E);
+		WRITE_UINT32(entry + 0x08, READ_UINT32(entry + 0x08) & ~1);
+	}
+}
+
+void BoltEngine::advanceHelpAnimation() {
+	if (g_georgeHelpStep < 0 || g_georgeHelpStep >= 4)
+		return;
+
+	bool isHovered = (g_georgePrevActiveHelpObject == g_georgeActiveHelpObject);
+	hiliteGeorgeHelpObject(g_georgePrevActiveHelpObject, isHovered ? 1 : 0);
+
+	byte *newActive;
+	if (!(g_georgeHelpStep & 1)) {
+		newActive = getResolvedPtr(g_georgeHelpSequence, (g_georgeHelpStep >> 1) * 4);
+	} else {
+		newActive = nullptr;
+	}
+
+	g_georgePrevActiveHelpObject = newActive;
+	g_georgeHelpTimer = 0;
+
+	hiliteGeorgeHelpObject(g_georgePrevActiveHelpObject, 1);
+
+	if (!(g_georgeHelpStep & 1)) {
+		g_georgeHelpTimer = _xp->startTimer(500);
+	} else {
+		if (g_georgeHelpTimer) {
+			_xp->killTimer(g_georgeHelpTimer);
+			g_georgeHelpTimer = 0;
+		}
+	}
+
+	g_georgeHelpStep++;
+}
+bool BoltEngine::spawnSatellite() {
+	// Count active satellites (type 3)...
+	int16 activeCount = 0;
+	for (int16 i = 0;; i++) {
+		GeorgeEntityState *sat = g_georgeEntityList[i];
+		if (!sat)
+			break;
+
+		if (sat->animMode == 3 && (sat->flags & 1))
+			activeCount++;
+	}
+
+	// Too many active, no need to spawn more...
+	if (activeCount >= 6)
+		return false;
+
+	// Find next inactive satellite slot...
+	int16 startIdx = g_georgeSatelliteSearchIdx;
+	int16 idx = startIdx;
+	bool wrapped = false;
+	GeorgeEntityState *candidate = nullptr;
+
+	while (true) {
+		GeorgeEntityState *s = g_georgeEntityList[idx];
+		if (!s) {
+			wrapped = true;
+			idx = -1;
+		} else if (s->animMode == 3 && !(s->flags & 1)) {
+			candidate = s;
+			break;
+		}
+
+		idx++;
+		if (idx == startIdx && wrapped)
+			return false;
+	}
+
+	g_georgeSatelliteSearchIdx = idx + 1;
+
+	// Get a shuffle-table row for this asteroid...
+	int16 row = getRandomAsteroidRow();
+	int16 numCols = g_georgeThresholds[4];
+	int16 rowBase = (numCols + 1) * row;
+	int16 colIdx = g_georgeSatelliteShuffleTable[rowBase];
+	byte animIdx = g_georgeSatelliteShuffleTable[rowBase + colIdx + 1];
+
+	// Get the motion path...
+	byte *rowPaths = getResolvedPtr(g_georgeSatellitePaths, row * 4);
+	byte *path = getResolvedPtr(rowPaths, animIdx * 4);
+
+	candidate->pathTable = path;
+
+	colIdx++;
+	g_georgeSatelliteShuffleTable[rowBase] = (byte)colIdx;
+	if (colIdx >= numCols)
+		g_georgeSatelliteShuffleTable[rowBase] = 0;
+
+	// Set initial position from pic's embedded coords...
+	candidate->pathIndex = 0;
+	candidate->x = (int32)((int16)READ_UINT16(path + 0x02) << 8);
+	candidate->y = (int32)((int16)READ_UINT16(path + 0x04) << 8);
+
+	candidate->flags |= 1;
+
+	return true;
+}
+
+int16 BoltEngine::getRandomSatelliteWait() {
+	int16 range = g_georgeThresholds[2];
+	int16 topY = g_georgeThresholds[1];
+	return _xp->getRandom(range) + topY;
+}
+
+int16 BoltEngine::getRandomAsteroidRow() {
+	GeorgeEntityState *carSat = g_georgeEntityList[g_georgeCarIdx];
+	int16 carY = carSat->y >> 8;
+
+	// Find which row the car is in by scanning frame Y thresholds...
+	int16 numRows = g_georgeThresholds[3];
+	int16 carRow = 0;
+	int16 *thresholds = g_georgeSatelliteThresholds;
+
+	for (int16 i = 0; i < numRows; i++) {
+		if (thresholds[i] > carY)
+			break;
+
+		carRow = i + 1;
+	}
+
+	// Pick a random row, retrying if too close to car's row...
+	int16 exclusionBand = g_georgeThresholds[5];
+	int16 randRow;
+	do {
+		randRow = _xp->getRandom(g_georgeThresholds[3]);
+	} while (randRow >= (carRow - exclusionBand) && randRow < (carRow + exclusionBand));
+
+	return randRow;
+}
+
+bool BoltEngine::confirmAsteroidHitTest() {
+	GeorgeEntityState *candidate = nullptr;
+	int16 startIdx = g_georgeHitSearchIdx;
+	int16 idx = startIdx;
+	bool wrapped = false;
+
+	while (true) {
+		GeorgeEntityState *s = g_georgeEntityList[idx];
+		if (!s) {
+			wrapped = true;
+			idx = -1;
+		} else if (s->animMode == 4 && (s->flags & 1)) {
+			candidate = s;
+			break;
+		}
+
+		idx++;
+		if (idx == startIdx && wrapped)
+			return false;
+	}
+
+	g_georgeHitSearchIdx = idx + 1;
+
+	setSatelliteAnimMode(candidate, 3, candidate->variant);
+
+	return true;
+}
+
+bool BoltEngine::spawnAsteroid() {
+	// Count active asteroids (type 5)...
+	int16 activeCount = 0;
+	for (int16 i = 0;; i++) {
+		GeorgeEntityState *sat = g_georgeEntityList[i];
+		if (!sat)
+			break;
+
+		if (sat->animMode == 5 && (sat->flags & 1))
+			activeCount++;
+	}
+
+	// No need to spawn more...
+	if (activeCount >= 1)
+		return false;
+
+	// Find next inactive asteroid slot...
+	int16 startIdx = g_georgeAsteroidSearchIdx;
+	int16 idx = startIdx;
+	bool wrapped = false;
+	GeorgeEntityState *candidate = nullptr;
+
+	while (true) {
+		GeorgeEntityState *s = g_georgeEntityList[idx];
+		if (!s) {
+			wrapped = true;
+			idx = -1;
+		} else if (s->animMode == 5 && !(s->flags & 1)) {
+			candidate = s;
+			break;
+		}
+
+		idx++;
+		if (idx == startIdx && wrapped)
+			return false;
+	}
+
+	g_georgeAsteroidSearchIdx = idx + 1;
+
+	// Get shuffle table row for this asteroid...
+	int16 row = getAsteroidRow();
+	int16 numCols = g_georgeThresholds[10];
+	int16 rowBase = (numCols + 1) * row;
+	int16 colIdx = g_georgeAsteroidShuffleTable[rowBase];
+	byte animIdx = g_georgeAsteroidShuffleTable[rowBase + colIdx + 1];
+
+	// Assign motion path...
+	byte *rowPaths = getResolvedPtr(g_georgeAsteroidPaths, row * 4);
+	byte *path = getResolvedPtr(rowPaths, animIdx * 4);
+
+	candidate->pathTable = path;
+	colIdx++;
+	g_georgeAsteroidShuffleTable[rowBase] = (byte)colIdx;
+	if (colIdx >= numCols)
+		g_georgeAsteroidShuffleTable[rowBase] = 0;
+
+	// Set initial position from pic's embedded coords...
+	candidate->pathIndex = 0;
+	candidate->x = (int32)(int16)READ_UINT16(path + 0x02) << 8;
+	candidate->y = (int32)(int16)READ_UINT16(path + 0x04) << 8;
+
+	candidate->flags |= 1;
+
+	return true;
+}
+
+int16 BoltEngine::getRandomAsteroidWait() {
+    int16 base  = g_georgeThresholds[7];
+    int16 range = g_georgeThresholds[8];
+    return _xp->getRandom(range) + base;
+}
+
+int16 BoltEngine::getAsteroidRow() {
+	GeorgeEntityState *carSat = g_georgeEntityList[g_georgeCarIdx];
+	int16 carY = carSat->y >> 8;
+
+	// Find which row the car is in by scanning frame Y thresholds...
+	int16 numRows = g_georgeThresholds[9];
+	int16 row = 0;
+	int16 *thresholds = g_georgeAsteroidThresholds;
+
+	for (int16 i = 0; i < numRows; i++) {
+		if (thresholds[i] > carY)
+			break;
+
+		row = i + 1;
+	}
+
+	return row;
+}
+
+void BoltEngine::setGeorgeAnimMode(GeorgeEntityState *entity, int16 mode) {
+	entity->animMode = mode;
+	entity->variant = 0;
+	entity->animTable = g_georgeCarPics[mode];
+	entity->frameIndex = 0;
+	entity->frameCountdown = READ_UINT16(entity->animTable + 0x04);
+}
+
+void BoltEngine::setSatelliteAnimMode(GeorgeEntityState *entity, int16 mode, int16 variant) {
+	if (mode == 4) {
+		// Deactivate...
+		entity->animMode = mode;
+		entity->flags &= ~1;
+		return;
+	}
+
+	entity->animMode = mode;
+	entity->variant = variant;
+	entity->animTable = getResolvedPtr(g_georgeSatelliteGfx, variant * 4);
+	entity->frameIndex = 0;
+	entity->frameCountdown = READ_UINT16(entity->animTable + 0x04);
+}
+
+void BoltEngine::setAsteroidAnimMode(GeorgeEntityState *entity, int16 mode, int16 variant) {
+	entity->animMode = mode;
+	entity->variant = variant;
+	entity->animTable = getResolvedPtr(g_georgeAsteroidGfx, variant * 4);
+	entity->frameIndex = 0;
+	entity->frameCountdown = READ_UINT16(entity->animTable + 0x04);
+}
+
+void BoltEngine::drawFlyingObjects() {
+	_xp->fillDisplay(0, stFront);
+
+	for (int16 i = 0;; i++) {
+		GeorgeEntityState *sat = g_georgeEntityList[i];
+		if (!sat)
+			break;
+
+		if (!(sat->flags & 1))
+			continue;
+
+		int16 x = sat->x >> 8;
+		int16 y = sat->y >> 8;
+
+		byte *pic = getResolvedPtr(sat->animTable, sat->frameIndex * 6);
+
+		displayPic(pic, x, y, stFront);
+	}
+}
+
+void BoltEngine::getGeorgeSoundInfo(BOLTLib *boltLib, int16 member, SoundInfo *outInfo, byte priority) {
+	outInfo->data = memberAddr(boltLib, member);
+	outInfo->size = memberSize(boltLib, member);
+	outInfo->priority = priority;
+	outInfo->channel = g_georgeSoundChannelCounter++;
+}
+
+void BoltEngine::playGeorgeSound(SoundInfo *newSound, SoundInfo *nextSound) {
+	// If a queued sound exists and has higher priority than new sound, don't replace it...
+	if (newSound && g_georgeSoundQueued) {
+		if (g_georgeSoundQueued->priority > newSound->priority)
+			return;
+	}
+
+	// Queue the new sounds...
+	g_georgeSoundQueued = newSound;
+	g_georgeSoundNext = nextSound;
+
+	// If currently playing sound has higher priority than new one, keep it...
+	if (g_georgeSoundToPlay && newSound) {
+		if (g_georgeSoundToPlay->priority > g_georgeSoundQueued->priority) {
+			// If something is queued but nothing playing, kick off playback now...
+			if (g_georgeSoundQueued && !g_georgeSoundToPlay)
+				updateGeorgeSound();
+
+			return;
+		}
+	}
+
+	// New sound wins...
+	_xp->stopSound();
+	g_georgeSoundCurrent = nullptr;
+	g_georgeSoundToPlay = nullptr;
+
+	// If something is queued but nothing playing, kick off playback now...
+	if (g_georgeSoundQueued && !g_georgeSoundToPlay)
+		updateGeorgeSound();
+}
+
+void BoltEngine::updateGeorgeSound() {
+	int16 playBoth = 0;
+
+	if (g_georgeSoundCurrent != nullptr) {
+		// Current sound still set, promote directly to play slot...
+		g_georgeSoundToPlay = g_georgeSoundCurrent;
+	} else if (g_georgeSoundQueued != nullptr) {
+		// Nothing current, promote queued sound...
+		g_georgeSoundToPlay = g_georgeSoundQueued;
+		g_georgeSoundCurrent = g_georgeSoundNext;
+
+		if (g_georgeSoundNext != nullptr)
+			playBoth = 1;
+
+		g_georgeSoundNext = nullptr;
+		g_georgeSoundQueued = nullptr;
+	} else {
+		g_georgeSoundToPlay = nullptr;
+	}
+
+	if (g_georgeSoundToPlay == nullptr)
+		return;
+
+	// Play primary sound...
+	_xp->playSound(g_georgeSoundToPlay->data, g_georgeSoundToPlay->size, 22050);
+
+	if (!playBoth)
+		return;
+
+	// Queue secondary sound twice...
+	_xp->playSound(g_georgeSoundCurrent->data, g_georgeSoundCurrent->size, 22050);
+	_xp->playSound(g_georgeSoundCurrent->data, g_georgeSoundCurrent->size, 22050);
+}
 
 } // End of namespace Bolt
diff --git a/engines/bolt/booths/scooby.cpp b/engines/bolt/booths/scooby.cpp
index 011bcbb5d1a..0908001c6c6 100644
--- a/engines/bolt/booths/scooby.cpp
+++ b/engines/bolt/booths/scooby.cpp
@@ -835,7 +835,7 @@ void BoltEngine::updateScoobyLocation() {
 	// Scroll Scooby off-screen
 	int16 halfW = READ_UINT16(g_scoobyBaseData + 0x0A) / 2;
 
-	while (true) {
+	while (!shouldQuit()) {
 		if (g_displayX - halfW >= g_scoobyGameState.scoobyX)
 			break;
 
@@ -1654,7 +1654,7 @@ int16 BoltEngine::helpScooby() {
 	hiliteScoobyHelpObject(helpButton, 1);
 
 	// Event loop
-	while (true) {
+	while (!shouldQuit()) {
 		// Handle ongoing audio playback
 		if (isPlaying != 0) {
 			if (!maintainAudioPlay(soundParam)) {
@@ -1886,7 +1886,7 @@ int16 BoltEngine::playScooby() {
 	_xp->enableController();
 
 	// Main game loop
-	while (true) {
+	while (!shouldQuit()) {
 		updateScoobyLocation();
 
 		// Level complete?
@@ -1926,6 +1926,8 @@ int16 BoltEngine::playScooby() {
 		updateScoobyWalls();
 		_xp->updateDisplay();
 	}
+
+	return 0;
 }
 
 int16 BoltEngine::scoobyGame(int16 prevBooth) {
diff --git a/engines/bolt/resource.cpp b/engines/bolt/resource.cpp
index facf68567ec..336493a9a6a 100644
--- a/engines/bolt/resource.cpp
+++ b/engines/bolt/resource.cpp
@@ -647,6 +647,9 @@ BOLTCallback BoltEngine::g_defaultGroupFreeCallbacks[25];
 BOLTCallback BoltEngine::g_fredTypeLoadCallbacks[28];
 BOLTCallback BoltEngine::g_fredTypeFreeCallbacks[28];
 
+BOLTCallback BoltEngine::g_georgeTypeLoadCallbacks[28];
+BOLTCallback BoltEngine::g_georgeTypeFreeCallbacks[28];
+
 BOLTCallback BoltEngine::g_scoobyTypeLoadCallbacks[28];
 BOLTCallback BoltEngine::g_scoobyTypeFreeCallbacks[28];
 
@@ -674,6 +677,10 @@ void BoltEngine::swapScoobyWordArrayCb() { ((BoltEngine *)g_engine)->swapScoobyW
 
 void BoltEngine::swapTopCatHelpEntryCb() { ((BoltEngine *)g_engine)->swapTopCatHelpEntry(); }
 
+void BoltEngine::swapGeorgeFrameArrayCb() { ((BoltEngine *)g_engine)->swapGeorgeFrameArray(); }
+void BoltEngine::swapGeorgeHelpEntryCb() { ((BoltEngine *)g_engine)->swapGeorgeHelpEntry(); }
+void BoltEngine::swapGeorgeThresholdsCb() { ((BoltEngine *)g_engine)->swapGeorgeThresholds(); }
+
 void BoltEngine::initCallbacks() {
 	// --- BOOTHS ---
 	for (int i = 0; i < ARRAYSIZE(g_defaultTypeLoadCallbacks); i++) {
@@ -745,6 +752,35 @@ void BoltEngine::initCallbacks() {
 	g_fredBoltCallbacks.groupLoadCallbacks = g_defaultGroupLoadCallbacks;
 	g_fredBoltCallbacks.groupFreeCallbacks = g_defaultGroupFreeCallbacks;
 
+	// --- GEORGE ---
+	for (int i = 0; i < ARRAYSIZE(g_georgeTypeLoadCallbacks); i++) {
+		g_georgeTypeLoadCallbacks[i] = noOpCb;
+	}
+
+	g_georgeTypeLoadCallbacks[2] = swapAllWordsCb;
+	g_georgeTypeLoadCallbacks[6] = resolveAllRefsCb;
+	g_georgeTypeLoadCallbacks[8] = swapSpriteHeaderCb;
+	g_georgeTypeLoadCallbacks[10] = swapPicHeaderCb;
+	g_georgeTypeLoadCallbacks[11] = swapAndResolvePicDescCb;
+	g_georgeTypeLoadCallbacks[12] = swapFirstTwoWordsCb;
+	g_georgeTypeLoadCallbacks[14] = swapFirstFourWordsCb;
+	g_georgeTypeLoadCallbacks[25] = swapGeorgeFrameArrayCb;
+	g_georgeTypeLoadCallbacks[26] = swapGeorgeHelpEntryCb;
+	g_georgeTypeLoadCallbacks[27] = swapGeorgeThresholdsCb;
+
+	for (int i = 0; i < ARRAYSIZE(g_georgeTypeFreeCallbacks); i++) {
+		g_georgeTypeFreeCallbacks[i] = noOpCb;
+	}
+
+	g_georgeTypeFreeCallbacks[8] = freeSpriteCleanUpCb;
+
+	g_georgeBoltCallbacks.typeLoadCallbacks = g_georgeTypeLoadCallbacks;
+	g_georgeBoltCallbacks.typeFreeCallbacks = g_georgeTypeFreeCallbacks;
+	g_georgeBoltCallbacks.memberLoadCallbacks = g_defaultMemberLoadCallbacks;
+	g_georgeBoltCallbacks.memberFreeCallbacks = g_defaultMemberFreeCallbacks;
+	g_georgeBoltCallbacks.groupLoadCallbacks = g_defaultGroupLoadCallbacks;
+	g_georgeBoltCallbacks.groupFreeCallbacks = g_defaultGroupFreeCallbacks;
+
 	// --- SCOOBY ---
 	for (int i = 0; i < ARRAYSIZE(g_scoobyTypeLoadCallbacks); i++) {
 		g_scoobyTypeLoadCallbacks[i] = noOpCb;


Commit: 5a08cf4461ce4680db6f5a60782c43c323245545
    https://github.com/scummvm/scummvm/commit/5a08cf4461ce4680db6f5a60782c43c323245545
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-04-07T01:11:14+02:00

Commit Message:
BOLT: CARNIVAL: Implement Huck's minigame

Changed paths:
    engines/bolt/bolt.h
    engines/bolt/booths/huck.cpp
    engines/bolt/resource.cpp
    engines/bolt/utils.cpp


diff --git a/engines/bolt/bolt.h b/engines/bolt/bolt.h
index 6e4235aa41d..4b8875ca129 100644
--- a/engines/bolt/bolt.h
+++ b/engines/bolt/bolt.h
@@ -312,6 +312,7 @@ typedef struct SoundInfo {
 	}
 } SoundInfo;
 
+
 typedef struct FredEntityState {
 	uint16 flags;
 	int16 frameCountdown;
@@ -344,6 +345,38 @@ typedef struct FredEntityState {
 	}
 } FredEntityState;
 
+// HUCK GAME
+
+typedef struct HuckState {
+	int16 levelNumber;
+	int16 slotIndex[10];
+	int16 levelComplete;
+	int16 drawTable1[24];
+	int16 drawTable2[24];
+	int16 giftCount;
+	int16 hasCycle;
+	int16 selectionPending;
+	int16 selected1Slot;
+	int16 selected2Slot;
+	int16 selected1SpriteId;
+	int16 selected2SpriteId;
+
+	HuckState() {
+		levelNumber = 0;
+		memset(slotIndex, 0, sizeof(slotIndex));
+		levelComplete = 0;
+		memset(drawTable1, 0, sizeof(drawTable1));
+		memset(drawTable2, 0, sizeof(drawTable2));
+		giftCount = 0;
+		hasCycle = 0;
+		selectionPending = 0;
+		selected1Slot = 0;
+		selected2Slot = 0;
+		selected1SpriteId = 0;
+		selected2SpriteId = 0;
+	}
+} HuckState;
+
 // GEORGE GAME
 
 typedef struct GeorgeEntityState {
@@ -645,10 +678,10 @@ protected:
 
 	// Utils
 	void displayColors(byte *palette, int16 page, int16 flags);
-	void sub_11035();
+	byte getPixel(byte *sprite, int16 localX, int16 localY);
 	void boltPict2Pict(XPPicDesc *dest, byte *boltSprite);
 	void displayPic(byte *boltSprite, int16 xOff, int16 yOff, int16 page);
-	void sub_11131();
+	bool pointInRect(Common::Rect *rect, int16 x, int16 y);
 	const char *assetPath(const char *fileName);
 	void boltCycleToXPCycle(byte *srcData, XPCycleState *cycleDesc);
 	void unpackColors(int16 count, byte *packedColors);
@@ -725,7 +758,7 @@ protected:
 	BOLTMemberEntry *g_boltCurrentMemberEntry = nullptr;
 	int16 g_pendingFixupCount = 0;
 
-	// Game state
+	// Game levelNumber
 	bool initVRam(int16 poolSize);
 	void freeVRam();
 	bool vLoad(void *dest, const char *name);
@@ -1032,7 +1065,70 @@ protected:
 	int16 *g_georgeThresholds = nullptr;
 
 	// --- HUCK ---
-	int16 huckGame(int16 prevBooth) { return 0; }
+	void playSoundMapHuck(int16 memberId);
+	void waitSoundMapHuck();
+	void setHuckColors(int16 which);
+	void restoreHuckColors(int16 which);
+	void startHuckShuffleTimer();
+	bool intersectRect(const Common::Rect *a, const Common::Rect *b, Common::Rect *out);
+	void drawGift(int16 slot);
+	void drawHuckGifts();
+	void checkHuckLevelComplete();
+	bool initHuckDisplay();
+	bool loadHuckResources();
+	void unloadHuckResources();
+	bool initHuckLevel();
+	bool resumeHuckLevel();
+	bool initHuck();
+	void huckToggleBlinking(int16 *state, int16 which);
+	void huckUpdateHotSpots(int16 x, int16 y);
+	int16 findGift(int16 x, int16 y);
+	bool handleGiftSelect(int16 x, int16 y);
+	void huckHandleActionButton(int16 x, int16 y);
+	void giftSwap();
+	void resolveHuckSelection();
+	void handleEvent(int16 eventType, uint32 eventData);
+	void playHuck();
+	void cleanUpHuck();
+	int16 huckGame(int16 prevBooth);
+	void swapHuckWordArray();
+	void swapHuckWords();
+
+	static void swapHuckWordArrayCb();
+	static void swapHuckWordsCb();
+
+	BOLTLib *g_huckBoltLib = nullptr;
+	BOLTCallbacks g_huckBoltCallbacks;
+
+	static BOLTCallback g_huckTypeLoadCallbacks[27];
+	static BOLTCallback g_huckTypeFreeCallbacks[27];
+
+	int16 g_huckSoundPlaying = 0;
+	int16 g_huckHotSpotCount = 0;
+	int16 g_huckActionState = 0;
+	uint32 g_huckScreensaverTimer = 0;
+	uint32 g_huckBlinkTimer = 0;
+	int16 g_huckScreensaverFlag = 0;
+	int16 g_huckBlinkFlag = 0;
+	uint32 g_huckShuffleTimer = 0;
+	int16 g_huckGlobal[30] = { 0 };
+	HuckState g_huckState;
+	byte *g_huckGiftPic = nullptr;
+	byte *g_huckBgPic = nullptr;
+	int16 g_huckGiftGroupId = 0;
+	int16 g_huckVariantGroupId = 0;
+	byte *g_huckBgDisplayPic = nullptr;
+	int16 g_huckScrollOffset = 0;
+	int16 g_huckPalRange[8] = { 0 };
+	int16 g_returnBooth = 0;
+	int16 g_huckExitFlag = 0;
+	int16 g_huckCursorX = 0;
+	int16 g_huckCursorY = 0;
+	byte g_huckPalSave0[15] = { 0 };
+	byte g_huckPalHighlight0[15] = { 0 };
+	byte g_huckPalSave1[15] = { 0 };
+	byte g_huckPalHighlight1[15] = { 0 };
+	XPPicDesc g_huckScratchPic;
 
 	// --- SCOOBY ---
 	bool loadScoobyBaseAssets();
diff --git a/engines/bolt/booths/huck.cpp b/engines/bolt/booths/huck.cpp
index d0e5b38e0bf..c137e694aaf 100644
--- a/engines/bolt/booths/huck.cpp
+++ b/engines/bolt/booths/huck.cpp
@@ -21,8 +21,915 @@
 
 #include "bolt/bolt.h"
 
+#include "common/memstream.h"
+
 namespace Bolt {
 
+void BoltEngine::playSoundMapHuck(int16 memberId) {
+	byte *soundData = getBOLTMember(g_huckBoltLib, memberId);
+	uint32 soundSize = memberSize(g_huckBoltLib, memberId);
+	if (soundData) {
+		_xp->playSound(soundData, soundSize, 22050);
+		g_huckSoundPlaying++;
+	}
+}
+
+void BoltEngine::waitSoundMapHuck() {
+	if (!g_huckSoundPlaying)
+		return;
+
+	uint32 dummy;
+	while (_xp->getEvent(etSound, &dummy) == etEmpty);
+	g_huckSoundPlaying--;
+}
+
+void BoltEngine::setHuckColors(int16 which) {
+	if (which == 0) {
+		int16 count = g_huckPalRange[1] - g_huckPalRange[0] + 1;
+		_xp->setPalette(count, g_huckPalRange[0], g_huckPalHighlight0);
+	} else if (which == 1) {
+		int16 count = g_huckPalRange[5] - g_huckPalRange[4] + 1;
+		_xp->setPalette(count, g_huckPalRange[4], g_huckPalHighlight1);
+	}
+}
+
+void BoltEngine::restoreHuckColors(int16 which) {
+	if (which == 0) {
+		int16 count = g_huckPalRange[1] - g_huckPalRange[0] + 1;
+		_xp->setPalette(count, g_huckPalRange[0], g_huckPalSave0);
+	} else if (which == 1) {
+		int16 count = g_huckPalRange[5] - g_huckPalRange[4] + 1;
+		_xp->setPalette(count, g_huckPalRange[4], g_huckPalSave1);
+	}
+}
+
+void BoltEngine::startHuckShuffleTimer() {
+	if (g_huckShuffleTimer) {
+		_xp->killTimer(g_huckShuffleTimer);
+		g_huckShuffleTimer = 0;
+	}
+
+	if (g_huckState.giftCount > 2) {
+		int16 speed = READ_UINT16(g_huckGiftPic + g_huckScrollOffset * 2 + 0x6E);
+		int32 ms = (int32)speed * 1000 / 60;
+		g_huckShuffleTimer = _xp->startTimer((int16)ms);
+	}
+}
+
+bool BoltEngine::intersectRect(const Common::Rect *a, const Common::Rect *b, Common::Rect *out) {
+	*out = a->findIntersectingRect(*b);
+	return !out->isEmpty();
+}
+
+void BoltEngine::drawGift(int16 slot) {
+	byte *giftSprite = memberAddr(g_huckBoltLib, g_huckState.drawTable1[slot]);
+	int16 sprStride = READ_UINT16(giftSprite + 0x0A);
+	int16 sprHeight = READ_UINT16(giftSprite + 0x0C);
+
+	memset(g_huckScratchPic.pixelData, 0, (int32)sprStride * sprHeight);
+
+	int16 slotX = READ_UINT16(g_huckGiftPic + slot * 2 + 0x3E);
+	int16 slotY = READ_UINT16(g_huckGiftPic + slot * 2 + 0x0E);
+	Common::Rect targetRect(slotX, slotY, slotX + sprHeight, slotY + sprStride);
+
+	int16 giftCount = READ_UINT16(g_huckGiftPic);
+	for (int16 i = 0; i < giftCount; i++) {
+		if (g_huckState.drawTable2[i] != 0)
+			continue;
+
+		byte *otherSprite = memberAddr(g_huckBoltLib, g_huckState.drawTable1[i]);
+		int16 otherStride = READ_UINT16(otherSprite + 0x0A);
+		int16 otherHeight = READ_UINT16(otherSprite + 0x0C);
+		int16 ox = READ_UINT16(g_huckGiftPic + i * 2 + 0x3E);
+		int16 oy = READ_UINT16(g_huckGiftPic + i * 2 + 0x0E);
+		Common::Rect otherRect(ox, oy, ox + otherHeight, oy + otherStride);
+
+		Common::Rect isect;
+		if (!intersectRect(&targetRect, &otherRect, &isect))
+			continue;
+
+		byte *srcPixels = getResolvedPtr(otherSprite, 0x12);
+		byte *src = srcPixels + (isect.left - ox) * otherStride + (isect.top - oy);
+
+		byte *dst = g_huckScratchPic.pixelData + (isect.left - slotX) * sprStride + (isect.top - slotY);
+
+		int16 blitWidth = isect.bottom - isect.top;
+		int16 blitHeight = isect.right - isect.left;
+		_xp->maskBlit(src, otherStride, dst, sprStride, blitWidth, blitHeight);
+	}
+
+	if ((g_huckState.hasCycle && g_huckState.selected1Slot == slot) || (g_huckState.selectionPending && g_huckState.selected2Slot == slot)) {
+		byte *hlSprite = memberAddr(g_huckBoltLib, READ_UINT16(g_huckGiftPic + 0x06));
+		int16 hlStride = READ_UINT16(hlSprite + 0x0A);
+		int16 hlHeight = READ_UINT16(hlSprite + 0x0C);
+		byte *hlPixels = getResolvedPtr(hlSprite, 0x12);
+		int16 hlOffsetX = READ_UINT16(g_huckGiftPic + 0x0A);
+		int16 hlOffsetY = READ_UINT16(g_huckGiftPic + 0x08);
+
+		byte *dst = g_huckScratchPic.pixelData + hlOffsetX * sprStride + hlOffsetY;
+
+		_xp->maskBlit(hlPixels, hlStride, dst, sprStride, hlStride, hlHeight);
+	}
+
+	g_huckScratchPic.width = sprStride;
+	g_huckScratchPic.height = sprHeight;
+	g_huckScratchPic.palette = nullptr;
+	g_huckScratchPic.flags = 0;
+
+	_xp->displayPic(&g_huckScratchPic, slotY, slotX, stFront);
+}
+
+void BoltEngine::drawHuckGifts() {
+	_xp->fillDisplay(0, stFront);
+
+	int16 giftCount = READ_UINT16(g_huckGiftPic);
+
+	for (int16 slot = 0; slot < giftCount; slot++) {
+		if (g_huckState.drawTable2[slot] != 0)
+			continue;
+
+		byte *gifPtr = memberAddr(g_huckBoltLib, g_huckState.drawTable1[slot]);
+		int16 x = READ_UINT16(g_huckGiftPic + slot * 2 + 0x0E);
+		int16 y = READ_UINT16(g_huckGiftPic + slot * 2 + 0x3E);
+		displayPic(gifPtr, x, y, stFront);
+	}
+
+	if (g_huckState.hasCycle) {
+		int16 x = READ_UINT16(g_huckGiftPic + g_huckState.selected1Slot * 2 + 0x0E) + READ_UINT16(g_huckGiftPic + 0x08);
+		int16 y = READ_UINT16(g_huckGiftPic + g_huckState.selected1Slot * 2 + 0x3E) + READ_UINT16(g_huckGiftPic + 0x0A);
+		int16 hlMember = READ_UINT16(g_huckGiftPic + 0x06);
+		byte *hlPtr = memberAddr(g_huckBoltLib, hlMember);
+		displayPic(hlPtr, x, y, stFront);
+	}
+
+	if (g_huckState.selectionPending) {
+		int16 x = READ_UINT16(g_huckGiftPic + g_huckState.selected2Slot * 2 + 0x0E) + READ_UINT16(g_huckGiftPic + 0x08);
+		int16 y = READ_UINT16(g_huckGiftPic + g_huckState.selected2Slot * 2 + 0x3E) + READ_UINT16(g_huckGiftPic + 0x0A);
+		int16 hlMember = READ_UINT16(g_huckGiftPic + 0x06);
+		byte *hlPtr = memberAddr(g_huckBoltLib, hlMember);
+		displayPic(hlPtr, x, y, stFront);
+	}
+}
+
+void BoltEngine::checkHuckLevelComplete() {
+	if (g_huckState.giftCount == 2) {
+		// Last pair matched, level complete!
+		g_huckExitFlag = 1;
+		g_huckState.levelComplete = 1;
+		g_huckState.levelNumber++;
+
+		if (g_huckState.levelNumber > 10)
+			g_huckState.levelNumber = 10;
+
+		_xp->fillDisplay(0, stFront);
+	} else {
+		// More pairs remain, redraw the matched slots as empty...
+		drawGift(g_huckState.selected1Slot);
+		drawGift(g_huckState.selected2Slot);
+	}
+
+	g_huckState.selected1Slot = -1;
+	g_huckState.selected2Slot = -1;
+	g_huckState.giftCount -= 2;
+
+	_xp->updateDisplay();
+
+	waitSoundMapHuck();
+	g_huckScrollOffset++;
+	startHuckShuffleTimer();
+}
+
+bool BoltEngine::initHuckDisplay() {
+	byte *palPtr = memberAddr(g_huckBoltLib, READ_UINT16(g_huckBgPic));
+
+	g_huckHotSpotCount = 0;
+	g_huckScreensaverTimer = 0;
+	g_huckBlinkTimer = 0;
+	g_huckExitFlag = 0;
+
+	int32 maxArea = 0;
+	int16 giftCount = READ_UINT16(g_huckGiftPic);
+	for (int16 i = 0; i < giftCount; i++) {
+		byte *spr = memberAddr(g_huckBoltLib, g_huckState.drawTable1[i]);
+		int32 area = (int32)READ_UINT16(spr + 0x0A) * READ_UINT16(spr + 0x0C);
+		if (area > maxArea)
+			maxArea = area;
+	}
+
+	g_huckScratchPic.pixelData = (byte *)_xp->allocMem(maxArea);
+	if (!g_huckScratchPic.pixelData)
+		return false;
+
+	uint32 dummy;
+	while (_xp->getEvent(etTimer, &dummy) != etTimer);
+
+	_xp->stopCycle();
+	_xp->setTransparency(false);
+	displayColors(palPtr, stBack, 1);
+	displayPic(g_huckBgDisplayPic, g_displayX, g_displayY, stFront);
+	_xp->updateDisplay();
+
+	_xp->setTransparency(true);
+	displayColors(palPtr, stFront, 0);
+	displayColors(palPtr, stBack, 1);
+	displayPic(g_huckBgDisplayPic, g_displayX, g_displayY, stBack);
+	_xp->fillDisplay(0, stFront);
+	_xp->updateDisplay();
+
+	drawHuckGifts();
+	_xp->updateDisplay();
+
+	if (g_huckState.hasCycle) {
+		XPCycleState cycleSpec[4];
+		byte *cycleData = memberAddr(g_huckBoltLib, READ_UINT16(g_huckGiftPic + 0x0C));
+		boltCycleToXPCycle(cycleData, cycleSpec);
+		_xp->startCycle(cycleSpec);
+	}
+
+	g_huckCursorY = 0x78;
+	g_huckCursorX = 0xC0;
+	_xp->setCursorPos(0x78, 0xC0);
+	_xp->setCursorColor(0xFF, 0xFF, 0xFF);
+	_xp->showCursor();
+
+	// Read palette ranges from gift pic data...
+	g_huckPalRange[0] = READ_UINT16(g_huckGiftPic + 0x8C) + 0x80;
+	g_huckPalRange[1] = READ_UINT16(g_huckGiftPic + 0x8E) + 0x80;
+	g_huckPalRange[2] = READ_UINT16(g_huckGiftPic + 0x90) + 0x80;
+	g_huckPalRange[3] = READ_UINT16(g_huckGiftPic + 0x92) + 0x80;
+	g_huckPalRange[4] = READ_UINT16(g_huckGiftPic + 0x9E) + 0x80;
+	g_huckPalRange[5] = READ_UINT16(g_huckGiftPic + 0xA0) + 0x80;
+	g_huckPalRange[6] = READ_UINT16(g_huckGiftPic + 0xA2) + 0x80;
+	g_huckPalRange[7] = READ_UINT16(g_huckGiftPic + 0xA4) + 0x80;
+
+	// Save original palette ranges, load highlight palette ranges...
+	_xp->getPalette(g_huckPalRange[0], g_huckPalRange[1] - g_huckPalRange[0] + 1, g_huckPalSave0);
+	_xp->getPalette(g_huckPalRange[2], g_huckPalRange[3] - g_huckPalRange[2] + 1, g_huckPalHighlight0);
+	_xp->getPalette(g_huckPalRange[4], g_huckPalRange[5] - g_huckPalRange[4] + 1, g_huckPalSave1);
+	_xp->getPalette(g_huckPalRange[6], g_huckPalRange[7] - g_huckPalRange[6] + 1, g_huckPalHighlight1);
+
+	return true;
+}
+
+bool BoltEngine::loadHuckResources() {
+	int16 stateIdx = g_huckState.levelNumber - 1;
+	int16 giftGroupId = (stateIdx << 10) + 0x100;
+	g_huckGiftGroupId = giftGroupId;
+
+	if (!getBOLTGroup(g_huckBoltLib, giftGroupId, 1))
+		return false;
+
+	g_huckGiftPic = memberAddr(g_huckBoltLib, giftGroupId);
+
+	for (int16 i = 0; i < 11; i++) {
+		int16 *speed = (int16 *)(g_huckGiftPic + 0x6E + i * 2);
+		if (*speed < 30)
+			*speed = 30;
+	}
+
+	// Load background display pic...
+	int16 bgMember = (g_displayMode != 0) ? READ_UINT16(g_huckGiftPic + 4) : READ_UINT16(g_huckGiftPic + 2);
+	g_huckBgDisplayPic = memberAddr(g_huckBoltLib, bgMember);
+
+	// Determine variant...
+	int16 slot = g_huckState.slotIndex[stateIdx];
+	int16 variant = g_huckGlobal[stateIdx * 3 + slot];
+	int16 variantGroupId = ((stateIdx * 4 + variant + 1) << 8) + 0x100;
+	g_huckVariantGroupId = variantGroupId;
+
+	if (!getBOLTGroup(g_huckBoltLib, variantGroupId, 1))
+		return false;
+
+	g_huckBgPic = memberAddr(g_huckBoltLib, variantGroupId);
+
+	return true;
+}
+
+void BoltEngine::unloadHuckResources() {
+	_xp->stopCycle();
+	_xp->hideCursor();
+	freeBOLTGroup(g_huckBoltLib, g_huckVariantGroupId, 1);
+	freeBOLTGroup(g_huckBoltLib, g_huckGiftGroupId, 1);
+}
+
+bool BoltEngine::initHuckLevel() {
+	// Advance slot variant (wraps 0..2)
+	int16 stateIdx = g_huckState.levelNumber - 1;
+	g_huckState.slotIndex[stateIdx]++;
+	if (g_huckState.slotIndex[stateIdx] >= 3)
+		g_huckState.slotIndex[stateIdx] = 0;
+
+	if (!loadHuckResources())
+		return false;
+
+	g_huckState.giftCount = READ_UINT16(g_huckGiftPic);
+	g_huckState.levelComplete = 0;
+	g_huckState.hasCycle = 0;
+	g_huckState.selectionPending = 0;
+	g_huckState.selected1Slot = -1;
+	g_huckState.selected2Slot = -1;
+
+	for (int16 i = 0; i < 24; i++) {
+		g_huckState.drawTable1[i] = 0;
+		g_huckState.drawTable2[i] = 0;
+	}
+
+	int16 baseId = READ_UINT16(g_huckBgPic + 2);
+
+	// Distribute gift pairs into random empty slots...
+	int16 count = 0;
+	int16 giftCount = READ_UINT16(g_huckGiftPic);
+	while (count < giftCount) {
+		int16 slot = _xp->getRandom(giftCount);
+		while (g_huckState.drawTable1[slot] > 0) {
+			slot++;
+			if (slot >= giftCount)
+				slot = 0;
+		}
+
+		// Pairs share the same member ID
+		g_huckState.drawTable1[slot] = count / 2 + baseId;
+		count++;
+	}
+
+	if (!initHuckDisplay())
+		return false;
+
+	g_huckScrollOffset = 0;
+	startHuckShuffleTimer();
+	return true;
+}
+
+bool BoltEngine::resumeHuckLevel() {
+	if (!loadHuckResources())
+		return false;
+
+	if (!initHuckDisplay())
+		return false;
+
+	if (g_huckState.giftCount > 2) {
+		g_huckScrollOffset = (READ_UINT16(g_huckGiftPic) - g_huckState.giftCount) >> 1;
+		startHuckShuffleTimer();
+	}
+
+	return true;
+}
+
+bool BoltEngine::initHuck() {
+	_xp->randomize();
+	g_huckSoundPlaying = 0;
+
+	if (!vLoad(&g_huckGlobal, "HuckGlobal")) {
+		// First run, generate random variant permutations for all 10 levels...
+		for (int16 level = 0; level < 10; level++) {
+			g_huckState.slotIndex[level] = -1;
+
+			// Fill row with sentinel value 3...
+			for (int16 j = 0; j < 3; j++)
+				g_huckGlobal[level * 3 + j] = 3;
+
+			// Place variants 0, 1, 2 into random empty slots...
+			for (int16 variant = 0; variant < 3; variant++) {
+				int16 slot = _xp->getRandom(3);
+				while (g_huckGlobal[level * 3 + slot] != 3) {
+					slot++;
+					if (slot >= 3)
+						slot = 0;
+				}
+				g_huckGlobal[level * 3 + slot] = variant;
+			}
+		}
+	}
+
+	byte huckStateBuf[0x86] = { 0 };
+
+	if (!vLoad(&huckStateBuf, "Huck")) {
+		g_huckState.levelNumber = 1;
+		return initHuckLevel();
+	}
+
+	Common::SeekableReadStream *huckStateReadStream = new Common::MemoryReadStream(huckStateBuf, sizeof(huckStateBuf), DisposeAfterUse::NO);
+
+	g_huckState.levelNumber = huckStateReadStream->readSint16BE();
+
+	for (int i = 0; i < 10; i++)
+		g_huckState.slotIndex[i] = huckStateReadStream->readSint16BE();
+
+	g_huckState.levelComplete = huckStateReadStream->readSint16BE();
+
+	for (int i = 0; i < 24; i++)
+		g_huckState.drawTable1[i] = huckStateReadStream->readSint16BE();
+
+	for (int i = 0; i < 24; i++)
+		g_huckState.drawTable2[i] = huckStateReadStream->readSint16BE();
+
+	g_huckState.giftCount = huckStateReadStream->readSint16BE();
+	g_huckState.hasCycle = huckStateReadStream->readSint16BE();
+	g_huckState.selectionPending = huckStateReadStream->readSint16BE();
+	g_huckState.selected1Slot = huckStateReadStream->readSint16BE();
+	g_huckState.selected2Slot = huckStateReadStream->readSint16BE();
+	g_huckState.selected1SpriteId = huckStateReadStream->readSint16BE();
+	g_huckState.selected2SpriteId = huckStateReadStream->readSint16BE();
+
+	// Sanity check: should be exactly 0x86
+	assert(huckStateReadStream->pos() == 0x86);
+	delete huckStateReadStream;
+
+	if (g_huckState.levelComplete)
+		return initHuckLevel();
+	else
+		return resumeHuckLevel();
+}
+
+void BoltEngine::huckToggleBlinking(int16 *state, int16 which) {
+	*state = (*state == 0) ? 1 : 0;
+	if (*state)
+		setHuckColors(which);
+	else
+		restoreHuckColors(which);
+}
+
+void BoltEngine::huckUpdateHotSpots(int16 x, int16 y) {
+	Common::Rect helpRect(
+		READ_UINT16(g_huckGiftPic + 0x84), READ_UINT16(g_huckGiftPic + 0x88),
+		READ_UINT16(g_huckGiftPic + 0x86), READ_UINT16(g_huckGiftPic + 0x8A));
+
+	Common::Rect exitRect(
+		READ_UINT16(g_huckGiftPic + 0x96), READ_UINT16(g_huckGiftPic + 0x9A),
+		READ_UINT16(g_huckGiftPic + 0x98), READ_UINT16(g_huckGiftPic + 0x9C));
+
+	if (helpRect.contains(x, y)) {
+		if (!g_huckScreensaverTimer && !g_huckHotSpotCount)
+			setHuckColors(0);
+	} else {
+		if (!g_huckScreensaverTimer && !g_huckHotSpotCount)
+			restoreHuckColors(0);
+	}
+
+	if (exitRect.contains(x, y)) {
+		if (!g_huckBlinkTimer)
+			setHuckColors(1);
+
+		return;
+	} else {
+		if (!g_huckBlinkTimer)
+			restoreHuckColors(1);
+	}
+}
+
+int16 BoltEngine::findGift(int16 x, int16 y) {
+	byte *sprite0 = memberAddr(g_huckBoltLib, g_huckState.drawTable1[0]);
+	int16 sprH = READ_UINT16(sprite0 + 0x0A);
+	int16 sprW = READ_UINT16(sprite0 + 0x0C);
+
+	int16 giftCount = READ_UINT16(g_huckGiftPic);
+
+	// Iterate in reverse, topmost gift wins...
+	for (int16 slot = giftCount - 1; slot >= 0; slot--) {
+		if (g_huckState.drawTable2[slot] != 0)
+			continue;
+
+		int16 slotY = READ_UINT16(g_huckGiftPic + slot * 2 + 0x3E);
+		int16 slotX = READ_UINT16(g_huckGiftPic + slot * 2 + 0x0E);
+		int16 slotY2 = slotY + sprH - 1;
+		int16 slotX2 = slotX + sprW - 1;
+
+		byte *sprite = memberAddr(g_huckBoltLib, g_huckState.drawTable1[slot]);
+
+		if (y < slotY || y > slotY2)
+			continue;
+		if (x < slotX || x > slotX2)
+			continue;
+
+		// Non-transparent pixel hit test...
+		if (getPixel(sprite, x - slotX, y - slotY) == 0)
+			continue;
+
+		return slot;
+	}
+	return -1;
+}
+
+bool BoltEngine::handleGiftSelect(int16 x, int16 y) {
+	int16 slot = findGift(x, y);
+	if (slot == -1)
+		return false;
+
+	if (!g_huckState.hasCycle) {
+		// No selection yet, select first gift...
+		g_huckState.hasCycle = 1;
+		g_huckState.selected1SpriteId = g_huckState.drawTable1[slot];
+		g_huckState.selected1Slot = slot;
+		playSoundMapHuck((int16)READ_UINT16(g_huckGiftPic + 0xA8));
+		drawGift(slot);
+		_xp->updateDisplay();
+
+		// Start palette cycle for highlight...
+		XPCycleState cycleSpec[4];
+		byte *cycleData = memberAddr(g_huckBoltLib, (int16)READ_UINT16(g_huckGiftPic + 0x0C));
+		boltCycleToXPCycle(cycleData, cycleSpec);
+		_xp->startCycle(cycleSpec);
+
+	} else if (!g_huckState.selectionPending) {
+		if (g_huckState.selected1Slot == slot) {
+			// Same gift clicked again, deselect...
+			g_huckState.hasCycle = 0;
+			g_huckState.selected1Slot = -1;
+			playSoundMapHuck((int16)READ_UINT16(g_huckGiftPic + 0xAA));
+			drawGift(slot);
+			_xp->updateDisplay();
+			_xp->stopCycle();
+		} else {
+			// Different gift, select second...
+			g_huckState.selectionPending = 1;
+			g_huckState.selected2SpriteId = g_huckState.drawTable1[slot];
+			g_huckState.selected2Slot = slot;
+			playSoundMapHuck((int16)READ_UINT16(g_huckGiftPic + 0xAC));
+			drawGift(slot);
+			_xp->updateDisplay();
+		}
+	}
+
+	// else: already have two selections pending, ignore click...
+
+	waitSoundMapHuck();
+	startHuckShuffleTimer();
+	return true;
+}
+
+void BoltEngine::huckHandleActionButton(int16 x, int16 y) {
+	bool var_6 = false;
+	bool var_4 = false;
+
+	if (g_huckHotSpotCount != 0) {
+		g_huckHotSpotCount = 0;
+		var_6 = true;
+		stopAnimation();
+
+		if (g_huckBlinkTimer) {
+			_xp->killTimer(g_huckBlinkTimer);
+			g_huckBlinkTimer = 0;
+		}
+
+		huckUpdateHotSpots(g_huckCursorX, g_huckCursorY);
+	}
+
+	Common::Rect helpRect(
+		READ_UINT16(g_huckGiftPic + 0x84), READ_UINT16(g_huckGiftPic + 0x88),
+		READ_UINT16(g_huckGiftPic + 0x86), READ_UINT16(g_huckGiftPic + 0x8A));
+
+	if (helpRect.contains(x, y)) {
+		var_4 = true;
+		if (g_huckScreensaverTimer) {
+			_xp->killTimer(g_huckScreensaverTimer);
+			g_huckScreensaverTimer = 0;
+			setHuckColors(0);
+		}
+
+		if (!g_huckHotSpotCount && !var_6) {
+			g_huckHotSpotCount = startAnimation(g_rtfHandle, 0x1A);
+			g_huckActionState = 0;
+		}
+	} else {
+		Common::Rect exitRect(
+			READ_UINT16(g_huckGiftPic + 0x96), READ_UINT16(g_huckGiftPic + 0x9A),
+			READ_UINT16(g_huckGiftPic + 0x98), READ_UINT16(g_huckGiftPic + 0x9C));
+
+		if (exitRect.contains(x, y)) {
+			setHuckColors(1);
+			restoreHuckColors(0);
+			g_returnBooth = 3;
+			g_huckExitFlag = 1;
+		}
+	}
+
+	if (handleGiftSelect(x, y))
+		var_4 = true;
+
+	if (var_4) {
+		if (g_huckScreensaverTimer) {
+			_xp->killTimer(g_huckScreensaverTimer);
+			g_huckScreensaverTimer = 0;
+			restoreHuckColors(0);
+		}
+
+		_xp->setInactivityTimer(30);
+	}
+}
+
+void BoltEngine::giftSwap() {
+	if (g_huckState.giftCount <= 2)
+		return;
+
+	// Determine pool size excluding selected gifts...
+	int16 pool;
+	if (g_huckState.selectionPending)
+		pool = g_huckState.giftCount - 2;
+	else if (g_huckState.hasCycle)
+		pool = g_huckState.giftCount - 1;
+	else
+		pool = g_huckState.giftCount;
+
+	// Pick two distinct random indices into the eligible pool...
+	int16 randA = _xp->getRandom(pool);
+	int16 randB = _xp->getRandom(pool);
+	if (randA == randB) {
+		randB++;
+		if (randB >= pool)
+			randB = 0;
+	}
+
+	// Map random indices to actual slots, skipping matched/selected...
+	int16 slotA = -1;
+	int16 slotB = 0;
+	int16 counter = -1;
+	for (int16 i = 0, j = 0; j <= READ_UINT16(g_huckGiftPic); i++, j++) {
+		if (g_huckState.drawTable2[j] != 0)
+			continue;
+
+		if (j == g_huckState.selected1Slot)
+			continue;
+
+		if (j == g_huckState.selected2Slot)
+			continue;
+
+		counter++;
+
+		if (counter == randA)
+			slotA = j;
+
+		if (counter == randB)
+			slotB = j;
+	}
+
+	// If the two chosen slots have the same sprite, find a different slotB...
+	if (g_huckState.drawTable1[slotA] == g_huckState.drawTable1[slotB]) {
+		// Walk forward from slotB, skipping invalid candidates...
+		while (true) {
+			slotB++;
+			if (slotB >= READ_UINT16(g_huckGiftPic))
+				slotB = 0;
+
+			if (g_huckState.drawTable2[slotB] != 0)
+				continue;
+
+			if (slotB == g_huckState.selected2Slot)
+				continue;
+
+			if (slotB == g_huckState.selected1Slot)
+				continue;
+
+			if (slotB == slotA)
+				continue;
+
+			break;
+		}
+	}
+
+	// Swap entries in draw table...
+	int16 tmp = g_huckState.drawTable1[slotA];
+	g_huckState.drawTable1[slotA] = g_huckState.drawTable1[slotB];
+	g_huckState.drawTable1[slotB] = tmp;
+
+	drawGift(slotA);
+	drawGift(slotB);
+}
+
+void BoltEngine::resolveHuckSelection() {
+	if (!g_huckState.selectionPending)
+		return;
+
+	_xp->stopCycle();
+
+	if (g_huckState.selected1SpriteId == g_huckState.selected2SpriteId) {
+		// Match, play match sound, mark both slots as matched...
+		playSoundMapHuck((int16)READ_UINT16(g_huckGiftPic + 0xAE));
+		g_huckState.drawTable2[g_huckState.selected1Slot] = 1;
+		g_huckState.drawTable2[g_huckState.selected2Slot] = 1;
+		g_huckState.hasCycle = 0;
+		g_huckState.selectionPending = 0;
+		checkHuckLevelComplete();
+	} else {
+		// Mismatch, play mismatch sound, redraw both gifts unselected...
+		playSoundMapHuck((int16)READ_UINT16(g_huckGiftPic + 0xB0));
+		g_huckState.hasCycle = 0;
+		g_huckState.selectionPending = 0;
+		drawGift(g_huckState.selected1Slot);
+		drawGift(g_huckState.selected2Slot);
+		g_huckState.selected1Slot = -1;
+		g_huckState.selected2Slot = -1;
+		waitSoundMapHuck();
+	}
+}
+
+void BoltEngine::handleEvent(int16 eventType, uint32 eventData) {
+	switch (eventType) {
+	case etMouseDown:
+		huckHandleActionButton(g_huckCursorX, g_huckCursorY);
+		resolveHuckSelection();
+
+		// The original does:
+		// if (g_huckExitFlag)
+		// 	return;
+		//
+		// and then breaks, which is redundant...
+
+		break;
+
+	case etMouseMove:
+		g_huckCursorX = (int16)(eventData >> 16);
+		g_huckCursorY = (int16)(eventData & -1);
+		huckUpdateHotSpots(g_huckCursorX, g_huckCursorY);
+		break;
+
+	case etTimer: {
+		if (eventData == g_huckScreensaverTimer) {
+			huckToggleBlinking(&g_huckScreensaverFlag, 0);
+			int16 ms = (int16)((int32)READ_UINT16(g_huckGiftPic + 0x94) * 1000 / 60);
+			g_huckScreensaverTimer = _xp->startTimer(ms);
+		} else if (eventData == g_huckBlinkTimer) {
+			huckToggleBlinking(&g_huckBlinkFlag, 1);
+			int16 ms = (int16)((int32)READ_UINT16(g_huckGiftPic + 0x94) * 1000 / 60);
+			g_huckBlinkTimer = _xp->startTimer(ms);
+		} else if (eventData == g_huckShuffleTimer) {
+			giftSwap();
+			_xp->updateDisplay();
+			startHuckShuffleTimer();
+		}
+
+		break;
+	}
+
+	case etInactivity: {
+		if (!g_huckScreensaverTimer) {
+			int16 ms = (int16)((int32)READ_UINT16(g_huckGiftPic + 0x94) * 1000 / 60);
+			g_huckScreensaverTimer = _xp->startTimer(ms);
+			g_huckScreensaverFlag = 0;
+			huckToggleBlinking(&g_huckScreensaverFlag, 0);
+			_xp->setInactivityTimer(1800);
+		} else {
+			bool exitLoop = false;
+			_xp->setScreenBrightness(25);
+
+			while (!shouldQuit()) {
+				uint32 innerData = 0;
+				int16 innerType = _xp->getEvent(etEmpty, &innerData);
+				switch (innerType) {
+				case etSound:
+					if (g_huckSoundPlaying > 0)
+						g_huckSoundPlaying--;
+
+					break;
+				default:
+					_xp->setScreenBrightness(100);
+					_xp->setInactivityTimer(1800);
+					exitLoop = true;
+					break;
+				}
+
+				handleEvent(innerType, innerData);
+				if (exitLoop)
+					break;
+			}
+		}
+	}
+
+	case etSound: {
+		if (g_huckHotSpotCount != 0) {
+			g_huckHotSpotCount = maintainAudioPlay(1);
+			if (g_huckHotSpotCount == 0)
+				huckUpdateHotSpots(g_huckCursorX, g_huckCursorY);
+		}
+
+		if (g_huckSoundPlaying > 0)
+			g_huckSoundPlaying--;
+
+		break;
+	}
+
+	case etTrigger: {
+		g_huckActionState++;
+		if (g_huckActionState == 1) {
+			int16 ms = (int16)((int32)READ_UINT16(g_huckGiftPic + 0x94) * 1000 / 60);
+			g_huckBlinkTimer = _xp->startTimer(ms);
+			g_huckBlinkFlag = 0;
+			huckToggleBlinking(&g_huckBlinkFlag, 1);
+		} else if (g_huckActionState == 2) {
+			_xp->killTimer(g_huckBlinkTimer);
+			g_huckBlinkTimer = 0;
+			restoreHuckColors(1);
+		}
+
+		break;
+	}
+
+	default:
+		break;
+	}
+}
+
+void BoltEngine::playHuck() {
+	g_returnBooth = 0x10;
+
+	while (!shouldQuit()) {
+		if (g_huckExitFlag)
+			break;
+
+		uint32 eventData = 0;
+		int16 eventType = _xp->getEvent(etEmpty, &eventData);
+		if (eventType)
+			handleEvent(eventType, eventData);
+
+		if (g_huckHotSpotCount != 0) {
+			g_huckHotSpotCount = maintainAudioPlay(0);
+			if (g_huckHotSpotCount == 0)
+				huckUpdateHotSpots(g_huckCursorX, g_huckCursorY);
+		}
+	}
+
+	if (shouldQuit())
+		g_returnBooth = 0;
+}
+
+void BoltEngine::cleanUpHuck() {
+	unloadHuckResources();
+
+	if (g_huckScratchPic.pixelData) {
+		_xp->freeMem(g_huckScratchPic.pixelData);
+		g_huckScratchPic.pixelData = nullptr;
+		g_huckScratchPic.palette = nullptr;
+	}
+
+	vSave(&g_huckGlobal, sizeof(g_huckGlobal), "HuckGlobal");
+
+	byte huckSaveBuf[0x86] = { 0 };
+	Common::MemoryWriteStream *huckStateWriteStream = new Common::MemoryWriteStream(huckSaveBuf, sizeof(huckSaveBuf));
+
+	huckStateWriteStream->writeSint16BE(g_huckState.levelNumber);
+
+	for (int i = 0; i < 10; i++)
+		huckStateWriteStream->writeSint16BE(g_huckState.slotIndex[i]);
+
+	huckStateWriteStream->writeSint16BE(g_huckState.levelComplete);
+
+	for (int i = 0; i < 24; i++)
+		huckStateWriteStream->writeSint16BE(g_huckState.drawTable1[i]);
+
+	for (int i = 0; i < 24; i++)
+		huckStateWriteStream->writeSint16BE(g_huckState.drawTable2[i]);
+
+	huckStateWriteStream->writeSint16BE(g_huckState.giftCount);
+	huckStateWriteStream->writeSint16BE(g_huckState.hasCycle);
+	huckStateWriteStream->writeSint16BE(g_huckState.selectionPending);
+	huckStateWriteStream->writeSint16BE(g_huckState.selected1Slot);
+	huckStateWriteStream->writeSint16BE(g_huckState.selected2Slot);
+	huckStateWriteStream->writeSint16BE(g_huckState.selected1SpriteId);
+	huckStateWriteStream->writeSint16BE(g_huckState.selected2SpriteId);
+
+	// Sanity check: should be exactly 0x86
+	assert(huckStateWriteStream->pos() == 0x86);
+	delete huckStateWriteStream;
+
+	vSave(&huckSaveBuf, 0x86, "Huck");
+
+	_xp->fillDisplay(0, stFront);
+	_xp->updateDisplay();
+}
+
+int16 BoltEngine::huckGame(int16 prevBooth) {
+	if (!openBOLTLib(&g_huckBoltLib, &g_huckBoltCallbacks, assetPath("huck.blt")))
+		return g_returnBooth;
+
+	int16 savedTimer = _xp->setInactivityTimer(30);
+
+	if (initHuck())
+		playHuck();
+
+	cleanUpHuck();
+
+	_xp->setInactivityTimer(savedTimer);
+	closeBOLTLib(&g_huckBoltLib);
+
+	return g_returnBooth;
+}
+
+void BoltEngine::swapHuckWordArray() {
+	byte *ptr = g_boltCurrentMemberEntry->dataPtr;
+	if (!ptr)
+		return;
+
+	int16 count = (int16)(g_boltCurrentMemberEntry->decompSize / 2);
+	for (int16 i = 0; i < count; i++, ptr += 2)
+		WRITE_UINT16(ptr, READ_BE_UINT16(ptr));
+}
+
+void BoltEngine::swapHuckWords() {
+	byte *ptr = g_boltCurrentMemberEntry->dataPtr;
+	if (!ptr)
+		return;
 
+	WRITE_UINT16(ptr, READ_BE_UINT16(ptr));
+	WRITE_UINT16(ptr + 2, READ_BE_UINT16(ptr + 2));
+}
 
 } // End of namespace Bolt
diff --git a/engines/bolt/resource.cpp b/engines/bolt/resource.cpp
index 336493a9a6a..0127978dd46 100644
--- a/engines/bolt/resource.cpp
+++ b/engines/bolt/resource.cpp
@@ -650,6 +650,9 @@ BOLTCallback BoltEngine::g_fredTypeFreeCallbacks[28];
 BOLTCallback BoltEngine::g_georgeTypeLoadCallbacks[28];
 BOLTCallback BoltEngine::g_georgeTypeFreeCallbacks[28];
 
+BOLTCallback BoltEngine::g_huckTypeLoadCallbacks[27];
+BOLTCallback BoltEngine::g_huckTypeFreeCallbacks[27];
+
 BOLTCallback BoltEngine::g_scoobyTypeLoadCallbacks[28];
 BOLTCallback BoltEngine::g_scoobyTypeFreeCallbacks[28];
 
@@ -672,15 +675,18 @@ void BoltEngine::swapFredAnimEntryCb() { ((BoltEngine *)g_engine)->swapFredAnimE
 void BoltEngine::swapFredAnimDescCb() { ((BoltEngine *)g_engine)->swapFredAnimDesc(); }
 void BoltEngine::swapFredLevelDescCb() { ((BoltEngine *)g_engine)->swapFredLevelDesc(); }
 
+void BoltEngine::swapGeorgeFrameArrayCb() { ((BoltEngine *)g_engine)->swapGeorgeFrameArray(); }
+void BoltEngine::swapGeorgeHelpEntryCb() { ((BoltEngine *)g_engine)->swapGeorgeHelpEntry(); }
+void BoltEngine::swapGeorgeThresholdsCb() { ((BoltEngine *)g_engine)->swapGeorgeThresholds(); }
+
+void BoltEngine::swapHuckWordArrayCb() { ((BoltEngine *)g_engine)->swapHuckWordArray(); }
+void BoltEngine::swapHuckWordsCb() { ((BoltEngine *)g_engine)->swapHuckWords(); }
+
 void BoltEngine::swapScoobyHelpEntryCb() { ((BoltEngine *)g_engine)->swapScoobyHelpEntry(); }
 void BoltEngine::swapScoobyWordArrayCb() { ((BoltEngine *)g_engine)->swapScoobyWordArray(); }
 
 void BoltEngine::swapTopCatHelpEntryCb() { ((BoltEngine *)g_engine)->swapTopCatHelpEntry(); }
 
-void BoltEngine::swapGeorgeFrameArrayCb() { ((BoltEngine *)g_engine)->swapGeorgeFrameArray(); }
-void BoltEngine::swapGeorgeHelpEntryCb() { ((BoltEngine *)g_engine)->swapGeorgeHelpEntry(); }
-void BoltEngine::swapGeorgeThresholdsCb() { ((BoltEngine *)g_engine)->swapGeorgeThresholds(); }
-
 void BoltEngine::initCallbacks() {
 	// --- BOOTHS ---
 	for (int i = 0; i < ARRAYSIZE(g_defaultTypeLoadCallbacks); i++) {
@@ -781,6 +787,34 @@ void BoltEngine::initCallbacks() {
 	g_georgeBoltCallbacks.groupLoadCallbacks = g_defaultGroupLoadCallbacks;
 	g_georgeBoltCallbacks.groupFreeCallbacks = g_defaultGroupFreeCallbacks;
 
+	// --- HUCK ---
+	for (int i = 0; i < ARRAYSIZE(g_huckTypeLoadCallbacks); i++) {
+		g_huckTypeLoadCallbacks[i] = noOpCb;
+	}
+
+	g_huckTypeLoadCallbacks[2] = swapAllWordsCb;
+	g_huckTypeLoadCallbacks[6] = resolveAllRefsCb;
+	g_huckTypeLoadCallbacks[8] = swapSpriteHeaderCb;
+	g_huckTypeLoadCallbacks[10] = swapPicHeaderCb;
+	g_huckTypeLoadCallbacks[11] = swapAndResolvePicDescCb;
+	g_huckTypeLoadCallbacks[12] = swapFirstTwoWordsCb;
+	g_huckTypeLoadCallbacks[14] = swapFirstFourWordsCb;
+	g_huckTypeLoadCallbacks[25] = swapHuckWordArrayCb;
+	g_huckTypeLoadCallbacks[26] = swapHuckWordsCb;
+
+	for (int i = 0; i < ARRAYSIZE(g_huckTypeFreeCallbacks); i++) {
+		g_huckTypeFreeCallbacks[i] = noOpCb;
+	}
+
+	g_huckTypeFreeCallbacks[8] = freeSpriteCleanUpCb;
+
+	g_huckBoltCallbacks.typeLoadCallbacks = g_huckTypeLoadCallbacks;
+	g_huckBoltCallbacks.typeFreeCallbacks = g_huckTypeFreeCallbacks;
+	g_huckBoltCallbacks.memberLoadCallbacks = g_defaultMemberLoadCallbacks;
+	g_huckBoltCallbacks.memberFreeCallbacks = g_defaultMemberFreeCallbacks;
+	g_huckBoltCallbacks.groupLoadCallbacks = g_defaultGroupLoadCallbacks;
+	g_huckBoltCallbacks.groupFreeCallbacks = g_defaultGroupFreeCallbacks;
+
 	// --- SCOOBY ---
 	for (int i = 0; i < ARRAYSIZE(g_scoobyTypeLoadCallbacks); i++) {
 		g_scoobyTypeLoadCallbacks[i] = noOpCb;
diff --git a/engines/bolt/utils.cpp b/engines/bolt/utils.cpp
index 67df914881a..b2a3c82ebcf 100644
--- a/engines/bolt/utils.cpp
+++ b/engines/bolt/utils.cpp
@@ -62,7 +62,10 @@ void BoltEngine::displayColors(byte *palette, int16 page, int16 flags) {
 	_xp->displayPic(&picDesc, 0, 0, page);
 }
 
-void BoltEngine::sub_11035() {
+byte BoltEngine::getPixel(byte *sprite, int16 localX, int16 localY) {
+	int16 sprH = READ_UINT16(sprite + 0x0A);
+	byte *pixels = getResolvedPtr(sprite, 0x12);
+	return pixels[localX * sprH + localY];
 }
 
 void BoltEngine::boltPict2Pict(XPPicDesc *dest, byte *boltSprite) {
@@ -95,7 +98,9 @@ void BoltEngine::displayPic(byte *boltSprite, int16 xOff, int16 yOff, int16 page
 		page);
 }
 
-void BoltEngine::sub_11131() {
+bool BoltEngine::pointInRect(Common::Rect *rect, int16 x, int16 y) {
+	// Slightly different from our rect.contains() method...
+	return rect->left <= x && x <= rect->right && rect->top <= y && y <= rect->bottom;
 }
 
 const char *BoltEngine::assetPath(const char *fileName) {


Commit: d0c9be04ab319c6de8c5ff4cacb5a6f154a36bc1
    https://github.com/scummvm/scummvm/commit/d0c9be04ab319c6de8c5ff4cacb5a6f154a36bc1
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-04-07T01:11:14+02:00

Commit Message:
BOLT: CARNIVAL: Implement Yogi's minigame

Changed paths:
    engines/bolt/bolt.h
    engines/bolt/booths/huck.cpp
    engines/bolt/booths/yogi.cpp
    engines/bolt/resource.cpp
    engines/bolt/utils.cpp


diff --git a/engines/bolt/bolt.h b/engines/bolt/bolt.h
index 4b8875ca129..c0cb0149957 100644
--- a/engines/bolt/bolt.h
+++ b/engines/bolt/bolt.h
@@ -510,6 +510,42 @@ typedef struct TopCatAnim {
 	}
 } TopCatAnim;
 
+// YOGI GAME
+
+typedef struct YogiState {
+	int16 levelNumber;
+	int16 currentSlot;
+	int16 levelIndex[10];
+	int16 slotIndex[10];
+	int16 levelComplete;
+	int16 basketSound[24];
+	int16 basketState[24];
+	int16 basketCount;
+	int16 matchCount;
+	int16 selectionPending;
+	int16 selected1Slot;
+	int16 selected2Slot;
+	int16 sound1;
+	int16 sound2;
+
+	YogiState() {
+		levelNumber = 0;
+		currentSlot = 0;
+		memset(levelIndex, 0, sizeof(levelIndex));
+		memset(slotIndex, 0, sizeof(slotIndex));
+		levelComplete = 0;
+		memset(basketSound, 0, sizeof(basketSound));
+		memset(basketState, 0, sizeof(basketState));
+		basketCount = 0;
+		matchCount = 0;
+		selectionPending = 0;
+		selected1Slot = 0;
+		selected2Slot = 0;
+		sound1 = 0;
+		sound2 = 0;
+	}
+} YogiState;
+
 class BoltEngine : public Engine {
 friend class XpLib;
 
@@ -685,6 +721,7 @@ protected:
 	const char *assetPath(const char *fileName);
 	void boltCycleToXPCycle(byte *srcData, XPCycleState *cycleDesc);
 	void unpackColors(int16 count, byte *packedColors);
+	bool intersectRect(const Common::Rect *a, const Common::Rect *b, Common::Rect *out);
 
 	// Swap
 	void swapPicHeader();
@@ -1070,7 +1107,6 @@ protected:
 	void setHuckColors(int16 which);
 	void restoreHuckColors(int16 which);
 	void startHuckShuffleTimer();
-	bool intersectRect(const Common::Rect *a, const Common::Rect *b, Common::Rect *out);
 	void drawGift(int16 slot);
 	void drawHuckGifts();
 	void checkHuckLevelComplete();
@@ -1120,7 +1156,7 @@ protected:
 	byte *g_huckBgDisplayPic = nullptr;
 	int16 g_huckScrollOffset = 0;
 	int16 g_huckPalRange[8] = { 0 };
-	int16 g_returnBooth = 0;
+	int16 g_huckReturnBooth = 0;
 	int16 g_huckExitFlag = 0;
 	int16 g_huckCursorX = 0;
 	int16 g_huckCursorY = 0;
@@ -1306,7 +1342,74 @@ protected:
 	};
 
 	// --- YOGI ---
-	int16 yogiGame(int16 prevBooth) { return 0; }
+	void playSoundMapYogi(int16 memberId);
+	void waitSoundMapYogi();
+	void stopSoundYogi();
+	void setYogiColors(int16 which);
+	void restoreYogiColors(int16 which);
+	void drawBasket(int16 slot, byte *basketSprite);
+	void drawAllBaskets();
+	void handleYogiMatch();
+	bool loadYogiBgPic();
+	void unloadYogiBgPic();
+	void drawYogiLevel();
+	bool loadYogiLevel();
+	void unloadYogiResources();
+	bool initYogiLevel();
+	bool resumeYogiLevel();
+	bool initYogi();
+	void yogiToggleBlinking(int16 which, int16 *state);
+	void yogiUpdateHotSpots(int16 x, int16 y);
+	int16 findBasket(int16 x, int16 y);
+	void resolveYogiSelection();
+	bool handleBasketSelect(int16 x, int16 y);
+	void yogiHandleActionButton(int16 x, int16 y);
+	void handleYogiEvent(int16 eventType, uint32 eventData);
+	void playYogi();
+	void cleanUpYogi();
+	int16 yogiGame(int16 prevBooth);
+
+	void swapYogiAllWords();
+	void swapYogiFirstWord();
+
+	static void swapYogiAllWordsCb();
+	static void swapYogiFirstWordCb();
+
+	BOLTLib *g_yogiBoltLib = nullptr;
+	BOLTCallbacks g_yogiBoltCallbacks;
+
+	static BOLTCallback g_yogiTypeLoadCallbacks[27];
+	static BOLTCallback g_yogiTypeFreeCallbacks[27];
+
+	int16 g_yogiSoundPlaying = 0;
+	int16 g_yogiSoundActive = 0;
+	int16 g_yogiHotSpotCount = 0;
+	int16 g_yogiAnimActive = 0;
+	uint32 g_yogiBlinkTimer1 = 0;
+	uint32 g_yogiBlinkTimer2 = 0;
+	int16 g_yogiBlinkState1 = 0;
+	int16 g_yogiBlinkState2 = 0;
+	int16 g_yogiGlobal[0xA0] = { 0 };
+	YogiState g_yogiState;
+	byte *g_yogiBasketPic = nullptr;
+	int16 g_yogiLevelGroupId = 0;
+	byte *g_yogiBgPic = nullptr;
+	byte *g_yogiNormalSprite = nullptr;
+	byte *g_yogiHlSprite = nullptr;
+	byte *g_yogiAnimSprite = nullptr;
+	int16 g_yogiSpriteStride = 0;
+	int16 g_yogiSpriteHeight = 0;
+	int16 g_yogiPalRange[8] = { 0 };
+	int16 g_yogiReturnBooth = 0;
+	int16 g_yogiExitFlag = 0;
+	int16 g_yogiLevelId = 0;
+	int16 g_yogiCursorX = 0;
+	int16 g_yogiCursorY = 0;
+	byte g_yogiPalSave0[15] = { 0 };
+	byte g_yogiPalHighlight0[15] = { 0 };
+	byte g_yogiPalSave1[15] = { 0 };
+	byte g_yogiPalHighlight1[15] = { 0 };
+	XPPicDesc g_yogiScratchBuf;
 };
 
 extern BoltEngine *g_engine;
diff --git a/engines/bolt/booths/huck.cpp b/engines/bolt/booths/huck.cpp
index c137e694aaf..ce66c678125 100644
--- a/engines/bolt/booths/huck.cpp
+++ b/engines/bolt/booths/huck.cpp
@@ -76,11 +76,6 @@ void BoltEngine::startHuckShuffleTimer() {
 	}
 }
 
-bool BoltEngine::intersectRect(const Common::Rect *a, const Common::Rect *b, Common::Rect *out) {
-	*out = a->findIntersectingRect(*b);
-	return !out->isEmpty();
-}
-
 void BoltEngine::drawGift(int16 slot) {
 	byte *giftSprite = memberAddr(g_huckBoltLib, g_huckState.drawTable1[slot]);
 	int16 sprStride = READ_UINT16(giftSprite + 0x0A);
@@ -598,7 +593,7 @@ void BoltEngine::huckHandleActionButton(int16 x, int16 y) {
 		if (exitRect.contains(x, y)) {
 			setHuckColors(1);
 			restoreHuckColors(0);
-			g_returnBooth = 3;
+			g_huckReturnBooth = 3;
 			g_huckExitFlag = 1;
 		}
 	}
@@ -829,7 +824,7 @@ void BoltEngine::handleEvent(int16 eventType, uint32 eventData) {
 }
 
 void BoltEngine::playHuck() {
-	g_returnBooth = 0x10;
+	g_huckReturnBooth = 0x10;
 
 	while (!shouldQuit()) {
 		if (g_huckExitFlag)
@@ -837,6 +832,7 @@ void BoltEngine::playHuck() {
 
 		uint32 eventData = 0;
 		int16 eventType = _xp->getEvent(etEmpty, &eventData);
+
 		if (eventType)
 			handleEvent(eventType, eventData);
 
@@ -848,7 +844,7 @@ void BoltEngine::playHuck() {
 	}
 
 	if (shouldQuit())
-		g_returnBooth = 0;
+		g_huckReturnBooth = 0;
 }
 
 void BoltEngine::cleanUpHuck() {
@@ -898,7 +894,7 @@ void BoltEngine::cleanUpHuck() {
 
 int16 BoltEngine::huckGame(int16 prevBooth) {
 	if (!openBOLTLib(&g_huckBoltLib, &g_huckBoltCallbacks, assetPath("huck.blt")))
-		return g_returnBooth;
+		return g_huckReturnBooth;
 
 	int16 savedTimer = _xp->setInactivityTimer(30);
 
@@ -910,7 +906,7 @@ int16 BoltEngine::huckGame(int16 prevBooth) {
 	_xp->setInactivityTimer(savedTimer);
 	closeBOLTLib(&g_huckBoltLib);
 
-	return g_returnBooth;
+	return g_huckReturnBooth;
 }
 
 void BoltEngine::swapHuckWordArray() {
diff --git a/engines/bolt/booths/yogi.cpp b/engines/bolt/booths/yogi.cpp
index d0e5b38e0bf..26247be03a0 100644
--- a/engines/bolt/booths/yogi.cpp
+++ b/engines/bolt/booths/yogi.cpp
@@ -21,8 +21,783 @@
 
 #include "bolt/bolt.h"
 
+#include "common/memstream.h"
+
 namespace Bolt {
 
+void BoltEngine::playSoundMapYogi(int16 memberId) {
+	byte *soundData = getBOLTMember(g_yogiBoltLib, memberId);
+	int32 soundSize = memberSize(g_yogiBoltLib, memberId);
+	if (soundData) {
+		_xp->playSound(soundData, soundSize, 22050);
+		g_yogiSoundActive = 1;
+		g_yogiSoundPlaying++;
+	}
+}
+
+void BoltEngine::waitSoundMapYogi() {
+	if (!g_yogiSoundPlaying)
+		return;
+
+	uint32 dummy;
+	while (_xp->getEvent(etSound, &dummy) == etEmpty);
+
+	g_yogiSoundActive = 0;
+	g_yogiSoundPlaying--;
+}
+
+void BoltEngine::stopSoundYogi() {
+	if (g_yogiSoundActive) {
+		_xp->stopSound();
+		g_yogiSoundActive = 0;
+	}
+}
+
+void BoltEngine::setYogiColors(int16 which) {
+	if (which == 0)
+		_xp->setPalette(g_yogiPalRange[1] - g_yogiPalRange[0] + 1, g_yogiPalRange[0], g_yogiPalHighlight0);
+	else if (which == 1)
+		_xp->setPalette(g_yogiPalRange[5] - g_yogiPalRange[4] + 1, g_yogiPalRange[4], g_yogiPalHighlight1);
+}
+
+void BoltEngine::restoreYogiColors(int16 which) {
+	if (which == 0)
+		_xp->setPalette(g_yogiPalRange[1] - g_yogiPalRange[0] + 1, g_yogiPalRange[0], g_yogiPalSave0);
+	else if (which == 1)
+		_xp->setPalette(g_yogiPalRange[5] - g_yogiPalRange[4] + 1, g_yogiPalRange[4], g_yogiPalSave1);
+}
+
+void BoltEngine::drawBasket(int16 slot, byte *basketSprite) {
+	int16 sprStride = READ_UINT16(basketSprite + 0x0A);
+	int16 sprHeight = READ_UINT16(basketSprite + 0x0C);
+
+	memset(g_yogiScratchBuf.pixelData, 0, (int32)sprStride * sprHeight);
+
+	int16 slotY = READ_UINT16(g_yogiBasketPic + slot * 2 + 0x42);
+	int16 slotX = READ_UINT16(g_yogiBasketPic + slot * 2 + 0x12);
+	Common::Rect targetRect(slotY, slotX, slotY + sprHeight, slotX + sprStride);
+
+	int16 basketCount = READ_UINT16(g_yogiBasketPic);
+	for (int16 i = 0; i < basketCount; i++) {
+		if (g_yogiState.basketState[i] != 0)
+			continue;
+
+		byte *otherSprite;
+		if (i == g_yogiState.selected1Slot || i == g_yogiState.selected2Slot)
+			otherSprite = g_yogiHlSprite;
+		else
+			otherSprite = g_yogiNormalSprite;
+
+		int16 otherStride = READ_UINT16(otherSprite + 0x0A);
+		int16 otherHeight = READ_UINT16(otherSprite + 0x0C);
+		int16 otherY = READ_UINT16(g_yogiBasketPic + i * 2 + 0x42);
+		int16 otherX = READ_UINT16(g_yogiBasketPic + i * 2 + 0x12);
+		Common::Rect otherRect(otherY, otherX, otherY + otherHeight, otherX + otherStride);
+
+		Common::Rect isect;
+		if (!intersectRect(&targetRect, &otherRect, &isect))
+			continue;
+
+		byte *srcPixels = getResolvedPtr(otherSprite, 0x12);
+		byte *src = srcPixels + (isect.left - otherY) * otherStride + (isect.top - otherX);
+		byte *dst = g_yogiScratchBuf.pixelData + (isect.left - slotY) * sprStride + (isect.top - slotX);
+
+		int16 blitWidth = isect.bottom - isect.top;
+		int16 blitHeight = isect.right - isect.left;
+		_xp->maskBlit(src, otherStride, dst, sprStride, blitWidth, blitHeight);
+	}
+
+	g_yogiScratchBuf.width = sprStride;
+	g_yogiScratchBuf.height = sprHeight;
+	g_yogiScratchBuf.palette = nullptr;
+	g_yogiScratchBuf.flags = 0;
+
+	_xp->displayPic(&g_yogiScratchBuf, slotX, slotY, stFront);
+}
+
+void BoltEngine::drawAllBaskets() {
+	_xp->fillDisplay(0, stFront);
+
+	int16 basketCount = READ_UINT16(g_yogiBasketPic);
+	for (int16 i = 0; i < basketCount; i++) {
+		if (g_yogiState.basketState[i] != 0)
+			continue;
+
+		byte *sprite = (i == g_yogiState.selected1Slot || i == g_yogiState.selected2Slot)
+						   ? g_yogiHlSprite
+						   : g_yogiNormalSprite;
+
+		int16 slotY = READ_UINT16(g_yogiBasketPic + i * 2 + 0x42);
+		int16 slotX = READ_UINT16(g_yogiBasketPic + i * 2 + 0x12);
+		displayPic(sprite, slotX, slotY, stFront);
+	}
+}
+
+void BoltEngine::handleYogiMatch() {
+	playSoundMapYogi((int16)READ_UINT16(g_yogiBasketPic + 0x9E));
+
+	if (g_yogiState.basketCount == 2) {
+		g_yogiExitFlag = 1;
+		g_yogiState.levelComplete = 1;
+		g_yogiState.levelNumber++;
+		if (g_yogiState.levelNumber > 10)
+			g_yogiState.levelNumber = 10;
+		_xp->fillDisplay(0, stFront);
+	} else {
+		drawBasket(g_yogiState.selected1Slot, g_yogiHlSprite);
+		drawBasket(g_yogiState.selected2Slot, g_yogiHlSprite);
+	}
+
+	g_yogiState.selected1Slot = -1;
+	g_yogiState.selected2Slot = -1;
+	g_yogiState.basketCount -= 2;
+	_xp->updateDisplay();
+	waitSoundMapYogi();
+}
+
+bool BoltEngine::loadYogiBgPic() {
+	int16 groupId;
+
+	if (g_displayMode == 0) {
+		groupId = 0x100;
+	} else if (g_displayMode == 1) {
+		groupId = 0x200;
+	} else {
+		return false;
+	}
+
+	if (!getBOLTGroup(g_yogiBoltLib, groupId, 1))
+		return false;
+
+	g_yogiBgPic = memberAddr(g_yogiBoltLib, groupId + 1);
+	return true;
+}
+
+void BoltEngine::unloadYogiBgPic() {
+	freeBOLTGroup(g_yogiBoltLib, 0x100, 1);
+}
+
+void BoltEngine::drawYogiLevel() {
+	byte *palSprite = memberAddr(g_yogiBoltLib, READ_UINT16(g_yogiBasketPic + 0x72));
+
+	g_yogiSoundActive = 0;
+	g_yogiHotSpotCount = 0;
+	g_yogiBlinkTimer1 = 0;
+	g_yogiBlinkTimer2 = 0;
+	g_yogiExitFlag = 0;
+
+	uint32 dummy;
+	while (_xp->getEvent(etTimer, &dummy) != etTimer);
+
+	_xp->stopCycle();
+	_xp->setTransparency(false);
+	displayColors(palSprite, stBack, 0);
+	displayPic(g_yogiBgPic, g_displayX, g_displayY, stFront);
+	_xp->updateDisplay();
+
+	_xp->setTransparency(true);
+	displayColors(palSprite, stFront, 0);
+	displayColors(palSprite, stBack, 1);
+	displayPic(g_yogiBgPic, g_displayX, g_displayY, stBack);
+
+	drawAllBaskets();
+	_xp->updateDisplay();
+
+	g_yogiCursorX = 0xC0;
+	g_yogiCursorY = 0x78;
+	_xp->setCursorPos(0xC0, 0x78);
+	_xp->setCursorColor(0, 0, 0xFF);
+	_xp->showCursor();
+
+	g_yogiPalRange[0] = READ_UINT16(g_yogiBasketPic + 0x7C) + 0x80;
+	g_yogiPalRange[1] = READ_UINT16(g_yogiBasketPic + 0x7E) + 0x80;
+	g_yogiPalRange[2] = READ_UINT16(g_yogiBasketPic + 0x80) + 0x80;
+	g_yogiPalRange[3] = READ_UINT16(g_yogiBasketPic + 0x82) + 0x80;
+	g_yogiPalRange[4] = READ_UINT16(g_yogiBasketPic + 0x8E) + 0x80;
+	g_yogiPalRange[5] = READ_UINT16(g_yogiBasketPic + 0x90) + 0x80;
+	g_yogiPalRange[6] = READ_UINT16(g_yogiBasketPic + 0x92) + 0x80;
+	g_yogiPalRange[7] = READ_UINT16(g_yogiBasketPic + 0x94) + 0x80;
+
+	_xp->getPalette(g_yogiPalRange[0], g_yogiPalRange[1] - g_yogiPalRange[0] + 1, g_yogiPalSave0);
+	_xp->getPalette(g_yogiPalRange[2], g_yogiPalRange[3] - g_yogiPalRange[2] + 1, g_yogiPalHighlight0);
+	_xp->getPalette(g_yogiPalRange[4], g_yogiPalRange[5] - g_yogiPalRange[4] + 1, g_yogiPalSave1);
+	_xp->getPalette(g_yogiPalRange[6], g_yogiPalRange[7] - g_yogiPalRange[6] + 1, g_yogiPalHighlight1);
+}
+
+bool BoltEngine::loadYogiLevel() {
+	int32 maxSize = 0;
+
+	g_yogiLevelGroupId = (g_yogiState.levelNumber - 1) * 0x100 + 0x300;
+
+	if (!getBOLTGroup(g_yogiBoltLib, g_yogiLevelGroupId, 1))
+		return false;
+
+	g_yogiBasketPic = memberAddr(g_yogiBoltLib, g_yogiLevelGroupId);
+	g_yogiNormalSprite = memberAddr(g_yogiBoltLib, READ_UINT16(g_yogiBasketPic + 0x02));
+	g_yogiHlSprite = memberAddr(g_yogiBoltLib, READ_UINT16(g_yogiBasketPic + 0x04));
+	g_yogiAnimSprite = memberAddr(g_yogiBoltLib, READ_UINT16(g_yogiBasketPic + 0x06));
+
+	int32 size;
+
+	size = (int32)READ_UINT16(g_yogiNormalSprite + 0x0A) * READ_UINT16(g_yogiNormalSprite + 0x0C);
+	if (size > maxSize)
+		maxSize = size;
+
+	size = (int32)READ_UINT16(g_yogiHlSprite + 0x0A) * READ_UINT16(g_yogiHlSprite + 0x0C);
+	if (size > maxSize)
+		maxSize = size;
+
+	size = (int32)READ_UINT16(g_yogiAnimSprite + 0x0A) * READ_UINT16(g_yogiAnimSprite + 0x0C);
+	if (size > maxSize)
+		maxSize = size;
+
+	g_yogiScratchBuf.pixelData = (byte *)_xp->allocMem(maxSize);
+	if (!g_yogiScratchBuf.pixelData)
+		return false;
+
+	g_yogiSpriteStride = READ_UINT16(g_yogiNormalSprite + 0x0A);
+	g_yogiSpriteHeight = READ_UINT16(g_yogiNormalSprite + 0x0C);
+	return true;
+}
+
+void BoltEngine::unloadYogiResources() {
+	_xp->hideCursor();
+	int16 basketGroupId = (g_yogiLevelId << 8) + 0xD00;
+	freeBOLTGroup(g_yogiBoltLib, basketGroupId, 1);
+	freeBOLTGroup(g_yogiBoltLib, g_yogiLevelGroupId, 1);
+}
+
+bool BoltEngine::initYogiLevel() {
+	g_yogiState.currentSlot++;
+	if (g_yogiState.currentSlot >= 10)
+		g_yogiState.currentSlot = 0;
+
+	g_yogiLevelId = g_yogiState.slotIndex[g_yogiState.currentSlot];
+
+	if (!loadYogiLevel())
+		return false;
+
+	int16 basketCount = READ_UINT16(g_yogiBasketPic);
+	g_yogiState.basketCount = basketCount;
+	g_yogiState.levelComplete = 0;
+	g_yogiState.matchCount = 0;
+	g_yogiState.selectionPending = 0;
+	g_yogiState.selected1Slot = -1;
+	g_yogiState.selected2Slot = -1;
+
+	for (int16 i = 0; i < 0x18; i++) {
+		g_yogiState.basketSound[i] = 0x10;
+		g_yogiState.basketState[i] = 0;
+	}
+
+	int16 idx = g_yogiState.levelIndex[g_yogiLevelId];
+	int16 snd = g_yogiGlobal[g_yogiLevelId * 0x10 + idx];
+	int16 flag = 0;
+
+	for (int16 var_4 = 0; var_4 < basketCount; var_4++) {
+		int16 rnd = _xp->getRandom(basketCount);
+		while (g_yogiState.basketSound[rnd] != 0x10) {
+			rnd++;
+			if (rnd >= basketCount)
+				rnd = 0;
+		}
+		g_yogiState.basketSound[rnd] = snd;
+
+		if (flag == 0) {
+			flag = 1;
+		} else {
+			flag = 0;
+			idx++;
+			if (idx == 0x10)
+				idx = 0;
+			snd = g_yogiGlobal[g_yogiLevelId * 0x10 + idx];
+		}
+	}
+
+	g_yogiState.levelIndex[g_yogiLevelId] = idx;
+	drawYogiLevel();
+	return true;
+}
+
+bool BoltEngine::resumeYogiLevel() {
+	g_yogiLevelId = g_yogiState.slotIndex[g_yogiState.currentSlot];
+
+	if (!loadYogiLevel())
+		return false;
+
+	drawYogiLevel();
+	return true;
+}
+
+bool BoltEngine::initYogi() {
+	g_yogiSoundPlaying = 0;
+
+	if (!loadYogiBgPic())
+		return false;
+
+	if (!vLoad(&g_yogiGlobal, "YogiGlobal")) {
+		int16 slotVal = 0;
+		int16 globIdx = 0;
+		int16 idx = 0;
+
+		while (slotVal < 10) {
+			g_yogiState.slotIndex[idx] = slotVal;
+			g_yogiState.levelIndex[idx] = 0;
+
+			for (int16 i = 0; i < 16; i++)
+				g_yogiGlobal[globIdx + i] = 0x10;
+
+			for (int16 di = 0; di < 16; di++) {
+				int16 rnd = _xp->getRandom(16);
+				while (g_yogiGlobal[globIdx + rnd] != 0x10) {
+					rnd++;
+					if (rnd >= 16)
+						rnd = 0;
+				}
+				g_yogiGlobal[globIdx + rnd] = di;
+			}
+
+			globIdx += 0x10;
+			idx++;
+			slotVal++;
+		}
+	}
+
+	byte yogiStateBuf[0x9C] = { 0 };
+
+	if (!vLoad(&yogiStateBuf, "Yogi")) {
+		g_yogiState.levelNumber = 1;
+		g_yogiState.currentSlot = -1;
+		return initYogiLevel();
+	}
+
+	Common::SeekableReadStream *yogiStateReadStream = new Common::MemoryReadStream(yogiStateBuf, sizeof(yogiStateBuf), DisposeAfterUse::NO);
+
+	g_yogiState.levelNumber = yogiStateReadStream->readSint16BE();
+	g_yogiState.currentSlot = yogiStateReadStream->readSint16BE();
+
+	for (int i = 0; i < 10; i++)
+		g_yogiState.levelIndex[i] = yogiStateReadStream->readSint16BE();
+
+	for (int i = 0; i < 10; i++)
+		g_yogiState.slotIndex[i] = yogiStateReadStream->readSint16BE();
+
+	g_yogiState.levelComplete = yogiStateReadStream->readSint16BE();
+
+	for (int i = 0; i < 24; i++)
+		g_yogiState.basketSound[i] = yogiStateReadStream->readSint16BE();
+
+	for (int i = 0; i < 24; i++)
+		g_yogiState.basketState[i] = yogiStateReadStream->readSint16BE();
+
+	g_yogiState.basketCount = yogiStateReadStream->readSint16BE();
+	g_yogiState.matchCount = yogiStateReadStream->readSint16BE();
+	g_yogiState.selectionPending = yogiStateReadStream->readSint16BE();
+	g_yogiState.selected1Slot = yogiStateReadStream->readSint16BE();
+	g_yogiState.selected2Slot = yogiStateReadStream->readSint16BE();
+	g_yogiState.sound1 = yogiStateReadStream->readSint16BE();
+	g_yogiState.sound2 = yogiStateReadStream->readSint16BE();
+
+	// Sanity check: should be exactly 0x9C
+	assert(yogiStateReadStream->pos() == 0x9C);
+	delete yogiStateReadStream;
+
+	if (g_yogiState.levelComplete)
+		return initYogiLevel();
+	else
+		return resumeYogiLevel();
+}
+
+void BoltEngine::yogiToggleBlinking(int16 which, int16 *state) {
+	*state = (*state == 0) ? 1 : 0;
+	if (*state)
+		setYogiColors(which);
+	else
+		restoreYogiColors(which);
+}
+
+void BoltEngine::yogiUpdateHotSpots(int16 x, int16 y) {
+	Common::Rect helpRect(
+		READ_UINT16(g_yogiBasketPic + 0x74), READ_UINT16(g_yogiBasketPic + 0x78),
+		READ_UINT16(g_yogiBasketPic + 0x76), READ_UINT16(g_yogiBasketPic + 0x7A));
+
+	Common::Rect exitRect(
+		READ_UINT16(g_yogiBasketPic + 0x86), READ_UINT16(g_yogiBasketPic + 0x8A),
+		READ_UINT16(g_yogiBasketPic + 0x88), READ_UINT16(g_yogiBasketPic + 0x8C));
+
+	if (helpRect.contains(x, y)) {
+		if (!g_yogiBlinkTimer1 && !g_yogiHotSpotCount)
+			setYogiColors(0);
+	} else {
+		if (!g_yogiBlinkTimer1 && !g_yogiHotSpotCount)
+			restoreYogiColors(0);
+	}
+
+	if (exitRect.contains(x, y)) {
+		if (!g_yogiBlinkTimer2)
+			setYogiColors(1);
+		return;
+	} else {
+		if (!g_yogiBlinkTimer2)
+			restoreYogiColors(1);
+	}
+}
+
+int16 BoltEngine::findBasket(int16 x, int16 y) {
+	int16 basketCount = READ_UINT16(g_yogiBasketPic);
+	for (int16 i = basketCount - 1; i >= 0; i--) {
+		if (g_yogiState.basketState[i] != 0)
+			continue;
+
+		int16 slotX = READ_UINT16(g_yogiBasketPic + i * 2 + 0x12);
+		int16 slotX2 = slotX + g_yogiSpriteStride - 1;
+		int16 slotY = READ_UINT16(g_yogiBasketPic + i * 2 + 0x42);
+		int16 slotY2 = slotY + g_yogiSpriteHeight - 1;
+
+		if (x < slotX || x > slotX2 || y < slotY || y > slotY2)
+			continue;
+
+		byte *sprite = (i == g_yogiState.selected1Slot || i == g_yogiState.selected2Slot)
+						   ? g_yogiHlSprite
+						   : g_yogiNormalSprite;
+
+		if (!getPixel(sprite, x - slotX, y - slotY))
+			continue;
+
+		return i;
+	}
+
+	return -1;
+}
+
+void BoltEngine::resolveYogiSelection() {
+	if (g_yogiState.sound1 == g_yogiState.sound2) {
+		g_yogiState.basketState[g_yogiState.selected1Slot] = 1;
+		g_yogiState.basketState[g_yogiState.selected2Slot] = 1;
+		handleYogiMatch();
+		g_yogiState.matchCount = 0;
+		g_yogiState.selectionPending = 0;
+	} else {
+		int16 slot1 = g_yogiState.selected1Slot;
+		g_yogiState.matchCount = 0;
+		g_yogiState.selected1Slot = -1;
+		drawBasket(slot1, g_yogiNormalSprite);
+		_xp->updateDisplay();
+		playSoundMapYogi((int32)(int16)READ_UINT16(g_yogiBasketPic + 0x9A));
+		waitSoundMapYogi();
+
+		int16 slot2 = g_yogiState.selected2Slot;
+		g_yogiState.selectionPending = 0;
+		g_yogiState.selected2Slot = -1;
+		drawBasket(slot2, g_yogiNormalSprite);
+		_xp->updateDisplay();
+		playSoundMapYogi((int32)(int16)READ_UINT16(g_yogiBasketPic + 0x9C));
+		waitSoundMapYogi();
+	}
+}
+
+bool BoltEngine::handleBasketSelect(int16 x, int16 y) {
+	int16 slot = findBasket(x, y);
+	if (slot == -1)
+		return false;
+
+	if (g_yogiState.matchCount == 0) {
+		g_yogiState.matchCount = 1;
+		g_yogiState.sound1 = g_yogiState.basketSound[slot];
+		g_yogiState.selected1Slot = slot;
+
+		playSoundMapYogi((int32)(int16)READ_UINT16(g_yogiBasketPic + 0x98));
+		drawBasket(slot, g_yogiHlSprite);
+		_xp->updateDisplay();
+		waitSoundMapYogi();
+
+		int32 soundId = (int32)((g_yogiLevelId << 8) + g_yogiState.sound1 + 0xD00);
+		playSoundMapYogi(soundId);
+
+	} else {
+		if (g_yogiState.selectionPending)
+			return true;
+
+		if (g_yogiState.selected1Slot == slot) {
+			// De-select basket...
+			g_yogiState.matchCount = 0;
+			g_yogiState.selected1Slot = -1;
+
+			stopSoundYogi();
+			playSoundMapYogi((int32)(int16)READ_UINT16(g_yogiBasketPic + 0x9A));
+			drawBasket(slot, g_yogiNormalSprite);
+			_xp->updateDisplay();
+			waitSoundMapYogi();
+
+		} else {
+			g_yogiState.selectionPending = 1;
+			g_yogiState.sound2 = g_yogiState.basketSound[slot];
+			g_yogiState.selected2Slot = slot;
+
+			if (g_yogiSoundActive) {
+				int16 slotY = READ_UINT16(g_yogiBasketPic + slot * 2 + 0x42);
+				int16 slotX = READ_UINT16(g_yogiBasketPic + slot * 2 + 0x12);
+				displayPic(g_yogiAnimSprite, slotX + READ_UINT16(g_yogiBasketPic + 0x0A), slotY + READ_UINT16(g_yogiBasketPic + 0x0C), stFront);
+				_xp->updateDisplay();
+				waitSoundMapYogi();
+			}
+
+			playSoundMapYogi((int32)(int16)READ_UINT16(g_yogiBasketPic + 0x98));
+			drawBasket(slot, g_yogiHlSprite);
+			_xp->updateDisplay();
+			waitSoundMapYogi();
+
+			int32 soundId = (int32)((g_yogiLevelId << 8) + g_yogiState.sound2 + 0xD00);
+			playSoundMapYogi(soundId);
+			waitSoundMapYogi();
+
+			resolveYogiSelection();
+		}
+	}
+
+	return true;
+}
+
+void BoltEngine::yogiHandleActionButton(int16 x, int16 y) {
+	bool didAction = false;
+	bool stoppedAnim = false;
+
+	if (g_yogiHotSpotCount != 0) {
+		g_yogiHotSpotCount = 0;
+		stoppedAnim = true;
+		stopAnimation();
+		if (g_yogiBlinkTimer2) {
+			_xp->killTimer(g_yogiBlinkTimer2);
+			g_yogiBlinkTimer2 = 0;
+		}
+
+		yogiUpdateHotSpots(g_yogiCursorX, g_yogiCursorY);
+	}
+
+	Common::Rect helpRect(
+		READ_UINT16(g_yogiBasketPic + 0x74), READ_UINT16(g_yogiBasketPic + 0x78),
+		READ_UINT16(g_yogiBasketPic + 0x76), READ_UINT16(g_yogiBasketPic + 0x7A));
+
+	if (helpRect.contains(x, y)) {
+		didAction = true;
+		stopSoundYogi();
+		if (g_yogiBlinkTimer1) {
+			_xp->killTimer(g_yogiBlinkTimer1);
+			g_yogiBlinkTimer1 = 0;
+			setYogiColors(0);
+		}
+		if (!g_yogiHotSpotCount && !stoppedAnim) {
+			g_yogiHotSpotCount = startAnimation(g_rtfHandle, 0x1D);
+			g_yogiAnimActive = 0;
+		}
+	} else {
+		Common::Rect exitRect(
+			READ_UINT16(g_yogiBasketPic + 0x86), READ_UINT16(g_yogiBasketPic + 0x8A),
+			READ_UINT16(g_yogiBasketPic + 0x88), READ_UINT16(g_yogiBasketPic + 0x8C));
+
+		if (exitRect.contains(x, y)) {
+			stopSoundYogi();
+			setYogiColors(1);
+			restoreYogiColors(0);
+			g_yogiReturnBooth = 6;
+			g_yogiExitFlag = 1;
+		}
+	}
+
+	if (handleBasketSelect(x, y))
+		didAction = true;
+
+	if (didAction) {
+		if (g_yogiBlinkTimer1) {
+			_xp->killTimer(g_yogiBlinkTimer1);
+			g_yogiBlinkTimer1 = 0;
+			restoreYogiColors(0);
+		}
+		_xp->setInactivityTimer(0x1E);
+	}
+}
+
+void BoltEngine::handleYogiEvent(int16 eventType, uint32 eventData) {
+	switch (eventType) {
+	case etMouseDown:
+		yogiHandleActionButton(g_yogiCursorX, g_yogiCursorY);
+		break;
+
+	case etMouseMove:
+		g_yogiCursorX = (int16)(eventData >> 16);
+		g_yogiCursorY = (int16)(eventData & -1);
+		yogiUpdateHotSpots(g_yogiCursorX, g_yogiCursorY);
+		break;
+
+	case etTimer: { // 1
+		int32 blinkInterval = ((int32)READ_UINT16(g_yogiBasketPic + 0x84)) * 1000 / 60;
+
+		if (eventData == g_yogiBlinkTimer1) {
+			yogiToggleBlinking(0, &g_yogiBlinkState1);
+			g_yogiBlinkTimer1 = _xp->startTimer((int16)blinkInterval);
+		} else if (eventData == g_yogiBlinkTimer2) {
+			yogiToggleBlinking(1, &g_yogiBlinkState2);
+			g_yogiBlinkTimer2 = _xp->startTimer((int16)blinkInterval);
+		}
+
+		break;
+	}
+
+	case etInactivity:
+		if (!g_yogiBlinkTimer1) {
+			int32 blinkInterval = ((int32)READ_UINT16(g_yogiBasketPic + 0x84)) * 1000 / 60;
+			g_yogiBlinkTimer1 = _xp->startTimer((int16)blinkInterval);
+			g_yogiBlinkState1 = 0;
+			yogiToggleBlinking(0, &g_yogiBlinkState1);
+		}
+
+		break;
+
+	case etSound:
+		if (g_yogiHotSpotCount != 0) {
+			g_yogiHotSpotCount = maintainAudioPlay(1);
+			if (g_yogiHotSpotCount == 0)
+				yogiUpdateHotSpots(g_yogiCursorX, g_yogiCursorY);
+		} else {
+			g_yogiSoundActive = 0;
+		}
+
+		if (g_yogiSoundPlaying != 0)
+			g_yogiSoundPlaying--;
+
+		break;
+
+	case etTrigger:
+		g_yogiAnimActive++;
+
+		if (g_yogiAnimActive == 1) {
+			int32 blinkInterval = ((int32)READ_UINT16(g_yogiBasketPic + 0x84)) * 1000 / 60;
+			g_yogiBlinkTimer2 = _xp->startTimer((int16)blinkInterval);
+			g_yogiBlinkState2 = 0;
+			yogiToggleBlinking(1, &g_yogiBlinkState2);
+		} else if (g_yogiAnimActive == 2) {
+			_xp->killTimer(g_yogiBlinkTimer2);
+			g_yogiBlinkTimer2 = 0;
+			restoreYogiColors(1);
+		}
+
+		break;
+
+	default:
+		break;
+	}
+}
+
+void BoltEngine::playYogi() {
+	g_yogiReturnBooth = 16;
+
+	while (!shouldQuit()) {
+		if (g_yogiExitFlag)
+			break;
+
+		uint32 eventData = 0;
+		int16 eventType = _xp->getEvent(etEmpty, &eventData);
+
+		if (eventType)
+			handleYogiEvent(eventType, eventData);
+
+		if (g_yogiHotSpotCount != 0) {
+			g_yogiHotSpotCount = maintainAudioPlay(0);
+			if (g_yogiHotSpotCount == 0)
+				yogiUpdateHotSpots(g_yogiCursorX, g_yogiCursorY);
+		}
+	}
+
+	if (shouldQuit())
+		g_yogiReturnBooth = 0;
+}
+
+void BoltEngine::cleanUpYogi() {
+	if (g_yogiScratchBuf.pixelData) {
+		_xp->freeMem(g_yogiScratchBuf.pixelData);
+		g_yogiScratchBuf.pixelData = nullptr;
+		g_yogiScratchBuf.width = 0;
+		g_yogiScratchBuf.height = 0;
+	}
+
+	vSave(&g_yogiGlobal, 0x140, "YogiGlobal");
+
+	byte yogiSaveBuf[0x9C] = { 0 };
+	Common::MemoryWriteStream *yogiStateWriteStream = new Common::MemoryWriteStream(yogiSaveBuf, sizeof(yogiSaveBuf));
+
+	yogiStateWriteStream->writeSint16BE(g_yogiState.levelNumber);
+	yogiStateWriteStream->writeSint16BE(g_yogiState.currentSlot);
+
+	for (int i = 0; i < 10; i++)
+		yogiStateWriteStream->writeSint16BE(g_yogiState.levelIndex[i]);
+
+	for (int i = 0; i < 10; i++)
+		yogiStateWriteStream->writeSint16BE(g_yogiState.slotIndex[i]);
+
+	yogiStateWriteStream->writeSint16BE(g_yogiState.levelComplete);
+
+	for (int i = 0; i < 24; i++)
+		yogiStateWriteStream->writeSint16BE(g_yogiState.basketSound[i]);
+
+	for (int i = 0; i < 24; i++)
+		yogiStateWriteStream->writeSint16BE(g_yogiState.basketState[i]);
+
+	yogiStateWriteStream->writeSint16BE(g_yogiState.basketCount);
+	yogiStateWriteStream->writeSint16BE(g_yogiState.matchCount);
+	yogiStateWriteStream->writeSint16BE(g_yogiState.selectionPending);
+	yogiStateWriteStream->writeSint16BE(g_yogiState.selected1Slot);
+	yogiStateWriteStream->writeSint16BE(g_yogiState.selected2Slot);
+	yogiStateWriteStream->writeSint16BE(g_yogiState.sound1);
+	yogiStateWriteStream->writeSint16BE(g_yogiState.sound2);
+
+	// Sanity check: should be exactly 0x9C
+	assert(yogiStateWriteStream->pos() == 0x9C);
+	delete yogiStateWriteStream;
+
+	vSave(&yogiSaveBuf, 0x9C, "Yogi");
+
+	unloadYogiResources();
+	unloadYogiBgPic();
+
+	_xp->stopCycle();
+	_xp->fillDisplay(0, stFront);
+	_xp->updateDisplay();
+}
+
+int16 BoltEngine::yogiGame(int16 prevBooth) {
+	if (!openBOLTLib(&g_yogiBoltLib, &g_yogiBoltCallbacks, assetPath("yogi.blt")))
+		return g_yogiReturnBooth;
+
+	int16 savedTimer = _xp->setInactivityTimer(30);
+
+	if (initYogi())
+		playYogi();
+
+	cleanUpYogi();
+	_xp->setInactivityTimer(savedTimer);
+	closeBOLTLib(&g_yogiBoltLib);
+
+	return g_yogiReturnBooth;
+}
+
+void BoltEngine::swapYogiAllWords() {
+	byte *ptr = g_boltCurrentMemberEntry->dataPtr;
+	if (!ptr)
+		return;
+
+	int16 count = (int16)(g_boltCurrentMemberEntry->decompSize >> 1);
+	for (int16 i = 0; i < count; i++) {
+		WRITE_UINT16(ptr, READ_BE_UINT16(ptr));
+		ptr += 2;
+	}
+}
+
+void BoltEngine::swapYogiFirstWord() {
+	byte *ptr = (byte *)g_boltCurrentMemberEntry->dataPtr;
+	if (!ptr)
+		return;
 
+	WRITE_UINT16(ptr, READ_BE_UINT16(ptr));
+}
 
 } // End of namespace Bolt
diff --git a/engines/bolt/resource.cpp b/engines/bolt/resource.cpp
index 0127978dd46..19d6d886654 100644
--- a/engines/bolt/resource.cpp
+++ b/engines/bolt/resource.cpp
@@ -659,6 +659,9 @@ BOLTCallback BoltEngine::g_scoobyTypeFreeCallbacks[28];
 BOLTCallback BoltEngine::g_topCatTypeLoadCallbacks[26];
 BOLTCallback BoltEngine::g_topCatTypeFreeCallbacks[26];
 
+BOLTCallback BoltEngine::g_yogiTypeLoadCallbacks[27];
+BOLTCallback BoltEngine::g_yogiTypeFreeCallbacks[27];
+
 void BoltEngine::noOpCb() {}
 void BoltEngine::swapAllWordsCb() { ((BoltEngine *)g_engine)->swapAllWords(); }
 void BoltEngine::swapAllLongsCb() { ((BoltEngine *)g_engine)->swapAllLongs(); }
@@ -687,6 +690,9 @@ void BoltEngine::swapScoobyWordArrayCb() { ((BoltEngine *)g_engine)->swapScoobyW
 
 void BoltEngine::swapTopCatHelpEntryCb() { ((BoltEngine *)g_engine)->swapTopCatHelpEntry(); }
 
+void BoltEngine::swapYogiAllWordsCb() { ((BoltEngine *)g_engine)->swapYogiAllWords(); }
+void BoltEngine::swapYogiFirstWordCb() { ((BoltEngine *)g_engine)->swapYogiFirstWord(); }
+
 void BoltEngine::initCallbacks() {
 	// --- BOOTHS ---
 	for (int i = 0; i < ARRAYSIZE(g_defaultTypeLoadCallbacks); i++) {
@@ -866,6 +872,31 @@ void BoltEngine::initCallbacks() {
 	g_topCatBoltCallbacks.memberFreeCallbacks = g_defaultMemberFreeCallbacks;
 	g_topCatBoltCallbacks.groupLoadCallbacks = g_defaultGroupLoadCallbacks;
 	g_topCatBoltCallbacks.groupFreeCallbacks = g_defaultGroupFreeCallbacks;
+
+	// --- YOGI ---
+	for (int i = 0; i < ARRAYSIZE(g_yogiTypeLoadCallbacks); i++) {
+		g_yogiTypeLoadCallbacks[i] = noOpCb;
+	}
+
+	g_yogiTypeLoadCallbacks[2] = swapAllWordsCb;
+	g_yogiTypeLoadCallbacks[8] = swapSpriteHeaderCb;
+	g_yogiTypeLoadCallbacks[10] = swapPicHeaderCb;
+	g_yogiTypeLoadCallbacks[14] = swapFirstFourWordsCb;
+	g_yogiTypeLoadCallbacks[25] = swapYogiFirstWordCb;
+	g_yogiTypeLoadCallbacks[26] = swapYogiAllWordsCb;
+
+	for (int i = 0; i < ARRAYSIZE(g_yogiTypeFreeCallbacks); i++) {
+		g_yogiTypeFreeCallbacks[i] = noOpCb;
+	}
+
+	g_yogiTypeFreeCallbacks[8] = freeSpriteCleanUpCb;
+
+	g_yogiBoltCallbacks.typeLoadCallbacks = g_yogiTypeLoadCallbacks;
+	g_yogiBoltCallbacks.typeFreeCallbacks = g_yogiTypeFreeCallbacks;
+	g_yogiBoltCallbacks.memberLoadCallbacks = g_defaultMemberLoadCallbacks;
+	g_yogiBoltCallbacks.memberFreeCallbacks = g_defaultMemberFreeCallbacks;
+	g_yogiBoltCallbacks.groupLoadCallbacks = g_defaultGroupLoadCallbacks;
+	g_yogiBoltCallbacks.groupFreeCallbacks = g_defaultGroupFreeCallbacks;
 }
 
 } // End of namespace Bolt
diff --git a/engines/bolt/utils.cpp b/engines/bolt/utils.cpp
index b2a3c82ebcf..293d7531e6b 100644
--- a/engines/bolt/utils.cpp
+++ b/engines/bolt/utils.cpp
@@ -158,4 +158,9 @@ void BoltEngine::unpackColors(int16 count, byte *packedColors) {
 	}
 }
 
+bool BoltEngine::intersectRect(const Common::Rect *a, const Common::Rect *b, Common::Rect *out) {
+	*out = a->findIntersectingRect(*b);
+	return !out->isEmpty();
+}
+
 } // End of namespace Bolt


Commit: 9f25c67c6d19bc64f63da8473929ec6e02501985
    https://github.com/scummvm/scummvm/commit/9f25c67c6d19bc64f63da8473929ec6e02501985
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-04-07T01:11:14+02:00

Commit Message:
BOLT: CARNIVAL: Final cleanup

Changed paths:
  R engines/bolt/console.cpp
  R engines/bolt/console.h
    engines/bolt/anim.cpp
    engines/bolt/av.cpp
    engines/bolt/barker.cpp
    engines/bolt/bolt.cpp
    engines/bolt/bolt.h
    engines/bolt/booth.cpp
    engines/bolt/booths/fred.cpp
    engines/bolt/booths/george.cpp
    engines/bolt/booths/huck.cpp
    engines/bolt/booths/scooby.cpp
    engines/bolt/booths/topcat.cpp
    engines/bolt/booths/yogi.cpp
    engines/bolt/configure.engine
    engines/bolt/detection_tables.h
    engines/bolt/metaengine.cpp
    engines/bolt/module.mk
    engines/bolt/resource.cpp
    engines/bolt/rtf.cpp
    engines/bolt/ssprite.cpp
    engines/bolt/state.cpp
    engines/bolt/swap.cpp
    engines/bolt/xplib/cursor.cpp
    engines/bolt/xplib/display.cpp
    engines/bolt/xplib/events.cpp
    engines/bolt/xplib/palette.cpp
    engines/bolt/xplib/timer.cpp
    engines/bolt/xplib/xplib.cpp
    engines/bolt/xplib/xplib.h


diff --git a/engines/bolt/anim.cpp b/engines/bolt/anim.cpp
index 7d9b90fc830..b87b54b8eff 100644
--- a/engines/bolt/anim.cpp
+++ b/engines/bolt/anim.cpp
@@ -57,37 +57,37 @@ bool BoltEngine::maintainAudioPlay(int16 mode) {
 bool BoltEngine::initAnim(RTFResource *rtf, int16 animIndex) {
 	int32 bufSize;
 
-	g_animPrevInactivityTimer = _xp->setInactivityTimer(0);
+	_animPrevInactivityTimer = _xp->setInactivityTimer(0);
 
 	// Try 80KB ring buffer, fall back to 40KB
 	bufSize = 0x14000;
-	g_animRingBuffer = (byte *)_xp->tryAllocMem(bufSize);
-	if (!g_animRingBuffer) {
+	_animRingBuffer = (byte *)_xp->tryAllocMem(bufSize);
+	if (!_animRingBuffer) {
 		bufSize = 0xA000;
-		g_animRingBuffer = (byte *)_xp->allocMem(bufSize);
+		_animRingBuffer = (byte *)_xp->allocMem(bufSize);
 	}
 
-	if (!g_animRingBuffer)
+	if (!_animRingBuffer)
 		return false;
 
-	if (!playRTF(rtf, animIndex, g_animRingBuffer, bufSize))
+	if (!playRTF(rtf, animIndex, _animRingBuffer, bufSize))
 		return false;
 
 	return true;
 }
 
 void BoltEngine::cleanUpAnim() {
-	if (g_animRingBuffer) {
-		_xp->freeMem(g_animRingBuffer);
-		g_animRingBuffer = nullptr;
+	if (_animRingBuffer) {
+		_xp->freeMem(_animRingBuffer);
+		_animRingBuffer = nullptr;
 	}
 
-	if (g_animFileHandle) {
-		_xp->closeFile(g_animFileHandle);
-		g_animFileHandle = nullptr;
+	if (_animFileHandle) {
+		_xp->closeFile(_animFileHandle);
+		_animFileHandle = nullptr;
 	}
 
-	_xp->setInactivityTimer(g_animPrevInactivityTimer);
+	_xp->setInactivityTimer(_animPrevInactivityTimer);
 }
 
 } // End of namespace Bolt
diff --git a/engines/bolt/av.cpp b/engines/bolt/av.cpp
index 710d13fd6d2..eb6eceb9be7 100644
--- a/engines/bolt/av.cpp
+++ b/engines/bolt/av.cpp
@@ -24,7 +24,7 @@
 namespace Bolt {
 
 void BoltEngine::setAVBufferSize(uint32 bufSize) {
-	g_avTargetBufSize = bufSize;
+	_avTargetBufSize = bufSize;
 }
 
 bool BoltEngine::prepareAV(RTFResource *rtfHandle, int16 animIndex, int16 width, int16 height, int16 xOff, int16 yOff) {
@@ -61,7 +61,7 @@ bool BoltEngine::playAV(RTFResource *rtfHandle, int16 animIndex, int16 width, in
 	int16 eventType = 0;
 
 	// Init if not already loaded...
-	if (!g_avRingBuffer) {
+	if (!_avRingBuffer) {
 		if (!initAV(rtfHandle, animIndex, width, height, xOff, yOff)) {
 			cleanUpAV();
 			return false;
@@ -113,17 +113,17 @@ void BoltEngine::processPacket(RTFPacket *packet) {
 		int16 frameRate = packet->frameRate;
 
 		// Skip this frame if: low framerate, only 1 behind, or haven't accumulated enough...
-		if (frameRate <= 10 || packet->skipCount > 1 || g_avFrameAccum < g_avSkipLevel) {
-			g_avFrameAccum += packet->duration;
+		if (frameRate <= 10 || packet->skipCount > 1 || _avFrameAccum < _avSkipLevel) {
+			_avFrameAccum += packet->duration;
 			debug(4, "BoltEngine::processPacket(): Skipping frame...");
 			return;
 		}
 
 		// Adjust skip level based on how far behind we are...
-		if (g_avFrameAccum > g_avSkipLevel)
-			g_avSkipLevel = (g_avFrameAccum > 4) ? 4 : g_avFrameAccum;
+		if (_avFrameAccum > _avSkipLevel)
+			_avSkipLevel = (_avFrameAccum > 4) ? 4 : _avFrameAccum;
 
-		g_avFrameAccum = 0;
+		_avFrameAccum = 0;
 
 		int16 targetSkip;
 		if (frameRate <= 12)
@@ -136,17 +136,17 @@ void BoltEngine::processPacket(RTFPacket *packet) {
 			targetSkip = 1;
 
 		// If keeping up fine, stop skipping entirely...
-		if (g_avSkipLevel <= 1 && targetSkip == 1 && packet->skipCount == 0) {
-			g_avSkipLevel = 0;
+		if (_avSkipLevel <= 1 && targetSkip == 1 && packet->skipCount == 0) {
+			_avSkipLevel = 0;
 		} else {
 			// Ramp toward target skip level...
-			if (targetSkip > g_avSkipLevel)
-				g_avSkipLevel = targetSkip;
-			else if (targetSkip < g_avSkipLevel)
-				g_avSkipLevel--;
+			if (targetSkip > _avSkipLevel)
+				_avSkipLevel = targetSkip;
+			else if (targetSkip < _avSkipLevel)
+				_avSkipLevel--;
 		}
 	} else if (tag == MKTAG('r', 'l', '7', ' ') || tag == MKTAG('r', 'l', '7', 'f')) {
-		g_avFrameAccum = 0;
+		_avFrameAccum = 0;
 	}
 
 	// Process frame, palette and trigger packets...
@@ -171,9 +171,9 @@ void BoltEngine::processRL7(RTFPacket *packet) {
 	uint32 tag = packet->tag;
 
 	if (tag == MKTAG('R', 'L', '7', 'B')) {
-		displayDesc = &g_avBackBufDesc;
+		displayDesc = &_avBackBufDesc;
 	} else {
-		displayDesc = &g_avFrontBufDesc;
+		displayDesc = &_avFrontBufDesc;
 	}
 
 	switch (tag) {
@@ -202,19 +202,19 @@ void BoltEngine::processRL7(RTFPacket *packet) {
 
 	displayDesc->pixelData = packet->dataPtr;
 
-	_xp->displayPic(displayDesc, g_avDisplayX, g_avDisplayY, page);
+	_xp->displayPic(displayDesc, _avDisplayX, _avDisplayY, page);
 
 	if (updateDisplay)
 		_xp->updateDisplay();
 
-	g_avFrontBufDesc.palette = nullptr;
-	g_avFrontBufDesc.paletteStart = 0;
-	g_avFrontBufDesc.paletteCount = 0;
+	_avFrontBufDesc.palette = nullptr;
+	_avFrontBufDesc.paletteStart = 0;
+	_avFrontBufDesc.paletteCount = 0;
 
 	if (transparency) {
-		g_avBackBufDesc.palette = nullptr;
-		g_avBackBufDesc.paletteStart = 0;
-		g_avBackBufDesc.paletteCount = 0;
+		_avBackBufDesc.palette = nullptr;
+		_avBackBufDesc.paletteStart = 0;
+		_avBackBufDesc.paletteCount = 0;
 	}
 }
 
@@ -228,11 +228,11 @@ void BoltEngine::processPLTE(RTFPacket *packet) {
 	byte *palBuffer;
 
 	if (packet->tag == MKTAG('P', 'L', 'T', 'B')) {
-		desc = &g_avBackBufDesc;
-		palBuffer = g_avFrontPalette;
+		desc = &_avBackBufDesc;
+		palBuffer = _avFrontPalette;
 	} else {
-		desc = &g_avFrontBufDesc;
-		palBuffer = g_avBackPalette;
+		desc = &_avFrontBufDesc;
+		palBuffer = _avBackPalette;
 	}
 
 	desc->paletteStart = startIndex;
@@ -249,83 +249,83 @@ void BoltEngine::processPLTE(RTFPacket *packet) {
 }
 
 bool BoltEngine::initAV(RTFResource *rtfHandle, int16 animIndex, int16 width, int16 height, int16 xOff, int16 yOff) {
-	uint32 savedAllocSize = g_avTargetBufSize;
+	uint32 savedAllocSize = _avTargetBufSize;
 	uint32 allocSize = 0;
 
-	g_avTargetBufSize = 0xFA000; // ~1MB
+	_avTargetBufSize = 0xFA000; // ~1MB
 
-	g_avDisplayY = yOff;
-	g_avDisplayX = xOff;
+	_avDisplayY = yOff;
+	_avDisplayX = xOff;
 
-	g_avSavedInactivityTimer = _xp->setInactivityTimer(0);
-	g_avSavedScreenSaverTimer = _xp->setScreenSaverTimer(0);
+	_avSavedInactivityTimer = _xp->setInactivityTimer(0);
+	_avSavedScreenSaverTimer = _xp->setScreenSaverTimer(0);
 
-	g_avBackPalette = nullptr;
-	g_avFrontPalette = nullptr;
-	g_avFrameAccum = 0;
+	_avBackPalette = nullptr;
+	_avFrontPalette = nullptr;
+	_avFrameAccum = 0;
 
 	// Allocate two 384-byte palette buffers (128 RGB triplets each)
-	g_avBackPalette = (byte *)_xp->allocMem(128 * 3);
-	if (!g_avBackPalette)
+	_avBackPalette = (byte *)_xp->allocMem(128 * 3);
+	if (!_avBackPalette)
 		return false;
 
-	g_avFrontPalette = (byte *)_xp->allocMem(128 * 3);
-	if (!g_avFrontPalette)
+	_avFrontPalette = (byte *)_xp->allocMem(128 * 3);
+	if (!_avFrontPalette)
 		return false;
 
 	// Allocate ring buffer: try saved size, shrink by 50KB until 300KB minimum
 	allocSize = savedAllocSize;
 	do {
-		g_avRingBuffer = (byte *)_xp->tryAllocMem(allocSize);
-		if (g_avRingBuffer)
+		_avRingBuffer = (byte *)_xp->tryAllocMem(allocSize);
+		if (_avRingBuffer)
 			break;
 
 		allocSize -= 0xC800;
 	} while (allocSize >= 0x4B000);
 
-	if (!g_avRingBuffer) {
+	if (!_avRingBuffer) {
 		error("BoltEngine::initAV(): Not enough memory");
 		return false;
 	}
 
-	g_avBackBufDesc.width = width;
-	g_avFrontBufDesc.width = width;
-	g_avBackBufDesc.height = height;
-	g_avFrontBufDesc.height = height;
-	g_avBackBufDesc.flags = 1;
-	g_avFrontBufDesc.flags = 1;
-	g_avBackBufDesc.palette = nullptr;
-	g_avFrontBufDesc.palette = nullptr;
-	g_avBackBufDesc.paletteStart = 0;
-	g_avFrontBufDesc.paletteStart = 0;
-	g_avBackBufDesc.paletteCount = 0;
-	g_avFrontBufDesc.paletteCount = 0;
+	_avBackBufDesc.width = width;
+	_avFrontBufDesc.width = width;
+	_avBackBufDesc.height = height;
+	_avFrontBufDesc.height = height;
+	_avBackBufDesc.flags = 1;
+	_avFrontBufDesc.flags = 1;
+	_avBackBufDesc.palette = nullptr;
+	_avFrontBufDesc.palette = nullptr;
+	_avBackBufDesc.paletteStart = 0;
+	_avFrontBufDesc.paletteStart = 0;
+	_avBackBufDesc.paletteCount = 0;
+	_avFrontBufDesc.paletteCount = 0;
 
 	// Open and start streaming!
-	if (!playRTF(rtfHandle, animIndex, g_avRingBuffer, allocSize))
+	if (!playRTF(rtfHandle, animIndex, _avRingBuffer, allocSize))
 		return false;
 
 	return true;
 }
 
 void BoltEngine::cleanUpAV() {
-	if (g_avRingBuffer) {
-		_xp->freeMem(g_avRingBuffer);
-		g_avRingBuffer = nullptr;
+	if (_avRingBuffer) {
+		_xp->freeMem(_avRingBuffer);
+		_avRingBuffer = nullptr;
 	}
 
-	if (g_avFrontPalette) {
-		_xp->freeMem(g_avFrontPalette);
-		g_avFrontPalette = nullptr;
+	if (_avFrontPalette) {
+		_xp->freeMem(_avFrontPalette);
+		_avFrontPalette = nullptr;
 	}
 
-	if (g_avBackPalette) {
-		_xp->freeMem(g_avBackPalette);
-		g_avBackPalette = nullptr;
+	if (_avBackPalette) {
+		_xp->freeMem(_avBackPalette);
+		_avBackPalette = nullptr;
 	}
 
-	_xp->setInactivityTimer(g_avSavedInactivityTimer);
-	_xp->setScreenSaverTimer(g_avSavedScreenSaverTimer);
+	_xp->setInactivityTimer(_avSavedInactivityTimer);
+	_xp->setScreenSaverTimer(_avSavedScreenSaverTimer);
 }
 
 } // End of namespace Bolt
diff --git a/engines/bolt/barker.cpp b/engines/bolt/barker.cpp
index 2fa68c1f179..f9d355c70a6 100644
--- a/engines/bolt/barker.cpp
+++ b/engines/bolt/barker.cpp
@@ -44,10 +44,10 @@ BarkerTable *BoltEngine::createBarker(int16 minIndex, int16 maxIndex) {
 	}
 
 	if (success) {
-		g_curErrorCode = 0;
+		_curErrorCode = 0;
 		return table;
 	} else {
-		g_curErrorCode = 1;
+		_curErrorCode = 1;
 		return nullptr;
 	}
 }
@@ -64,13 +64,13 @@ void BoltEngine::freeBarker(BarkerTable *table) {
 
 bool BoltEngine::registerSideShow(BarkerTable *table, SideShowHandler handler, int16 boothId) {
 	if (table->minIndex > boothId || table->maxIndex < boothId) {
-		g_curErrorCode = 5;
+		_curErrorCode = 5;
 		return false;
 	}
 
 	table->handlers[boothId - table->minIndex] = handler;
 
-	g_curErrorCode = 0;
+	_curErrorCode = 0;
 	return true;
 }
 
@@ -90,11 +90,11 @@ int16 BoltEngine::barker(BarkerTable *table, int16 startBooth) {
 		currentBooth = nextBooth;
 	}
 
-	return g_currentBoothId;
+	return _currentBoothId;
 }
 
 bool BoltEngine::checkError() {
-	return g_curErrorCode != 0;
+	return _curErrorCode != 0;
 }
 
 } // End of namespace Bolt
diff --git a/engines/bolt/bolt.cpp b/engines/bolt/bolt.cpp
index 76a04e3e052..76078ea854d 100644
--- a/engines/bolt/bolt.cpp
+++ b/engines/bolt/bolt.cpp
@@ -20,9 +20,9 @@
  */
 
 #include "bolt/bolt.h"
-#include "graphics/framelimiter.h"
 #include "bolt/detection.h"
-#include "bolt/console.h"
+#include "bolt/xplib/xplib.h"
+
 #include "common/scummsys.h"
 #include "common/config-manager.h"
 #include "common/debug-channels.h"
@@ -31,8 +31,6 @@
 #include "engines/util.h"
 #include "graphics/paletteman.h"
 
-#include "bolt/xplib/xplib.h"
-
 namespace Bolt {
 
 BoltEngine *g_engine;
@@ -60,21 +58,18 @@ Common::String BoltEngine::getGameId() const {
 Common::Error BoltEngine::run() {
 	ConfMan.registerDefault("extended_viewport", false);
 	if (ConfMan.hasKey("extended_viewport", _targetName)) {
-		g_extendedViewport = ConfMan.getBool("extended_viewport");
+		_extendedViewport = ConfMan.getBool("extended_viewport");
 	}
 
 	// Initialize paletted graphics mode
-	if (!g_extendedViewport) {
+	if (!_extendedViewport) {
 		initGraphics(SCREEN_WIDTH, SCREEN_HEIGHT);
 	} else {
 		initGraphics(EXTENDED_SCREEN_WIDTH, EXTENDED_SCREEN_HEIGHT);
 	}
 
 	if ((getFeatures() & ADGF_DEMO) != 0)
-		g_isDemo = true;
-
-	// Set the engine's debugger console
-	setDebugger(new Console());
+		_isDemo = true;
 
 	_xp->initialize();
 	boltMain();
@@ -83,17 +78,6 @@ Common::Error BoltEngine::run() {
 	return Common::kNoError;
 }
 
-Common::Error BoltEngine::syncGame(Common::Serializer &s) {
-	// The Serializer has methods isLoading() and isSaving()
-	// if you need to specific steps; for example setting
-	// an array size after reading it's length, whereas
-	// for saving it would write the existing array's length
-	int dummy = 0;
-	s.syncAsUint32LE(dummy);
-
-	return Common::kNoError;
-}
-
 void BoltEngine::boltMain() {
 	byte *testAlloc;
 	BarkerTable *barkerTable;
@@ -108,56 +92,56 @@ void BoltEngine::boltMain() {
 	_xp->randomize();
 
 	if (allocResourceIndex()) {
-		g_boothsBoltLib = nullptr;
+		_boothsBoltLib = nullptr;
 
-		if (openBOLTLib(&g_boothsBoltLib, &g_boothsBoltCallbacks, assetPath("booths.blt"))) {
-			int16 chosenSpecId = g_extendedViewport ? 0 : 1;
+		if (openBOLTLib(&_boothsBoltLib, &_boothsBoltCallbacks, assetPath("booths.blt"))) {
+			int16 chosenSpecId = _extendedViewport ? 0 : 1;
 
-			if (_xp->setDisplaySpec(&g_displayMode, &g_displaySpecs[chosenSpecId])) {
-				assert(g_displayMode >= 0);
-				g_displayWidth = g_displaySpecs[g_displayMode].width;
-				g_displayHeight = g_displaySpecs[g_displayMode].height;
+			if (_xp->setDisplaySpec(&_displayMode, &_displaySpecs[chosenSpecId])) {
+				assert(_displayMode >= 0);
+				_displayWidth = _displaySpecs[_displayMode].width;
+				_displayHeight = _displaySpecs[_displayMode].height;
 
 				// Center within 384x240 virtual coordinate space...
-				g_displayX = (EXTENDED_SCREEN_WIDTH - g_displayWidth) / 2;
-				g_displayY = (EXTENDED_SCREEN_HEIGHT - g_displayHeight) / 2;
+				_displayX = (EXTENDED_SCREEN_WIDTH - _displayWidth) / 2;
+				_displayY = (EXTENDED_SCREEN_HEIGHT - _displayHeight) / 2;
 
-				_xp->setCoordSpec(g_displayX, g_displayY, g_displayWidth, g_displayHeight);
+				_xp->setCoordSpec(_displayX, _displayY, _displayWidth, _displayHeight);
 				
-				if (g_displayMode == 0) {
-					g_rtfHandle = openRTF(assetPath("cc_og.av"));
+				if (_displayMode == 0) {
+					_rtfHandle = openRTF(assetPath("cc_og.av"));
 				} else {
-					g_rtfHandle = openRTF(assetPath("cc_cr.av"));
+					_rtfHandle = openRTF(assetPath("cc_cr.av"));
 				}
 
-				if (g_rtfHandle) {
-					int16 boothGroupBase = g_isDemo ? 0x0E00 : 0x1700;
+				if (_rtfHandle) {
+					int16 boothGroupBase = _isDemo ? 0x0E00 : 0x1700;
 
-					if (!g_isDemo) {
-						playAV(g_rtfHandle, 0, g_displayWidth, g_displayHeight, g_displayX, g_displayY);
+					if (!_isDemo) {
+						playAV(_rtfHandle, 0, _displayWidth, _displayHeight, _displayX, _displayY);
 					}
 
-					boothSprite = getBOLTMember(g_boothsBoltLib, (g_displayMode != 0) ? (boothGroupBase + 1) : (boothGroupBase + 2));
+					boothSprite = getBOLTMember(_boothsBoltLib, (_displayMode != 0) ? (boothGroupBase + 1) : (boothGroupBase + 2));
 
 					_xp->setTransparency(false);
 
-					displayColors(getBOLTMember(g_boothsBoltLib, boothGroupBase), stFront, 0);
-					displayPic(boothSprite, g_displayX, g_displayY, stFront);
+					displayColors(getBOLTMember(_boothsBoltLib, boothGroupBase), stFront, 0);
+					displayPic(boothSprite, _displayX, _displayY, stFront);
 
 					_xp->updateDisplay();
 
-					displayColors(getBOLTMember(g_boothsBoltLib, boothGroupBase), stBack, 0);
-					displayPic(boothSprite, g_displayX, g_displayY, stBack);
+					displayColors(getBOLTMember(_boothsBoltLib, boothGroupBase), stBack, 0);
+					displayPic(boothSprite, _displayX, _displayY, stBack);
 
-					playAV(g_rtfHandle, g_isDemo ? 0 : 2, g_displayWidth, g_displayHeight, g_displayX, g_displayY);
+					playAV(_rtfHandle, _isDemo ? 0 : 2, _displayWidth, _displayHeight, _displayX, _displayY);
 
-					freeBOLTGroup(g_boothsBoltLib, boothGroupBase, 1);
+					freeBOLTGroup(_boothsBoltLib, boothGroupBase, 1);
 
-					if (getBOLTGroup(g_boothsBoltLib, 0, 1)) {
-						setCursorPict(memberAddr(g_boothsBoltLib, 0));
+					if (getBOLTGroup(_boothsBoltLib, 0, 1)) {
+						setCursorPict(memberAddr(_boothsBoltLib, 0));
 
 						if (initVRam(1500)) {
-							barkerTable = createBarker(2, g_isDemo ? 19 : 17);
+							barkerTable = createBarker(2, _isDemo ? 19 : 17);
 							if (barkerTable) {
 								// Register booth handlers...
 								registerSideShow(barkerTable, &BoltEngine::hucksBooth, 3);
@@ -175,12 +159,12 @@ void BoltEngine::boltMain() {
 								registerSideShow(barkerTable, &BoltEngine::topCatGame, 15);
 								registerSideShow(barkerTable, &BoltEngine::winALetter, 16);
 
-								if (g_isDemo) {
+								if (_isDemo) {
 									registerSideShow(barkerTable, &BoltEngine::displayDemoPict, 17);
 									registerSideShow(barkerTable, &BoltEngine::endDemo, 18);
 								}
 
-								g_lettersWon = 0;
+								_lettersWon = 0;
 								_xp->setScreenSaverTimer(1800);
 
 								// The barker function runs the main loop, starting at mainEntrance()...
@@ -196,13 +180,13 @@ void BoltEngine::boltMain() {
 		}
 	}
 
-	freeBOLTGroup(g_boothsBoltLib, 0, 1);
+	freeBOLTGroup(_boothsBoltLib, 0, 1);
 
-	if (g_boothsBoltLib != 0)
-		closeBOLTLib(&g_boothsBoltLib);
+	if (_boothsBoltLib != 0)
+		closeBOLTLib(&_boothsBoltLib);
 
-	if (g_rtfHandle != nullptr)
-		closeRTF(g_rtfHandle);
+	if (_rtfHandle != nullptr)
+		closeRTF(_rtfHandle);
 
 	freeVRam();
 	freeResourceIndex();
@@ -237,9 +221,9 @@ int16 BoltEngine::displayDemoPict(int16 prevBooth) {
 	_xp->stopCycle();
 	_xp->setTransparency(false);
 
-	int16 memberId = (g_displayMode != 0) ? 0x1002 : 0x1001;
-	displayPic(getBOLTMember(g_boothsBoltLib, memberId), g_displayX, g_displayY, stFront);
-	displayColors(getBOLTMember(g_boothsBoltLib, 0x1000), stFront, 0);
+	int16 memberId = (_displayMode != 0) ? 0x1002 : 0x1001;
+	displayPic(getBOLTMember(_boothsBoltLib, memberId), _displayX, _displayY, stFront);
+	displayColors(getBOLTMember(_boothsBoltLib, 0x1000), stFront, 0);
 
 	_xp->updateDisplay();
 
diff --git a/engines/bolt/bolt.h b/engines/bolt/bolt.h
index c0cb0149957..2a2a1b9a5f7 100644
--- a/engines/bolt/bolt.h
+++ b/engines/bolt/bolt.h
@@ -567,17 +567,11 @@ public:
 	 */
 	Common::String getGameId() const;
 
-	/**
-	 * Gets a random number
-	 */
-	uint32 getRandomNumber(uint maxNum) {
-		return _randomSource.getRandomNumber(maxNum);
-	}
-
 	bool hasFeature(EngineFeature f) const override {
 		return (f == kSupportsReturnToLauncher);
 	};
 
+	// This engine doesn't save anything!
 	bool canLoadGameStateCurrently(Common::U32String *msg = nullptr) override {
 		return false;
 	}
@@ -585,30 +579,15 @@ public:
 		return false;
 	}
 
-	/**
-	 * Uses a serializer to allow implementing savegame
-	 * loading and saving using a single method
-	 */
-	Common::Error syncGame(Common::Serializer &s);
-
-	Common::Error saveGameStream(Common::WriteStream *stream, bool isAutosave = false) override {
-		Common::Serializer s(nullptr, stream);
-		return syncGame(s);
-	}
-	Common::Error loadGameStream(Common::SeekableReadStream *stream) override {
-		Common::Serializer s(stream, nullptr);
-		return syncGame(s);
-	}
-
 protected:
-	DisplaySpecs g_displaySpecs[2] = {
+	DisplaySpecs _displaySpecs[2] = {
 		{0, 384, 240},
 		{1, 320, 200}
 	};
 
 	XpLib *_xp = nullptr;
-	bool g_extendedViewport = false;
-	bool g_isDemo = false;
+	bool _extendedViewport = false;
+	bool _isDemo = false;
 
 	// xpMain
 	void boltMain();
@@ -651,56 +630,56 @@ protected:
 	int16 winALetter(int16 prevBooth);
 	int16 endDemo(int16 prevBooth);
 
-	int16 g_lettersWon = 0;
-	bool g_allLettersWonFlag = false;
-	int g_displayMode = 0;
-	int32 g_displayX = 0;
-	int32 g_displayY = 0;
-	int32 g_displayWidth = 0;
-	int32 g_displayHeight = 0;
-
-	int16 g_boothLoadedMask = 0;
-	int16 g_currentBoothScene = 0;
-	int16 g_boothNumHotspots = 0;
-	int16 g_boothNumAnimations = 0;
-	byte *g_boothSceneDesc = nullptr;
-	byte *g_boothHotPalDescs[4];
-	Common::Rect g_boothHotspotDescs[8]; 
-	byte *g_boothAnimDescs[7];
-	byte *g_boothAnimPalDescs[4];
-	byte g_savedPalettes[7][30];
-	byte g_savedHotPalettes[4][9];
-	byte *g_boothPalCycleData = nullptr;
-	XPPicDesc g_boothLetterSprite;
-	bool g_needInitCursorPos = true;
-	byte *g_boothVisitSignOn = nullptr;
-	byte *g_boothVisitSignOff = nullptr;
-	byte g_leftDoorNavTable[3] = {3, 2, 4};
-	byte g_rightDoorNavTable[3] = {1, 0, 5};
-
-	int16 g_cursorX = 0;
-	int16 g_cursorY = 0;
-	int16 g_hoveredHotspot = 0;
-	uint32 g_helpTimer = 0;
-	int16 g_keyReleased = 0;
-	int16 g_keyLeft = 0;
-	int16 g_keyRight = 0;
-	int16 g_keyUp = 0;
-	int16 g_keyDown = 0;
-	int16 g_helpFlag = 0;
-	int16 g_keyEnter = 0;
-
-	int16 g_tourStep = 0;
-	int16 g_helpPlaying = 0;
-	int16 g_helpIsIdle = 0;
-	int16 g_idleHelpAudioAvailable = 1;
-
-	int16 g_huckWins = 0;
-	int16 g_fredWins = 0;
-	int16 g_scoobyWins = 0;
-	int16 g_yogiWins = 0;
-	int16 g_georgeWins = 0;
-	int16 g_topCatWins = 0;
+	int16 _lettersWon = 0;
+	bool _allLettersWonFlag = false;
+	int _displayMode = 0;
+	int32 _displayX = 0;
+	int32 _displayY = 0;
+	int32 _displayWidth = 0;
+	int32 _displayHeight = 0;
+
+	int16 _boothLoadedMask = 0;
+	int16 _currentBoothScene = 0;
+	int16 _boothNumHotspots = 0;
+	int16 _boothNumAnimations = 0;
+	byte *_boothSceneDesc = nullptr;
+	byte *_boothHotPalDescs[4];
+	Common::Rect _boothHotspotDescs[8]; 
+	byte *_boothAnimDescs[7];
+	byte *_boothAnimPalDescs[4];
+	byte _savedPalettes[7][30];
+	byte _savedHotPalettes[4][9];
+	byte *_boothPalCycleData = nullptr;
+	XPPicDesc _boothLetterSprite;
+	bool _needInitCursorPos = true;
+	byte *_boothVisitSignOn = nullptr;
+	byte *_boothVisitSignOff = nullptr;
+	byte _leftDoorNavTable[3]  = { 3, 2, 4 };
+	byte _rightDoorNavTable[3] = { 1, 0, 5 };
+
+	int16 _cursorX = 0;
+	int16 _cursorY = 0;
+	int16 _hoveredHotspot = 0;
+	uint32 _helpTimer = 0;
+	int16 _keyReleased = 0;
+	int16 _keyLeft = 0;
+	int16 _keyRight = 0;
+	int16 _keyUp = 0;
+	int16 _keyDown = 0;
+	int16 _helpFlag = 0;
+	int16 _keyEnter = 0;
+
+	int16 _tourStep = 0;
+	int16 _helpPlaying = 0;
+	int16 _helpIsIdle = 0;
+	int16 _idleHelpAudioAvailable = 1;
+
+	int16 _huckWins = 0;
+	int16 _fredWins = 0;
+	int16 _scoobyWins = 0;
+	int16 _yogiWins = 0;
+	int16 _georgeWins = 0;
+	int16 _topCatWins = 0;
 
 	// Barker
 	BarkerTable *createBarker(int16 minIndex, int16 maxIndex);
@@ -709,8 +688,8 @@ protected:
 	int16 barker(BarkerTable *table, int16 startBooth);
 	bool checkError();
 
-	int32 g_curErrorCode = 0;
-	int16 g_currentBoothId = 0;
+	int32 _curErrorCode = 0;
+	int16 _currentBoothId = 0;
 
 	// Utils
 	void displayColors(byte *palette, int16 page, int16 flags);
@@ -758,15 +737,15 @@ protected:
 	void swapAllWords();
 	void swapAllLongs();
 
-	BOLTLib *g_boothsBoltLib = nullptr;
-	BOLTCallbacks g_boothsBoltCallbacks;
+	BOLTLib *_boothsBoltLib = nullptr;
+	BOLTCallbacks _boothsBoltCallbacks;
 
-	static BOLTCallback g_defaultTypeLoadCallbacks[25];
-	static BOLTCallback g_defaultTypeFreeCallbacks[25];
-	static BOLTCallback g_defaultMemberLoadCallbacks[25];
-	static BOLTCallback g_defaultMemberFreeCallbacks[25];
-	static BOLTCallback g_defaultGroupLoadCallbacks[25];
-	static BOLTCallback g_defaultGroupFreeCallbacks[25];
+	static BOLTCallback _defaultTypeLoadCallbacks[25];
+	static BOLTCallback _defaultTypeFreeCallbacks[25];
+	static BOLTCallback _defaultMemberLoadCallbacks[25];
+	static BOLTCallback _defaultMemberFreeCallbacks[25];
+	static BOLTCallback _defaultGroupLoadCallbacks[25];
+	static BOLTCallback _defaultGroupFreeCallbacks[25];
 
 	static void noOpCb();
 
@@ -782,20 +761,20 @@ protected:
 
 	void initCallbacks();
 
-	int16 g_resourceIndexCount = 1000;
-	uint32 **g_resourceIndex = nullptr;
-	Common::Array<byte *> g_resolvedPtrs;
-	Common::File *g_cachedFileHandle = nullptr;
-	uint32 g_cachedFilePos = 0xFFFFFFFF;
-	BOLTLib *g_boltCurrentLib = nullptr;
-	BOLTHeader g_boltFileHeader;
-	BOLTGroupEntry *g_boltCurrentGroupEntry = nullptr;
-	int16 g_boltLoadDepth = 0;
-	byte *g_boltRawMemberData = nullptr;
-	BOLTMemberEntry *g_boltCurrentMemberEntry = nullptr;
-	int16 g_pendingFixupCount = 0;
-
-	// Game levelNumber
+	int16 _resourceIndexCount = 1000;
+	uint32 **_resourceIndex = nullptr;
+	Common::Array<byte *> _resolvedPtrs;
+	Common::File *_cachedFileHandle = nullptr;
+	uint32 _cachedFilePos = 0xFFFFFFFF;
+	BOLTLib *_boltCurrentLib = nullptr;
+	BOLTHeader _boltFileHeader;
+	BOLTGroupEntry *_boltCurrentGroupEntry = nullptr;
+	int16 _boltLoadDepth = 0;
+	byte *_boltRawMemberData = nullptr;
+	BOLTMemberEntry *_boltCurrentMemberEntry = nullptr;
+	int16 _pendingFixupCount = 0;
+
+	// Game state
 	bool initVRam(int16 poolSize);
 	void freeVRam();
 	bool vLoad(void *dest, const char *name);
@@ -805,10 +784,10 @@ protected:
 	uint16 dataSize(int16 recordOffset);
 	bool findRecord(const char *name, int16 *outOffset);
 
-	int32 g_vramRecordCount = 0;
-	int32 g_vramUsedBytes = 0;
-	byte *g_allocatedMemPool = nullptr;
-	uint32 g_allocatedMemPoolSize = 0;
+	int32 _vramRecordCount = 0;
+	int32 _vramUsedBytes = 0;
+	byte *_allocatedMemPool = nullptr;
+	uint32 _allocatedMemPoolSize = 0;
 
 	// RTF
 	RTFResource *openRTF(const char *fileName);
@@ -828,33 +807,33 @@ protected:
 	void resetPlaybackState();
 	void setAVBufferSize(uint32 bufSize);
 
-	RTFResource *g_rtfHandle = nullptr;
-	Common::File *g_rtfFileHandle = nullptr;
-	uint32 g_rtfChunkRemaining = 0;
-	bool g_rtfMidChunk = false;
-	RTFPacket *g_rtfCurrentPacket = nullptr;
-	byte *g_ringBufBase = nullptr;
-	byte *g_ringBufWritePtr = nullptr;
-	uint32 g_ringBufSize = 0;
-	uint32 g_ringBufFreeSpace = 0;
-	uint32 g_ringBufLowWater = 0;
-	uint32 g_ringBufHighWater = 0;
-	uint32 g_ringBufUsed = 0;
-	bool g_rtfSoundActive = false;
-	int16 g_rtfPlaybackTime = 0;
-	int16 g_rtfCumulativeTime = 0;
-	RTFPacket *g_rtfPendingFrame = nullptr;
-	RTFPacket *g_rtfSoundQueueHead = nullptr;
-	RTFPacket *g_rtfSoundPlayHead = nullptr;
-	RTFPacket *g_rtfChunkListTail = nullptr;
-	RTFPacket *g_rtfChunkListHead = nullptr;
-	int16 g_rtfChunkCount = 0;
-	int16 g_rtfQueuedSoundCount = 0;
-	int16 g_rtfSoundTiming = 0;
-	uint32 g_rtfAnimStartOffset = 0;
-	bool g_rtfNeedInitialFill = false;
-	uint32 g_rtfChunkTag = 0;
-	uint32 g_rtfChunkSize = 0;
+	RTFResource *_rtfHandle = nullptr;
+	Common::File *_rtfFileHandle = nullptr;
+	uint32 _rtfChunkRemaining = 0;
+	bool _rtfMidChunk = false;
+	RTFPacket *_rtfCurrentPacket = nullptr;
+	byte *_ringBufBase = nullptr;
+	byte *_ringBufWritePtr = nullptr;
+	uint32 _ringBufSize = 0;
+	uint32 _ringBufFreeSpace = 0;
+	uint32 _ringBufLowWater = 0;
+	uint32 _ringBufHighWater = 0;
+	uint32 _ringBufUsed = 0;
+	bool _rtfSoundActive = false;
+	int16 _rtfPlaybackTime = 0;
+	int16 _rtfCumulativeTime = 0;
+	RTFPacket *_rtfPendingFrame = nullptr;
+	RTFPacket *_rtfSoundQueueHead = nullptr;
+	RTFPacket *_rtfSoundPlayHead = nullptr;
+	RTFPacket *_rtfChunkListTail = nullptr;
+	RTFPacket *_rtfChunkListHead = nullptr;
+	int16 _rtfChunkCount = 0;
+	int16 _rtfQueuedSoundCount = 0;
+	int16 _rtfSoundTiming = 0;
+	uint32 _rtfAnimStartOffset = 0;
+	bool _rtfNeedInitialFill = false;
+	uint32 _rtfChunkTag = 0;
+	uint32 _rtfChunkSize = 0;
 
 	// AV
 	bool prepareAV(RTFResource *rtfHandle, int16 animIndex, int16 width, int16 height, int16 xOff, int16 yOff);
@@ -867,19 +846,19 @@ protected:
 	bool initAV(RTFResource *rtfHandle, int16 animIndex, int16 width, int16 height, int16 xOff, int16 yOff);
 	void cleanUpAV();
 
-	byte *g_avRingBuffer = nullptr;
-	uint32 g_avTargetBufSize = 0x0FA000;
-	int16 g_avSkipLevel = 0;
-	byte *g_avFrontPalette = nullptr;
-	byte *g_avBackPalette = nullptr;
-	uint32 g_avSavedInactivityTimer = 0;
-	uint32 g_avSavedScreenSaverTimer = 0;
-	int16 g_avFrameAccum = 0;
-	int16 g_avDisplayX = 0;
-	int16 g_avDisplayY = 0;
+	byte *_avRingBuffer = nullptr;
+	uint32 _avTargetBufSize = 0x0FA000;
+	int16 _avSkipLevel = 0;
+	byte *_avFrontPalette = nullptr;
+	byte *_avBackPalette = nullptr;
+	uint32 _avSavedInactivityTimer = 0;
+	uint32 _avSavedScreenSaverTimer = 0;
+	int16 _avFrameAccum = 0;
+	int16 _avDisplayX = 0;
+	int16 _avDisplayY = 0;
 
-	XPPicDesc g_avFrontBufDesc;
-	XPPicDesc g_avBackBufDesc;
+	XPPicDesc _avFrontBufDesc;
+	XPPicDesc _avBackBufDesc;
 
 	// Anim
 	bool startAnimation(RTFResource *rtf, int16 animIndex);
@@ -888,9 +867,9 @@ protected:
 	bool initAnim(RTFResource *rtf, int16 animIndex);
 	void cleanUpAnim();
 
-	int16 g_animPrevInactivityTimer = 0;
-	byte *g_animRingBuffer = nullptr;
-	Common::File *g_animFileHandle = nullptr;
+	int16 _animPrevInactivityTimer = 0;
+	byte *_animRingBuffer = nullptr;
+	Common::File *_animFileHandle = nullptr;
 
 	// SSprite
 	void setUpSSprite(SSprite *sprite, int16 frameCount, byte **frameData, int16 frameRate, int16 velocityX, int16 velocityY);
@@ -922,18 +901,18 @@ protected:
 	void freezeSSprite(SSprite *sprite);
 	void unfreezeSSprite(SSprite *sprite);
 
-	int16 g_spriteCollTempX = 0;
-	int16 g_spriteCollTempY = 0;
-	int16 g_spriteCollTempW = 0;
-	int16 g_spriteCollTempH = 0;
-	int16 g_spriteScreenX = 0;
-	int16 g_spriteScreenY = 0;
-	int16 g_spriteCollTempA[4] = { 0 };
-	int16 g_spriteCollTempB[4] = { 0 };
-	int16 g_spriteScreenAX = 0;
-	int16 g_spriteScreenAY = 0;
-	int16 g_spriteScreenBX = 0;
-	int16 g_spriteScreenBY = 0;
+	int16 _spriteCollTempX = 0;
+	int16 _spriteCollTempY = 0;
+	int16 _spriteCollTempW = 0;
+	int16 _spriteCollTempH = 0;
+	int16 _spriteScreenX = 0;
+	int16 _spriteScreenY = 0;
+	int16 _spriteCollTempA[4] = { 0 };
+	int16 _spriteCollTempB[4] = { 0 };
+	int16 _spriteScreenAX = 0;
+	int16 _spriteScreenAY = 0;
+	int16 _spriteScreenBX = 0;
+	int16 _spriteScreenBY = 0;
 
 	// --- MINIGAMES ---
 
@@ -964,54 +943,52 @@ protected:
 	static void swapFredAnimDescCb();
 	static void swapFredLevelDescCb();
 
-	BOLTLib *g_fredBoltLib = nullptr;
-	BOLTCallbacks g_fredBoltCallbacks;
-
-	static BOLTCallback g_fredTypeLoadCallbacks[28];
-	static BOLTCallback g_fredTypeFreeCallbacks[28];
-
-	byte *g_fredLevelPtr = nullptr;
-	byte *g_fredBackground = nullptr;
-	byte *g_fredBalloonString = nullptr;
-	byte *g_fredPalette = nullptr;
-	byte *g_fredFacingLeftRect = nullptr;
-	byte *g_fredFacingRightRect = nullptr;
-	byte *g_fredTurningRect = nullptr;
-	byte *g_fredBalloonRect = nullptr;
-	byte *g_fredHelpEntries = nullptr;
-	byte *g_fredPlayButton = nullptr;
-	byte *g_fredSprites[10] = { nullptr };
-	FredEntityState **g_fredEntitiesTable = nullptr;
-
-	byte *g_fredRowBounds = nullptr;
-	byte *g_fredShuffleTable = nullptr;
-	byte *g_fredCycleRaw = nullptr;
-	byte *g_fredBalloonSprite = nullptr;
-	byte *g_fredPathMatrix = nullptr;
-	byte *g_fredCurrentHelpObject = nullptr;
-	byte *g_fredHoveredEntry = nullptr;
-
-	SoundInfo g_fredSounds[4];
-
-	SoundInfo *g_fredCurrentSound = nullptr;
-	SoundInfo *g_fredLoopSound = nullptr;
-	SoundInfo *g_fredPendingOneShot = nullptr;
-	SoundInfo *g_fredPendingLoop = nullptr;
-
-	FredEntityState g_fredSprite;
-
-	int16 g_fredSaveData[3] = { 0, 0, 0 };
-	int16 g_fredLevelIndex = 0;
-
-	int16 g_fredBalloonSpawnDelay = 0;
-	int16 g_fredBalloonSearchIdx = 0;
-	uint32 g_fredTimer = 0;
-	int16 g_fredHelpStep = -1;
-	int16 g_fredShowHelp = 1;
-
-	XPCycleState g_fredCycleSpecs[4];
-
-	const char *g_fredSaveFile = "FredBC";
+	BOLTLib *_fredBoltLib = nullptr;
+	BOLTCallbacks _fredBoltCallbacks;
+
+	static BOLTCallback _fredTypeLoadCallbacks[28];
+	static BOLTCallback _fredTypeFreeCallbacks[28];
+
+	byte *_fredLevelPtr = nullptr;
+	byte *_fredBackground = nullptr;
+	byte *_fredBalloonString = nullptr;
+	byte *_fredPalette = nullptr;
+	byte *_fredFacingLeftRect = nullptr;
+	byte *_fredFacingRightRect = nullptr;
+	byte *_fredTurningRect = nullptr;
+	byte *_fredBalloonRect = nullptr;
+	byte *_fredHelpEntries = nullptr;
+	byte *_fredPlayButton = nullptr;
+	byte *_fredSprites[10] = { nullptr };
+	FredEntityState **_fredEntitiesTable = nullptr;
+
+	byte *_fredRowBounds = nullptr;
+	byte *_fredShuffleTable = nullptr;
+	byte *_fredCycleRaw = nullptr;
+	byte *_fredBalloonSprite = nullptr;
+	byte *_fredPathMatrix = nullptr;
+	byte *_fredCurrentHelpObject = nullptr;
+	byte *_fredHoveredEntry = nullptr;
+
+	SoundInfo _fredSounds[4];
+
+	SoundInfo *_fredCurrentSound = nullptr;
+	SoundInfo *_fredLoopSound = nullptr;
+	SoundInfo *_fredPendingOneShot = nullptr;
+	SoundInfo *_fredPendingLoop = nullptr;
+
+	FredEntityState _fredSprite;
+
+	int16 _fredSaveData[3] = { 0, 0, 0 };
+	int16 _fredLevelIndex = 0;
+
+	int16 _fredBalloonSpawnDelay = 0;
+	int16 _fredBalloonSearchIdx = 0;
+	uint32 _fredTimer = 0;
+	int16 _fredHelpStep = -1;
+	int16 _fredShowHelp = 1;
+
+	XPCycleState _fredCycleSpecs[4];
 
 	// --- GEORGE ---
 	int16 georgeGame(int16 prevBooth);
@@ -1045,61 +1022,60 @@ protected:
 	static void swapGeorgeHelpEntryCb();
 	static void swapGeorgeThresholdsCb();
 
-	BOLTLib *g_georgeBoltLib = nullptr;
-	BOLTCallbacks g_georgeBoltCallbacks;
-
-	static BOLTCallback g_georgeTypeLoadCallbacks[28];
-	static BOLTCallback g_georgeTypeFreeCallbacks[28];
-
-	byte *g_georgePrevActiveHelpObject = nullptr;
-	byte *g_georgeActiveHelpObject = nullptr;
-	int16 g_georgeHelpStep = -1;
-	int16 g_georgeHelpActive = 1;
-	const char *g_georgeSaveFileName = "GeorgeBE";
-
-	GeorgeEntityState **g_georgeEntityList = nullptr;
-	int16 g_georgeCarIdx = 0;
-	int16 g_georgeNumSatellites = 0;
-	int16 g_georgeFirstAsteroidIdx = 0;
-	int16 g_georgeTotalSatellites = 0;
-	byte *g_georgeCarPics[3] = { nullptr };
-	byte *g_georgeHelpObjects = nullptr;
-	byte *g_georgeHelpSequence = nullptr;
-	uint32 g_georgeHelpTimer = 0;
-	byte *g_georgeSatelliteShuffleTable = nullptr;
-	byte *g_georgeAsteroidShuffleTable = nullptr;
-	int16 *g_georgeSatelliteThresholds = nullptr;
-	int16 *g_georgeAsteroidThresholds = nullptr;
-	byte *g_georgeSatellitePaths = nullptr;
-	byte *g_georgeAsteroidPaths = nullptr;
-	byte *g_georgeCollisionRect = nullptr;
-	byte *g_georgeSatelliteCollisionRects = nullptr;
-	byte *g_georgeAsteroidCollisionRects = nullptr;
-	byte *g_georgeSatelliteGfx = nullptr;
-	byte *g_georgeAsteroidGfx = nullptr;
-	int16 g_georgeSatelliteWait = 0;
-	int16 g_georgeAsteroidWait = 0;
-	int16 g_georgeSatelliteSearchIdx = 0;
-	int16 g_georgeAsteroidSearchIdx = 0;
-	int16 g_georgeHitSearchIdx = 0;
-	byte *g_georgeBgPic = nullptr;
-	byte *g_georgePalette = nullptr;
-	byte *g_georgePalCycleRawData = nullptr;
-	XPCycleState g_georgePalCycleSpecs[4];
-	int16 g_georgeCollectedSatellitesNum = 0;
-	SoundInfo g_georgeSoundCarStartUp;
-	SoundInfo g_georgeSoundCarGoesAway;
-	SoundInfo g_georgeSoundCarLoopHi;
-	SoundInfo g_georgeSoundCarLoopLo;
-	SoundInfo g_georgeSoundCarTumble;
-	SoundInfo **g_georgeSatelliteSoundList = nullptr;
-	byte g_georgeSoundChannelCounter = 0;
-	SoundInfo *g_georgeSoundToPlay = nullptr;
-	SoundInfo *g_georgeSoundCurrent = nullptr;
-	SoundInfo *g_georgeSoundQueued = nullptr;
-	SoundInfo *g_georgeSoundNext = nullptr;
-	int16 g_georgeSaveData[3] = { 0 };
-	int16 *g_georgeThresholds = nullptr;
+	BOLTLib *_georgeBoltLib = nullptr;
+	BOLTCallbacks _georgeBoltCallbacks;
+
+	static BOLTCallback _georgeTypeLoadCallbacks[28];
+	static BOLTCallback _georgeTypeFreeCallbacks[28];
+
+	byte *_georgePrevActiveHelpObject = nullptr;
+	byte *_georgeActiveHelpObject = nullptr;
+	int16 _georgeHelpStep = -1;
+	int16 _georgeHelpActive = 1;
+
+	GeorgeEntityState **_georgeEntityList = nullptr;
+	int16 _georgeCarIdx = 0;
+	int16 _georgeNumSatellites = 0;
+	int16 _georgeFirstAsteroidIdx = 0;
+	int16 _georgeTotalSatellites = 0;
+	byte *_georgeCarPics[3] = { nullptr };
+	byte *_georgeHelpObjects = nullptr;
+	byte *_georgeHelpSequence = nullptr;
+	uint32 _georgeHelpTimer = 0;
+	byte *_georgeSatelliteShuffleTable = nullptr;
+	byte *_georgeAsteroidShuffleTable = nullptr;
+	int16 *_georgeSatelliteThresholds = nullptr;
+	int16 *_georgeAsteroidThresholds = nullptr;
+	byte *_georgeSatellitePaths = nullptr;
+	byte *_georgeAsteroidPaths = nullptr;
+	byte *_georgeCollisionRect = nullptr;
+	byte *_georgeSatelliteCollisionRects = nullptr;
+	byte *_georgeAsteroidCollisionRects = nullptr;
+	byte *_georgeSatelliteGfx = nullptr;
+	byte *_georgeAsteroidGfx = nullptr;
+	int16 _georgeSatelliteWait = 0;
+	int16 _georgeAsteroidWait = 0;
+	int16 _georgeSatelliteSearchIdx = 0;
+	int16 _georgeAsteroidSearchIdx = 0;
+	int16 _georgeHitSearchIdx = 0;
+	byte *_georgeBgPic = nullptr;
+	byte *_georgePalette = nullptr;
+	byte *_georgePalCycleRawData = nullptr;
+	XPCycleState _georgePalCycleSpecs[4];
+	int16 _georgeCollectedSatellitesNum = 0;
+	SoundInfo _georgeSoundCarStartUp;
+	SoundInfo _georgeSoundCarGoesAway;
+	SoundInfo _georgeSoundCarLoopHi;
+	SoundInfo _georgeSoundCarLoopLo;
+	SoundInfo _georgeSoundCarTumble;
+	SoundInfo **_georgeSatelliteSoundList = nullptr;
+	byte _georgeSoundChannelCounter = 0;
+	SoundInfo *_georgeSoundToPlay = nullptr;
+	SoundInfo *_georgeSoundCurrent = nullptr;
+	SoundInfo *_georgeSoundQueued = nullptr;
+	SoundInfo *_georgeSoundNext = nullptr;
+	int16 _georgeSaveData[3] = { 0 };
+	int16 *_georgeThresholds = nullptr;
 
 	// --- HUCK ---
 	void playSoundMapHuck(int16 memberId);
@@ -1133,38 +1109,38 @@ protected:
 	static void swapHuckWordArrayCb();
 	static void swapHuckWordsCb();
 
-	BOLTLib *g_huckBoltLib = nullptr;
-	BOLTCallbacks g_huckBoltCallbacks;
-
-	static BOLTCallback g_huckTypeLoadCallbacks[27];
-	static BOLTCallback g_huckTypeFreeCallbacks[27];
-
-	int16 g_huckSoundPlaying = 0;
-	int16 g_huckHotSpotCount = 0;
-	int16 g_huckActionState = 0;
-	uint32 g_huckScreensaverTimer = 0;
-	uint32 g_huckBlinkTimer = 0;
-	int16 g_huckScreensaverFlag = 0;
-	int16 g_huckBlinkFlag = 0;
-	uint32 g_huckShuffleTimer = 0;
-	int16 g_huckGlobal[30] = { 0 };
-	HuckState g_huckState;
-	byte *g_huckGiftPic = nullptr;
-	byte *g_huckBgPic = nullptr;
-	int16 g_huckGiftGroupId = 0;
-	int16 g_huckVariantGroupId = 0;
-	byte *g_huckBgDisplayPic = nullptr;
-	int16 g_huckScrollOffset = 0;
-	int16 g_huckPalRange[8] = { 0 };
-	int16 g_huckReturnBooth = 0;
-	int16 g_huckExitFlag = 0;
-	int16 g_huckCursorX = 0;
-	int16 g_huckCursorY = 0;
-	byte g_huckPalSave0[15] = { 0 };
-	byte g_huckPalHighlight0[15] = { 0 };
-	byte g_huckPalSave1[15] = { 0 };
-	byte g_huckPalHighlight1[15] = { 0 };
-	XPPicDesc g_huckScratchPic;
+	BOLTLib *_huckBoltLib = nullptr;
+	BOLTCallbacks _huckBoltCallbacks;
+
+	static BOLTCallback _huckTypeLoadCallbacks[27];
+	static BOLTCallback _huckTypeFreeCallbacks[27];
+
+	int16 _huckSoundPlaying = 0;
+	int16 _huckHotSpotCount = 0;
+	int16 _huckActionState = 0;
+	uint32 _huckScreensaverTimer = 0;
+	uint32 _huckBlinkTimer = 0;
+	int16 _huckScreensaverFlag = 0;
+	int16 _huckBlinkFlag = 0;
+	uint32 _huckShuffleTimer = 0;
+	int16 _huckGlobal[30] = { 0 };
+	HuckState _huckState;
+	byte *_huckGiftPic = nullptr;
+	byte *_huckBgPic = nullptr;
+	int16 _huckGiftGroupId = 0;
+	int16 _huckVariantGroupId = 0;
+	byte *_huckBgDisplayPic = nullptr;
+	int16 _huckScrollOffset = 0;
+	int16 _huckPalRange[8] = { 0 };
+	int16 _huckReturnBooth = 0;
+	int16 _huckExitFlag = 0;
+	int16 _huckCursorX = 0;
+	int16 _huckCursorY = 0;
+	byte _huckPalSave0[15] = { 0 };
+	byte _huckPalHighlight0[15] = { 0 };
+	byte _huckPalSave1[15] = { 0 };
+	byte _huckPalHighlight1[15] = { 0 };
+	XPPicDesc _huckScratchPic;
 
 	// --- SCOOBY ---
 	bool loadScoobyBaseAssets();
@@ -1203,43 +1179,43 @@ protected:
 	static void swapScoobyHelpEntryCb();
 	static void swapScoobyWordArrayCb();
 
-	BOLTLib *g_scoobyBoltLib = nullptr;
-	BOLTCallbacks g_scoobyBoltCallbacks;
-
-	static BOLTCallback g_scoobyTypeLoadCallbacks[28];
-	static BOLTCallback g_scoobyTypeFreeCallbacks[28];
-
-	int16 g_scoobySoundMode = 0;
-	int16 g_scoobySoundPlaying = 0;
-	int16 g_scoobyShowHelp = 1;
-
-	int16 g_scoobyLastInputDir = 0;
-	int16 g_scoobyTransitionFrom = 0;
-	int16 g_scoobyTransitionTarget = 0;
-	int16 g_scoobyTransitionVelX = 0;
-	int16 g_scoobyTransitionVelY = 0;
-	XPPicDesc g_scoobyTempPic;
-	byte g_scoobyGlobalSaveData[0x3C] = { 0 };
-	ScoobyState g_scoobyGameState;
-	SSprite g_scoobySprite;
-	byte *g_scoobyBaseData = nullptr;
-	byte *g_scoobyBgPic = nullptr;
-	byte *g_scoobyWallPicsA[4] = { nullptr };
-	byte *g_scoobyWallPicsB[5] = { nullptr };
-	ScoobyRect g_scoobyCellBounds[25];
-	Common::Point g_scoobyLevelStartXY[25];
-	byte *g_scoobyLevelData = nullptr;
-	int16 g_scoobySelectedGraphicsGroup = 0;
-	int16 g_scoobyDifficulty = 0;
-	int16 g_scoobyLevelCount = 0;
-	int16 g_scoobyMoveRequested = 0;
-	int16 g_scoobyTransitioning = 0;
-	int16 g_scoobyDesiredDir = 0;
-	int16 g_scoobyInputHoldCount = 0;
-	int16 g_scoobyWallAnimating = 0;
-	int16 g_scoobyWallAnimStep = 0;
-	int16 g_scoobyWallsToOpen = 0;
-	int16 g_scoobyWallsToClose = 0;
+	BOLTLib *_scoobyBoltLib = nullptr;
+	BOLTCallbacks _scoobyBoltCallbacks;
+
+	static BOLTCallback _scoobyTypeLoadCallbacks[28];
+	static BOLTCallback _scoobyTypeFreeCallbacks[28];
+
+	int16 _scoobySoundMode = 0;
+	int16 _scoobySoundPlaying = 0;
+	int16 _scoobyShowHelp = 1;
+
+	int16 _scoobyLastInputDir = 0;
+	int16 _scoobyTransitionFrom = 0;
+	int16 _scoobyTransitionTarget = 0;
+	int16 _scoobyTransitionVelX = 0;
+	int16 _scoobyTransitionVelY = 0;
+	XPPicDesc _scoobyTempPic;
+	byte _scoobyGlobalSaveData[0x3C] = { 0 };
+	ScoobyState _scoobyGameState;
+	SSprite _scoobySprite;
+	byte *_scoobyBaseData = nullptr;
+	byte *_scoobyBgPic = nullptr;
+	byte *_scoobyWallPicsA[4] = { nullptr };
+	byte *_scoobyWallPicsB[5] = { nullptr };
+	ScoobyRect _scoobyCellBounds[25];
+	Common::Point _scoobyLevelStartXY[25];
+	byte *_scoobyLevelData = nullptr;
+	int16 _scoobySelectedGraphicsGroup = 0;
+	int16 _scoobyDifficulty = 0;
+	int16 _scoobyLevelCount = 0;
+	int16 _scoobyMoveRequested = 0;
+	int16 _scoobyTransitioning = 0;
+	int16 _scoobyDesiredDir = 0;
+	int16 _scoobyInputHoldCount = 0;
+	int16 _scoobyWallAnimating = 0;
+	int16 _scoobyWallAnimStep = 0;
+	int16 _scoobyWallsToOpen = 0;
+	int16 _scoobyWallsToClose = 0;
 
 	// --- TOPCAT ---
 	int16 topCatGame(int16 prevBooth);
@@ -1263,71 +1239,69 @@ protected:
 
 	static void swapTopCatHelpEntryCb();
 
-	RTFResource *g_topCatRtfHandle = nullptr;
-	RTFResource *g_topCatAvHandle = nullptr;
-	BOLTLib *g_topCatBoltLib = nullptr;
-	BOLTCallbacks g_topCatBoltCallbacks;
-
-	static BOLTCallback g_topCatTypeLoadCallbacks[26];
-	static BOLTCallback g_topCatTypeFreeCallbacks[26];
-
-	byte *g_topCatBackgroundPalette = nullptr;
-	byte *g_topCatBackground = nullptr;
-	byte *g_topCatBackgroundAnimationPalette = nullptr;
-	byte *g_topCatGraphicsAssets = nullptr;
-
-	int16 g_topCatBackgroundAnimFrame = 0;
-	int16 g_topCatMaxBackgroundAnimFrames = 0;
-	int16 g_topCatCurrentAnimType = 0;
-	int16 g_topCatAnimStateMachineStep = 0;
-	int16 g_topCatAnimQueueSize = 0;
-	int16 g_topCatQueuedSoundFrames = 0;
-	byte *g_topCatButtonsPalette = nullptr;
-	byte *g_topCatBlinkEntries = nullptr;
-	byte *g_topCatLightsPalette = nullptr;
-	int16 *g_topCatBlinkSeqPtr = nullptr;
-	byte *g_topCatSelectedChoiceOverlayGfx = nullptr;
-	byte *g_topCatCycleData = nullptr;
-	XPCycleState g_topCatCycleSpecs[4];
-	byte *g_topCatShuffledQuestions = nullptr;
-	byte *g_topCatShuffledAnswers = nullptr;
-	byte *g_topCatAnswersPermutations = nullptr;
-	byte *g_topCatAnswers = nullptr;
-	byte *g_topCatAnswersScreenPositions = nullptr;
-	int16 g_topCatSavedScore = 0;
-	int16 g_topCatSaveHistory = 0;
-	const char *g_topCatSaveFileName = "TopCatBF";
-	const char *g_topCatStaticSaveFileName = "TopCatBFStatic";
-	int16 g_topCatScore = 0;
-	int16 g_topCatShuffledQuestionsArrayIdx = 0;
-	uint32 g_topCatBlinkTimer = 0;
-	SoundInfo g_topCatSoundInfo;
-	byte g_topCatSavedShuffledQuestions[60] = { 0 };
-	byte g_topCatSavedShuffledAnswers[60] = { 0 };
-	byte g_topCatSavedAnswersPermutations[60 * 3] = { 0 };
-	byte g_topCatSaveBuffer[302] = { 0 };
-	byte *g_topCatHoveredEntry = nullptr;
-	byte *g_topCatHelpButton = nullptr;
-	byte *g_topCatBackButton = nullptr;
-	byte *g_topCatBlinkEntry = nullptr;
-	int16 g_topCatCycleStep = 0;
-	int16 g_topCatDrawnQuestionId = 0;
-	int16 g_topCatCurrentAnswerIdx = 0;
-	XPCycleState g_topCatChoiceCycleState[4];
-	TopCatAnim g_topCatAnimQueue[3];
-	int16 g_topCatCorrectAnimIdx = 0;
-	int16 g_topCatWrongAnimIdx = 0;
-	int16 g_topCatShouldPlayAnswerAnim = 0;
-	int16 g_topCatMaintainSoundFlag = 0;
-	int16 g_topCatPermTableA[3] = { 0, 1, 2 };
-	int16 g_topCatPermTableB[3] = { 0, 1, 2 };
-	int16 g_topCatBlinkSeq0[5] = { 0x01, 0x00, 0x01, 0x00, 0x01 };
-	int16 g_topCatBlinkSeq1[5] = { 0x03, 0x01, 0x03, 0x01, 0x03 };
-	int16 g_topCatBlinkSeq2[5] = { 0x07, 0x03, 0x07, 0x03, 0x07 };
-	int16 g_topCatBlinkSeq3[5] = { 0x0F, 0x07, 0x0F, 0x07, 0x0F };
-	int16 g_topCatBlinkSeq4[5] = { 0x1F, 0x0F, 0x1F, 0x0F, 0x1F };
-	int16 g_topCatBlinkSeq5[5] = { 0x3F, 0x1F, 0x3F, 0x1F, 0x3F };
-	int16 g_topCatBlinkSeq6[25] = {
+	RTFResource *_topCatRtfHandle = nullptr;
+	RTFResource *_topCatAvHandle = nullptr;
+	BOLTLib *_topCatBoltLib = nullptr;
+	BOLTCallbacks _topCatBoltCallbacks;
+
+	static BOLTCallback _topCatTypeLoadCallbacks[26];
+	static BOLTCallback _topCatTypeFreeCallbacks[26];
+
+	byte *_topCatBackgroundPalette = nullptr;
+	byte *_topCatBackground = nullptr;
+	byte *_topCatBackgroundAnimationPalette = nullptr;
+	byte *_topCatGraphicsAssets = nullptr;
+
+	int16 _topCatBackgroundAnimFrame = 0;
+	int16 _topCatMaxBackgroundAnimFrames = 0;
+	int16 _topCatCurrentAnimType = 0;
+	int16 _topCatAnimStateMachineStep = 0;
+	int16 _topCatAnimQueueSize = 0;
+	int16 _topCatQueuedSoundFrames = 0;
+	byte *_topCatButtonsPalette = nullptr;
+	byte *_topCatBlinkEntries = nullptr;
+	byte *_topCatLightsPalette = nullptr;
+	int16 *_topCatBlinkSeqPtr = nullptr;
+	byte *_topCatSelectedChoiceOverlayGfx = nullptr;
+	byte *_topCatCycleData = nullptr;
+	XPCycleState _topCatCycleSpecs[4];
+	byte *_topCatShuffledQuestions = nullptr;
+	byte *_topCatShuffledAnswers = nullptr;
+	byte *_topCatAnswersPermutations = nullptr;
+	byte *_topCatAnswers = nullptr;
+	byte *_topCatAnswersScreenPositions = nullptr;
+	int16 _topCatSavedScore = 0;
+	int16 _topCatSaveHistory = 0;
+	int16 _topCatScore = 0;
+	int16 _topCatShuffledQuestionsArrayIdx = 0;
+	uint32 _topCatBlinkTimer = 0;
+	SoundInfo _topCatSoundInfo;
+	byte _topCatSavedShuffledQuestions[60] = { 0 };
+	byte _topCatSavedShuffledAnswers[60] = { 0 };
+	byte _topCatSavedAnswersPermutations[60 * 3] = { 0 };
+	byte _topCatSaveBuffer[302] = { 0 };
+	byte *_topCatHoveredEntry = nullptr;
+	byte *_topCatHelpButton = nullptr;
+	byte *_topCatBackButton = nullptr;
+	byte *_topCatBlinkEntry = nullptr;
+	int16 _topCatCycleStep = 0;
+	int16 _topCatDrawnQuestionId = 0;
+	int16 _topCatCurrentAnswerIdx = 0;
+	XPCycleState _topCatChoiceCycleState[4];
+	TopCatAnim _topCatAnimQueue[3];
+	int16 _topCatCorrectAnimIdx = 0;
+	int16 _topCatWrongAnimIdx = 0;
+	int16 _topCatShouldPlayAnswerAnim = 0;
+	int16 _topCatMaintainSoundFlag = 0;
+	int16 _topCatPermTableA[3] = { 0, 1, 2 };
+	int16 _topCatPermTableB[3] = { 0, 1, 2 };
+	int16 _topCatBlinkSeq0[5] = { 0x01, 0x00, 0x01, 0x00, 0x01 };
+	int16 _topCatBlinkSeq1[5] = { 0x03, 0x01, 0x03, 0x01, 0x03 };
+	int16 _topCatBlinkSeq2[5] = { 0x07, 0x03, 0x07, 0x03, 0x07 };
+	int16 _topCatBlinkSeq3[5] = { 0x0F, 0x07, 0x0F, 0x07, 0x0F };
+	int16 _topCatBlinkSeq4[5] = { 0x1F, 0x0F, 0x1F, 0x0F, 0x1F };
+	int16 _topCatBlinkSeq5[5] = { 0x3F, 0x1F, 0x3F, 0x1F, 0x3F };
+	int16 _topCatBlinkSeq6[25] = {
 		0x09, 0x12, 0x24, 0x09, 0x12, 0x24,
 		0x09, 0x12, 0x24, 0x09, 0x12, 0x24,
 		0x09, 0x12, 0x24, 0x09, 0x12, 0x24,
@@ -1335,10 +1309,10 @@ protected:
 		0x3F
 	};
 
-	int16 *g_topCatBlinkSeqTable[7] = {
-		g_topCatBlinkSeq0, g_topCatBlinkSeq1, g_topCatBlinkSeq2,
-		g_topCatBlinkSeq3, g_topCatBlinkSeq4, g_topCatBlinkSeq5,
-		g_topCatBlinkSeq6
+	int16 *_topCatBlinkSeqTable[7] = {
+		_topCatBlinkSeq0, _topCatBlinkSeq1, _topCatBlinkSeq2,
+		_topCatBlinkSeq3, _topCatBlinkSeq4, _topCatBlinkSeq5,
+		_topCatBlinkSeq6
 	};
 
 	// --- YOGI ---
@@ -1375,41 +1349,41 @@ protected:
 	static void swapYogiAllWordsCb();
 	static void swapYogiFirstWordCb();
 
-	BOLTLib *g_yogiBoltLib = nullptr;
-	BOLTCallbacks g_yogiBoltCallbacks;
-
-	static BOLTCallback g_yogiTypeLoadCallbacks[27];
-	static BOLTCallback g_yogiTypeFreeCallbacks[27];
-
-	int16 g_yogiSoundPlaying = 0;
-	int16 g_yogiSoundActive = 0;
-	int16 g_yogiHotSpotCount = 0;
-	int16 g_yogiAnimActive = 0;
-	uint32 g_yogiBlinkTimer1 = 0;
-	uint32 g_yogiBlinkTimer2 = 0;
-	int16 g_yogiBlinkState1 = 0;
-	int16 g_yogiBlinkState2 = 0;
-	int16 g_yogiGlobal[0xA0] = { 0 };
-	YogiState g_yogiState;
-	byte *g_yogiBasketPic = nullptr;
-	int16 g_yogiLevelGroupId = 0;
-	byte *g_yogiBgPic = nullptr;
-	byte *g_yogiNormalSprite = nullptr;
-	byte *g_yogiHlSprite = nullptr;
-	byte *g_yogiAnimSprite = nullptr;
-	int16 g_yogiSpriteStride = 0;
-	int16 g_yogiSpriteHeight = 0;
-	int16 g_yogiPalRange[8] = { 0 };
-	int16 g_yogiReturnBooth = 0;
-	int16 g_yogiExitFlag = 0;
-	int16 g_yogiLevelId = 0;
-	int16 g_yogiCursorX = 0;
-	int16 g_yogiCursorY = 0;
-	byte g_yogiPalSave0[15] = { 0 };
-	byte g_yogiPalHighlight0[15] = { 0 };
-	byte g_yogiPalSave1[15] = { 0 };
-	byte g_yogiPalHighlight1[15] = { 0 };
-	XPPicDesc g_yogiScratchBuf;
+	BOLTLib *_yogiBoltLib = nullptr;
+	BOLTCallbacks _yogiBoltCallbacks;
+
+	static BOLTCallback _yogiTypeLoadCallbacks[27];
+	static BOLTCallback _yogiTypeFreeCallbacks[27];
+
+	int16 _yogiSoundPlaying = 0;
+	int16 _yogiSoundActive = 0;
+	int16 _yogiHotSpotCount = 0;
+	int16 _yogiAnimActive = 0;
+	uint32 _yogiBlinkTimer1 = 0;
+	uint32 _yogiBlinkTimer2 = 0;
+	int16 _yogiBlinkState1 = 0;
+	int16 _yogiBlinkState2 = 0;
+	int16 _yogiGlobal[0xA0] = { 0 };
+	YogiState _yogiState;
+	byte *_yogiBasketPic = nullptr;
+	int16 _yogiLevelGroupId = 0;
+	byte *_yogiBgPic = nullptr;
+	byte *_yogiNormalSprite = nullptr;
+	byte *_yogiHlSprite = nullptr;
+	byte *_yogiAnimSprite = nullptr;
+	int16 _yogiSpriteStride = 0;
+	int16 _yogiSpriteHeight = 0;
+	int16 _yogiPalRange[8] = { 0 };
+	int16 _yogiReturnBooth = 0;
+	int16 _yogiExitFlag = 0;
+	int16 _yogiLevelId = 0;
+	int16 _yogiCursorX = 0;
+	int16 _yogiCursorY = 0;
+	byte _yogiPalSave0[15] = { 0 };
+	byte _yogiPalHighlight0[15] = { 0 };
+	byte _yogiPalSave1[15] = { 0 };
+	byte _yogiPalHighlight1[15] = { 0 };
+	XPPicDesc _yogiScratchBuf;
 };
 
 extern BoltEngine *g_engine;
diff --git a/engines/bolt/booth.cpp b/engines/bolt/booth.cpp
index a8f26fd9ef0..92ce101e054 100644
--- a/engines/bolt/booth.cpp
+++ b/engines/bolt/booth.cpp
@@ -34,8 +34,8 @@ void BoltEngine::startCycle(byte *cycleResource) {
 
 void BoltEngine::displayBooth(int16 page) {
 	_xp->setTransparency(page);
-	_xp->displayPic(&g_boothLetterSprite, g_displayX, g_displayY, page);
-	displayColors(g_boothPalCycleData, page, 0);
+	_xp->displayPic(&_boothLetterSprite, _displayX, _displayY, page);
+	displayColors(_boothPalCycleData, page, 0);
 
 	if (page != stFront)
 		_xp->fillDisplay(0, stFront);
@@ -46,7 +46,7 @@ void BoltEngine::displayBooth(int16 page) {
 void BoltEngine::playAVOverBooth(int16 animIndex) {
 	_xp->hideCursor();
 	displayBooth(stBack);
-	playAV(g_rtfHandle, animIndex, g_displayWidth, g_displayHeight, g_displayX, g_displayY);
+	playAV(_rtfHandle, animIndex, _displayWidth, _displayHeight, _displayX, _displayY);
 	displayBooth(stFront);
 	_xp->showCursor();
 }
@@ -65,7 +65,7 @@ int16 BoltEngine::hucksBooth(int16 prevBooth) {
 	case 2:
 		return 10; // default (play game)
 	case 3:
-		return g_isDemo ? 17 : 10; // play game
+		return _isDemo ? 17 : 10; // play game
 	default:
 		return 10;
 	}
@@ -85,7 +85,7 @@ int16 BoltEngine::fredsBooth(int16 prevBooth) {
 	case 2:
 		return 11;
 	case 3:
-		return g_isDemo ? 17 : 11;
+		return _isDemo ? 17 : 11;
 	default:
 		return 11;
 	}
@@ -105,7 +105,7 @@ int16 BoltEngine::scoobysBooth(int16 prevBooth) {
 	case 2:
 		return 12;
 	case 3:
-		if (g_isDemo && g_scoobyWins >= 2)
+		if (_isDemo && _scoobyWins >= 2)
 			return 17;
 
 		return 12;
@@ -128,7 +128,7 @@ int16 BoltEngine::yogisBooth(int16 prevBooth) {
 	case 2:
 		return 13;
 	case 3:
-		if (g_isDemo && g_yogiWins >= 2)
+		if (_isDemo && _yogiWins >= 2)
 			return 17;
 
 		return 13;
@@ -151,7 +151,7 @@ int16 BoltEngine::georgesBooth(int16 prevBooth) {
 	case 2:
 		return 14;
 	case 3:
-		if (g_isDemo && g_georgeWins >= 2)
+		if (_isDemo && _georgeWins >= 2)
 			return 17;
 
 		return 14;
@@ -174,7 +174,7 @@ int16 BoltEngine::topCatsBooth(int16 prevBooth) {
 	case 2:
 		return 15;
 	case 3:
-		return g_isDemo ? 17 : 15;
+		return _isDemo ? 17 : 15;
 	default:
 		return 15;
 	}
@@ -186,7 +186,7 @@ int16 BoltEngine::mainEntrance(int16 prevBooth) {
 
 	switch (result) {
 	case -1:
-		return g_isDemo ? 18 : 0; // exit
+		return _isDemo ? 18 : 0; // exit
 	case 0:
 		return 8; // left (TopCat)
 	case 1:
@@ -198,9 +198,9 @@ int16 BoltEngine::mainEntrance(int16 prevBooth) {
 	case 4:
 		return 9; // self
 	case 5:
-		return g_isDemo ? 18 : 0; // exit
+		return _isDemo ? 18 : 0; // exit
 	case 6:
-		return g_isDemo ? 18 : 9; // Case only available in the demo
+		return _isDemo ? 18 : 9; // Case only available in the demo
 	default:
 		return 9;
 	}
@@ -209,61 +209,61 @@ int16 BoltEngine::mainEntrance(int16 prevBooth) {
 bool BoltEngine::loadBooth(int16 boothId) {
 	switch (boothId) {
 	case 3:
-		if (g_boothLoadedMask & 0x01)
+		if (_boothLoadedMask & 0x01)
 			break;
-		if (!getBOLTGroup(g_boothsBoltLib, 0x100, 1))
+		if (!getBOLTGroup(_boothsBoltLib, 0x100, 1))
 			return false;
-		g_boothLoadedMask |= 0x01;
+		_boothLoadedMask |= 0x01;
 		break;
 
 	case 4:
-		if (g_boothLoadedMask & 0x02)
+		if (_boothLoadedMask & 0x02)
 			break;
-		if (!getBOLTGroup(g_boothsBoltLib, 0x200, 1))
+		if (!getBOLTGroup(_boothsBoltLib, 0x200, 1))
 			return false;
-		g_boothLoadedMask |= 0x02;
+		_boothLoadedMask |= 0x02;
 		break;
 
 	case 5:
-		if (g_boothLoadedMask & 0x04)
+		if (_boothLoadedMask & 0x04)
 			break;
-		if (!getBOLTGroup(g_boothsBoltLib, 0x300, 1))
+		if (!getBOLTGroup(_boothsBoltLib, 0x300, 1))
 			return false;
-		g_boothLoadedMask |= 0x04;
+		_boothLoadedMask |= 0x04;
 		break;
 
 	case 6:
-		if (g_boothLoadedMask & 0x08)
+		if (_boothLoadedMask & 0x08)
 			break;
-		if (!getBOLTGroup(g_boothsBoltLib, 0x400, 1))
+		if (!getBOLTGroup(_boothsBoltLib, 0x400, 1))
 			return false;
-		g_boothLoadedMask |= 0x08;
+		_boothLoadedMask |= 0x08;
 		break;
 
 	case 7:
-		if (g_boothLoadedMask & 0x10)
+		if (_boothLoadedMask & 0x10)
 			break;
-		if (!getBOLTGroup(g_boothsBoltLib, 0x500, 1))
+		if (!getBOLTGroup(_boothsBoltLib, 0x500, 1))
 			return false;
-		g_boothLoadedMask |= 0x10;
+		_boothLoadedMask |= 0x10;
 		break;
 
 	case 8:
-		if (g_boothLoadedMask & 0x20)
+		if (_boothLoadedMask & 0x20)
 			break;
-		if (!getBOLTGroup(g_boothsBoltLib, 0x600, 1))
+		if (!getBOLTGroup(_boothsBoltLib, 0x600, 1))
 			return false;
-		g_boothLoadedMask |= 0x20;
+		_boothLoadedMask |= 0x20;
 		break;
 
 	case 9:
-		if (g_boothLoadedMask & 0x40)
+		if (_boothLoadedMask & 0x40)
 			break;
-		if (!getBOLTGroup(g_boothsBoltLib, 0x700, 1))
+		if (!getBOLTGroup(_boothsBoltLib, 0x700, 1))
 			return false;
-		if (!getBOLTGroup(g_boothsBoltLib, 0x800 + (g_lettersWon << 8), 1))
+		if (!getBOLTGroup(_boothsBoltLib, 0x800 + (_lettersWon << 8), 1))
 			return false;
-		g_boothLoadedMask |= 0x40;
+		_boothLoadedMask |= 0x40;
 		break;
 	}
 
@@ -271,37 +271,37 @@ bool BoltEngine::loadBooth(int16 boothId) {
 }
 
 void BoltEngine::unloadBooth() {
-	if (g_boothLoadedMask & 0x01)
-		freeBOLTGroup(g_boothsBoltLib, 0x100, 1);
+	if (_boothLoadedMask & 0x01)
+		freeBOLTGroup(_boothsBoltLib, 0x100, 1);
 
-	if (g_boothLoadedMask & 0x02)
-		freeBOLTGroup(g_boothsBoltLib, 0x200, 1);
+	if (_boothLoadedMask & 0x02)
+		freeBOLTGroup(_boothsBoltLib, 0x200, 1);
 
-	if (g_boothLoadedMask & 0x04)
-		freeBOLTGroup(g_boothsBoltLib, 0x300, 1);
+	if (_boothLoadedMask & 0x04)
+		freeBOLTGroup(_boothsBoltLib, 0x300, 1);
 
-	if (g_boothLoadedMask & 0x08)
-		freeBOLTGroup(g_boothsBoltLib, 0x400, 1);
+	if (_boothLoadedMask & 0x08)
+		freeBOLTGroup(_boothsBoltLib, 0x400, 1);
 
-	if (g_boothLoadedMask & 0x10)
-		freeBOLTGroup(g_boothsBoltLib, 0x500, 1);
+	if (_boothLoadedMask & 0x10)
+		freeBOLTGroup(_boothsBoltLib, 0x500, 1);
 
-	if (g_boothLoadedMask & 0x20)
-		freeBOLTGroup(g_boothsBoltLib, 0x600, 1);
+	if (_boothLoadedMask & 0x20)
+		freeBOLTGroup(_boothsBoltLib, 0x600, 1);
 
-	if (g_boothLoadedMask & 0x40) {
-		freeBOLTGroup(g_boothsBoltLib, 0x800 + (g_lettersWon << 8), 1);
-		freeBOLTGroup(g_boothsBoltLib, 0x700, 1);
+	if (_boothLoadedMask & 0x40) {
+		freeBOLTGroup(_boothsBoltLib, 0x800 + (_lettersWon << 8), 1);
+		freeBOLTGroup(_boothsBoltLib, 0x700, 1);
 	}
 
-	g_boothLoadedMask = 0;
+	_boothLoadedMask = 0;
 }
 
 int16 BoltEngine::openBooth(int16 boothId) {
 	int16 baseResId;
 	int16 resId;
 
-	g_currentBoothScene = boothId;
+	_currentBoothScene = boothId;
 
 	if (!loadBooth(boothId))
 		return -1;
@@ -310,63 +310,63 @@ int16 BoltEngine::openBooth(int16 boothId) {
 
 	// Main entrance has more hotspots/animations than regular booths
 	if (boothId == 9) {
-		g_boothNumHotspots = 8;
-		g_boothNumAnimations = 7;
+		_boothNumHotspots = 8;
+		_boothNumAnimations = 7;
 	} else {
-		g_boothNumHotspots = 5;
-		g_boothNumAnimations = 4;
+		_boothNumHotspots = 5;
+		_boothNumAnimations = 4;
 	}
 
 	// Background scene descriptor
-	g_boothSceneDesc = memberAddr(g_boothsBoltLib, baseResId + 0x100);
+	_boothSceneDesc = memberAddr(_boothsBoltLib, baseResId + 0x100);
 
 	// Load 4 palette descriptors (at DS:0x13AC, members +0x105..+0x108)
 	resId = baseResId + 0x105;
 	for (int16 i = 0; i < 4; i++) {
-		g_boothHotPalDescs[i] = memberAddr(g_boothsBoltLib, resId);
+		_boothHotPalDescs[i] = memberAddr(_boothsBoltLib, resId);
 		resId++;
 	}
 
 	// Load animation descriptors
 	if (boothId == 9) {
-		for (int16 i = 0; i < g_boothNumAnimations; i++)
-			g_boothAnimDescs[i] = memberAddr(g_boothsBoltLib, 0x709 + i);
+		for (int16 i = 0; i < _boothNumAnimations; i++)
+			_boothAnimDescs[i] = memberAddr(_boothsBoltLib, 0x709 + i);
 	} else {
 		resId = baseResId + 0x109;
-		for (int16 i = 0; i < g_boothNumAnimations; i++) {
-			g_boothAnimDescs[i] = memberAddr(g_boothsBoltLib, resId);
+		for (int16 i = 0; i < _boothNumAnimations; i++) {
+			_boothAnimDescs[i] = memberAddr(_boothsBoltLib, resId);
 			resId++;
 		}
 	}
 
 	// Load hotspot descriptors
 	if (boothId == 9) {
-		for (int16 i = 0; i < g_boothNumHotspots; i++)
-			g_boothHotspotDescs[i] = memberToRect(memberAddr(g_boothsBoltLib, 0x710 + i));
+		for (int16 i = 0; i < _boothNumHotspots; i++)
+			_boothHotspotDescs[i] = memberToRect(memberAddr(_boothsBoltLib, 0x710 + i));
 	} else {
 		resId = baseResId + 0x10D;
-		for (int16 i = 0; i < g_boothNumHotspots; i++) {
-			g_boothHotspotDescs[i] = memberToRect(memberAddr(g_boothsBoltLib, resId));
+		for (int16 i = 0; i < _boothNumHotspots; i++) {
+			_boothHotspotDescs[i] = memberToRect(memberAddr(_boothsBoltLib, resId));
 			resId++;
 		}
 	}
 
 	if (boothId == 9) {
 		// Main entrance palette cycling data
-		g_boothPalCycleData = memberAddr(g_boothsBoltLib, 0x718);
+		_boothPalCycleData = memberAddr(_boothsBoltLib, 0x718);
 
 		// Letter marquee sprite (display mode selects variant)
-		byte *sprite = memberAddr(g_boothsBoltLib, (g_displayMode != 0 ? 0x801 : 0x800) + (g_lettersWon << 8));
-		boltPict2Pict(&g_boothLetterSprite, sprite);
+		byte *sprite = memberAddr(_boothsBoltLib, (_displayMode != 0 ? 0x801 : 0x800) + (_lettersWon << 8));
+		boltPict2Pict(&_boothLetterSprite, sprite);
 
 		// Visit sign sprite and its off variant
-		g_boothVisitSignOn = memberAddr(g_boothsBoltLib, 0x747);
-		g_boothVisitSignOff = memberAddr(g_boothsBoltLib, 0x748);
+		_boothVisitSignOn = memberAddr(_boothsBoltLib, 0x747);
+		_boothVisitSignOff = memberAddr(_boothsBoltLib, 0x748);
 	} else {
-		g_boothPalCycleData = memberAddr(g_boothsBoltLib, baseResId + 0x112);
+		_boothPalCycleData = memberAddr(_boothsBoltLib, baseResId + 0x112);
 
-		byte *sprite = memberAddr(g_boothsBoltLib, baseResId + (g_displayMode != 0 ? 0x114 : 0x113));
-		boltPict2Pict(&g_boothLetterSprite, sprite);
+		byte *sprite = memberAddr(_boothsBoltLib, baseResId + (_displayMode != 0 ? 0x114 : 0x113));
+		boltPict2Pict(&_boothLetterSprite, sprite);
 	}
 
 	displayBooth(stFront);
@@ -374,19 +374,19 @@ int16 BoltEngine::openBooth(int16 boothId) {
 	_xp->setCursorColor(255, 255, 255);
 
 	// First-time cursor position from resource data
-	if (g_needInitCursorPos) {
-		g_cursorX = (int16)READ_UINT16(memberAddr(g_boothsBoltLib, 1));
-		g_cursorY = (int16)READ_UINT16(memberAddr(g_boothsBoltLib, 2));
-		g_needInitCursorPos = false;
+	if (_needInitCursorPos) {
+		_cursorX = (int16)READ_UINT16(memberAddr(_boothsBoltLib, 1));
+		_cursorY = (int16)READ_UINT16(memberAddr(_boothsBoltLib, 2));
+		_needInitCursorPos = false;
 	}
 
-	_xp->setCursorPos(g_cursorX, g_cursorY);
+	_xp->setCursorPos(_cursorX, _cursorY);
 
 	flushInput();
 
-	if (g_isDemo && boothId == 9) {
-		g_helpFlag = 1;
-		g_helpTimer = _xp->startTimer(500);
+	if (_isDemo && boothId == 9) {
+		_helpFlag = 1;
+		_helpTimer = _xp->startTimer(500);
 	}
 
 	return boothEventLoop();
@@ -395,7 +395,7 @@ int16 BoltEngine::openBooth(int16 boothId) {
 void BoltEngine::closeBooth() {
 	uint16 buttonState;
 
-	_xp->readCursor(&buttonState, &g_cursorY, &g_cursorX);
+	_xp->readCursor(&buttonState, &_cursorY, &_cursorX);
 	flushInput();
 	_xp->fillDisplay(0, 0);
 	unloadBooth();
@@ -405,10 +405,10 @@ void BoltEngine::playTour() {
 	int16 playing = 1;
 
 	_xp->hideCursor();
-	g_tourStep = 0;
+	_tourStep = 0;
 
-	if (prepareAV(g_rtfHandle, 4 - (g_isDemo ? 3 : 0), g_displayWidth, g_displayHeight, g_displayX, g_displayY)) {
-		while (playing) {
+	if (prepareAV(_rtfHandle, 4 - (_isDemo ? 3 : 0), _displayWidth, _displayHeight, _displayX, _displayY)) {
+		while (playing && !shouldQuit()) {
 			playing = maintainAV(0);
 			if (!playing)
 				break;
@@ -432,6 +432,9 @@ void BoltEngine::playTour() {
 			}
 		}
 
+		if (shouldQuit())
+			stopAV();
+
 		_xp->stopCycle();
 		displayBooth(stFront);
 	}
@@ -440,10 +443,10 @@ void BoltEngine::playTour() {
 }
 
 void BoltEngine::finishPlayingHelp(int16 activeHotspot) {
-	g_helpPlaying = 0;
+	_helpPlaying = 0;
 
-	if (g_helpIsIdle != 0) {
-		g_helpIsIdle = 0;
+	if (_helpIsIdle != 0) {
+		_helpIsIdle = 0;
 	} else {
 		resetInactivityState();
 	}
@@ -454,12 +457,12 @@ void BoltEngine::finishPlayingHelp(int16 activeHotspot) {
 	if (activeHotspot != 1)
 		restoreColors(1);
 
-	if (activeHotspot != 2 && g_helpFlag == 0)
+	if (activeHotspot != 2 && _helpFlag == 0)
 		restoreColors(2);
 
-	g_keyLeft = 0;
+	_keyLeft = 0;
 
-	if (g_currentBoothScene == 9) {
+	if (_currentBoothScene == 9) {
 		if (activeHotspot != 7)
 			restoreColors(7);
 
@@ -469,14 +472,14 @@ void BoltEngine::finishPlayingHelp(int16 activeHotspot) {
 		if (activeHotspot != 6)
 			restoreColors(6);
 
-		g_keyRight = 0;
-		g_keyUp = 0;
-		g_keyDown = 0;
+		_keyRight = 0;
+		_keyUp = 0;
+		_keyDown = 0;
 	} else {
 		if (activeHotspot != 3)
 			restoreColors(3);
 
-		g_keyEnter = 0;
+		_keyEnter = 0;
 	}
 }
 
@@ -484,25 +487,25 @@ int16 BoltEngine::hotSpotActive(int16 hotspot) {
 	switch (hotspot) {
 	case 0:
 	case 1:
-		return g_keyLeft;
+		return _keyLeft;
 
 	case 2:
-		return (g_helpPlaying || g_helpFlag) ? 1 : 0;
+		return (_helpPlaying || _helpFlag) ? 1 : 0;
 
 	case 3:
-		return g_keyEnter;
+		return _keyEnter;
 
 	case 4:
 		return 0;
 
 	case 5:
-		return g_keyDown;
+		return _keyDown;
 
 	case 6:
-		return g_keyUp;
+		return _keyUp;
 
 	case 7:
-		return g_keyRight;
+		return _keyRight;
 
 	default:
 		return 0;
@@ -512,26 +515,26 @@ int16 BoltEngine::hotSpotActive(int16 hotspot) {
 void BoltEngine::hoverHotSpot() {
 	int16 i = 0;
 
-	while (i < g_boothNumHotspots) {
-		if (g_boothHotspotDescs[i].contains(g_cursorX, g_cursorY))
+	while (i < _boothNumHotspots) {
+		if (_boothHotspotDescs[i].contains(_cursorX, _cursorY))
 			break;
 
 		i++;
 	}
 
-	if (i == g_hoveredHotspot)
+	if (i == _hoveredHotspot)
 		return;
 
-	if (g_hoveredHotspot < g_boothNumHotspots) {
-		if (!hotSpotActive(g_hoveredHotspot))
-			restoreColors(g_hoveredHotspot);
+	if (_hoveredHotspot < _boothNumHotspots) {
+		if (!hotSpotActive(_hoveredHotspot))
+			restoreColors(_hoveredHotspot);
 	}
 
-	g_hoveredHotspot = i;
+	_hoveredHotspot = i;
 
-	if (i < g_boothNumHotspots) {
+	if (i < _boothNumHotspots) {
 		if (!hotSpotActive(i))
-			setColors(g_hoveredHotspot);
+			setColors(_hoveredHotspot);
 	}
 }
 
@@ -540,45 +543,45 @@ int16 BoltEngine::boothEventLoop() {
 	int16 exitCode = 0;
 
 	_xp->showCursor();
-	g_hoveredHotspot = -1;
+	_hoveredHotspot = -1;
 	hoverHotSpot();
 
 	_xp->setInactivityTimer(30);
 
 	while (exitCode == 0 && !shouldQuit()) {
-		if (g_helpPlaying != 0) {
-			g_helpPlaying = maintainAudioPlay(0);
-			if (g_helpPlaying == 0)
-				finishPlayingHelp(g_hoveredHotspot);
+		if (_helpPlaying != 0) {
+			_helpPlaying = maintainAudioPlay(0);
+			if (_helpPlaying == 0)
+				finishPlayingHelp(_hoveredHotspot);
 		}
 
 		eventData = 0;
 		switch (_xp->getEvent(etEmpty, &eventData)) {
 		case etTimer:
-			if (g_keyLeft == 0 && g_keyRight == 0 &&
-				g_keyUp == 0 && g_keyDown == 0 &&
-				g_keyEnter == 0 && g_helpFlag == 0)
+			if (_keyLeft == 0 && _keyRight == 0 &&
+				_keyUp == 0 && _keyDown == 0 &&
+				_keyEnter == 0 && _helpFlag == 0)
 				break;
 
-			if (g_keyReleased != 0) {
+			if (_keyReleased != 0) {
 				// Key release: restore hotspot colors
-				g_keyReleased = 0;
-				if (g_keyLeft != 0) {
+				_keyReleased = 0;
+				if (_keyLeft != 0) {
 					restoreColors(0);
 					restoreColors(1);
-				} else if (g_keyRight != 0) {
+				} else if (_keyRight != 0) {
 					restoreColors(7);
-				} else if (g_keyUp != 0) {
+				} else if (_keyUp != 0) {
 					restoreColors(6);
-				} else if (g_keyDown != 0) {
+				} else if (_keyDown != 0) {
 					restoreColors(5);
-				} else if (g_keyEnter != 0) {
+				} else if (_keyEnter != 0) {
 					restoreColors(3);
-				} else if (g_helpFlag != 0) {
+				} else if (_helpFlag != 0) {
 					restoreColors(2);
 
-					if (g_helpPlaying != 0) {
-						if (g_helpIsIdle == 0 || g_currentBoothScene != 9)
+					if (_helpPlaying != 0) {
+						if (_helpIsIdle == 0 || _currentBoothScene != 9)
 							break;
 					}
 
@@ -586,23 +589,23 @@ int16 BoltEngine::boothEventLoop() {
 				}
 			} else {
 				// Key press: highlight hotspot colors
-				g_keyReleased = 1;
-				if (g_keyLeft != 0) {
+				_keyReleased = 1;
+				if (_keyLeft != 0) {
 					setColors(0);
 					setColors(1);
-				} else if (g_keyRight != 0) {
+				} else if (_keyRight != 0) {
 					setColors(7);
-				} else if (g_keyUp != 0) {
+				} else if (_keyUp != 0) {
 					setColors(6);
-				} else if (g_keyDown != 0) {
+				} else if (_keyDown != 0) {
 					setColors(5);
-				} else if (g_keyEnter != 0) {
+				} else if (_keyEnter != 0) {
 					setColors(3);
-				} else if (g_helpFlag != 0) {
+				} else if (_helpFlag != 0) {
 					setColors(2);
 
-					if (g_helpPlaying != 0) {
-						if (g_helpIsIdle == 0 || g_currentBoothScene != 9)
+					if (_helpPlaying != 0) {
+						if (_helpIsIdle == 0 || _currentBoothScene != 9)
 							break;
 					}
 
@@ -610,32 +613,32 @@ int16 BoltEngine::boothEventLoop() {
 				}
 			}
 
-			g_helpTimer = _xp->startTimer(500);
+			_helpTimer = _xp->startTimer(500);
 			break;
 
 		case etMouseMove:
-			g_cursorX = (int16)(eventData >> 16);
-			g_cursorY = (int16)eventData;
+			_cursorX = (int16)(eventData >> 16);
+			_cursorY = (int16)eventData;
 			hoverHotSpot();
 			break;
 
 		case etMouseDown:
 		{
-			int16 prevHelp = g_helpPlaying;
-			if (g_helpPlaying != 0) {
+			int16 prevHelp = _helpPlaying;
+			if (_helpPlaying != 0) {
 				stopAnimation();
-				finishPlayingHelp(g_hoveredHotspot);
+				finishPlayingHelp(_hoveredHotspot);
 			}
 
-			if (prevHelp != 0 && g_hoveredHotspot == 2)
+			if (prevHelp != 0 && _hoveredHotspot == 2)
 				break;
 
-			if (g_hoveredHotspot == -1)
+			if (_hoveredHotspot == -1)
 				break;
 
-			exitCode = handleButtonPress(g_hoveredHotspot);
+			exitCode = handleButtonPress(_hoveredHotspot);
 			if (exitCode == 0) {
-				g_hoveredHotspot = -1;
+				_hoveredHotspot = -1;
 				hoverHotSpot();
 			}
 
@@ -643,43 +646,43 @@ int16 BoltEngine::boothEventLoop() {
 		}
 
 		case etSound:
-			if (g_helpPlaying != 0) {
-				g_helpPlaying = maintainAudioPlay(1);
-				if (g_helpPlaying == 0)
-					finishPlayingHelp(g_hoveredHotspot);
+			if (_helpPlaying != 0) {
+				_helpPlaying = maintainAudioPlay(1);
+				if (_helpPlaying == 0)
+					finishPlayingHelp(_hoveredHotspot);
 			}
 
 			break;
 
 		case etInactivity:
-			if (g_helpPlaying != 0)
+			if (_helpPlaying != 0)
 				break;
 
-			if (g_helpFlag == 0) {
-				g_helpFlag = 1;
-				g_helpTimer = _xp->startTimer(500);
-				if (g_idleHelpAudioAvailable != 0) {
+			if (_helpFlag == 0) {
+				_helpFlag = 1;
+				_helpTimer = _xp->startTimer(500);
+				if (_idleHelpAudioAvailable != 0) {
 					_xp->setInactivityTimer(30);
 					continue;
 				}
 			} else {
-				if (g_idleHelpAudioAvailable == 0)
+				if (_idleHelpAudioAvailable == 0)
 					break;
 
-				if (startAnimation(g_rtfHandle, g_isDemo ? 0x16 : 0x19)) {
-					g_idleHelpAudioAvailable = 0;
-					g_helpPlaying = 1;
-					g_helpIsIdle = 1;
+				if (startAnimation(_rtfHandle, _isDemo ? 0x16 : 0x19)) {
+					_idleHelpAudioAvailable = 0;
+					_helpPlaying = 1;
+					_helpIsIdle = 1;
 				}
 			}
 
 			break;
 
 		case etTrigger:
-			if (g_helpPlaying == 0)
+			if (_helpPlaying == 0)
 				break;
 
-			if (g_currentBoothScene == 9)
+			if (_currentBoothScene == 9)
 				mainEntranceHelpBlink();
 			else
 				boothHelpBlink();
@@ -693,37 +696,37 @@ int16 BoltEngine::boothEventLoop() {
 	}
 
 	_xp->hideCursor();
-	return g_hoveredHotspot;
+	return _hoveredHotspot;
 }
 
 void BoltEngine::resetInactivityState() {
-	if (g_helpTimer != 0) {
-		_xp->killTimer(g_helpTimer);
-		g_helpTimer = 0;
+	if (_helpTimer != 0) {
+		_xp->killTimer(_helpTimer);
+		_helpTimer = 0;
 	}
 
-	g_idleHelpAudioAvailable = 0;
+	_idleHelpAudioAvailable = 0;
 	_xp->setInactivityTimer(30);
 
-	if (g_helpFlag != 0) {
+	if (_helpFlag != 0) {
 		restoreColors(2);
-		if (g_currentBoothScene == 9)
+		if (_currentBoothScene == 9)
 			restoreColors(8);
 	}
 
-	g_helpFlag = 0;
+	_helpFlag = 0;
 }
 
 bool BoltEngine::handleButtonPress(int16 hotspot) {
 	byte savedLeftDoor[3];
 	byte savedRightDoor[3];
 
-	savedLeftDoor[0] = g_leftDoorNavTable[0];
-	savedLeftDoor[1] = g_leftDoorNavTable[1];
-	savedLeftDoor[2] = g_leftDoorNavTable[2];
-	savedRightDoor[0] = g_rightDoorNavTable[0];
-	savedRightDoor[1] = g_rightDoorNavTable[1];
-	savedRightDoor[2] = g_rightDoorNavTable[2];
+	savedLeftDoor[0] = _leftDoorNavTable[0];
+	savedLeftDoor[1] = _leftDoorNavTable[1];
+	savedLeftDoor[2] = _leftDoorNavTable[2];
+	savedRightDoor[0] = _rightDoorNavTable[0];
+	savedRightDoor[1] = _rightDoorNavTable[1];
+	savedRightDoor[2] = _rightDoorNavTable[2];
 
 	if (hotspot != -1)
 		resetInactivityState();
@@ -735,27 +738,27 @@ bool BoltEngine::handleButtonPress(int16 hotspot) {
 
 	case 2: {
 		int16 animId;
-		switch (g_currentBoothScene) {
+		switch (_currentBoothScene) {
 		case 3:
-			animId = g_isDemo ? 0x0F : 0x18;
+			animId = _isDemo ? 0x0F : 0x18;
 			break;
 		case 4:
-			animId = g_isDemo ? 0x10 : 0x12;
+			animId = _isDemo ? 0x10 : 0x12;
 			break;
 		case 5:
-			animId = g_isDemo ? 0x11 : 0x13;
+			animId = _isDemo ? 0x11 : 0x13;
 			break;
 		case 6:
-			animId = g_isDemo ? 0x12 : 0x14;
+			animId = _isDemo ? 0x12 : 0x14;
 			break;
 		case 7:
-			animId = g_isDemo ? 0x13 : 0x15;
+			animId = _isDemo ? 0x13 : 0x15;
 			break;
 		case 8:
-			animId = g_isDemo ? 0x14 : 0x16;
+			animId = _isDemo ? 0x14 : 0x16;
 			break;
 		case 9:
-			animId = g_isDemo ? 0x15 : 0x17;
+			animId = _isDemo ? 0x15 : 0x17;
 			break;
 		default:
 			animId = -1;
@@ -764,44 +767,44 @@ bool BoltEngine::handleButtonPress(int16 hotspot) {
 
 		bool ok = false;
 		if (animId != -1)
-			ok = startAnimation(g_rtfHandle, animId);
+			ok = startAnimation(_rtfHandle, animId);
 
 		if (ok) {
-			g_helpPlaying = 1;
+			_helpPlaying = 1;
 			setColors(2);
-			g_helpFlag = 0;
-			g_tourStep = 0;
+			_helpFlag = 0;
+			_tourStep = 0;
 		}
 
 		return false;
 	}
 
 	default:
-		if (g_currentBoothScene == 9) {
+		if (_currentBoothScene == 9) {
 			switch (hotspot) {
 			case 3:
 			case 4:
 			{
 				byte *navTable = (hotspot == 3) ? savedLeftDoor : savedRightDoor;
-				int8 boothOffset = (int8)navTable[g_lettersWon % 3];
-				playAVOverBooth(boothOffset + (g_isDemo ? 9 : 12));
+				int8 boothOffset = (int8)navTable[_lettersWon % 3];
+				playAVOverBooth(boothOffset + (_isDemo ? 9 : 12));
 				return false;
 			}
 
 			case 5:
-				if (g_isDemo) {
+				if (_isDemo) {
 					return true;
 				}
 
 				_xp->hideCursor();
 				displayBooth(stBack);
 
-				if (playAV(g_rtfHandle, 3, g_displayWidth, g_displayHeight, g_displayX, g_displayY) == 0) {
+				if (playAV(_rtfHandle, 3, _displayWidth, _displayHeight, _displayX, _displayY) == 0) {
 					fadeToBlack(1);
 					_xp->setTransparency(false);
-					displayPic(getBOLTMember(g_boothsBoltLib, g_displayMode ? 0x1802 : 0x1801), g_displayX, g_displayY, 0);
+					displayPic(getBOLTMember(_boothsBoltLib, _displayMode ? 0x1802 : 0x1801), _displayX, _displayY, 0);
 					_xp->updateDisplay();
-					displayColors(getBOLTMember(g_boothsBoltLib, 0x1800), stFront, 0);
+					displayColors(getBOLTMember(_boothsBoltLib, 0x1800), stFront, 0);
 					_xp->updateDisplay();
 
 					uint32 timer = _xp->startTimer(5000);
@@ -827,30 +830,30 @@ bool BoltEngine::handleButtonPress(int16 hotspot) {
 				vDelete("TopCatBF");
 				vDelete("Yogi");
 
-				g_huckWins = 0;
-				g_fredWins = 0;
-				g_scoobyWins = 0;
-				g_yogiWins = 0;
-				g_georgeWins = 0;
-				g_topCatWins = 0;
+				_huckWins = 0;
+				_fredWins = 0;
+				_scoobyWins = 0;
+				_yogiWins = 0;
+				_georgeWins = 0;
+				_topCatWins = 0;
 
 				displayBooth(stBack);
-				playAV(g_rtfHandle, 5 - (g_isDemo ? 3 : 0),
-					   g_displayWidth, g_displayHeight,
-					   g_displayX, g_displayY);
-
-				if (g_lettersWon != 0) {
-					freeBOLTGroup(g_boothsBoltLib, 0x800 + (g_lettersWon << 8), 1);
-					if (!getBOLTGroup(g_boothsBoltLib, 0x800, 1)) {
-						g_hoveredHotspot = -1;
+				playAV(_rtfHandle, 5 - (_isDemo ? 3 : 0),
+					   _displayWidth, _displayHeight,
+					   _displayX, _displayY);
+
+				if (_lettersWon != 0) {
+					freeBOLTGroup(_boothsBoltLib, 0x800 + (_lettersWon << 8), 1);
+					if (!getBOLTGroup(_boothsBoltLib, 0x800, 1)) {
+						_hoveredHotspot = -1;
 						return true;
 					}
 
-					boltPict2Pict(&g_boothLetterSprite, memberAddr(g_boothsBoltLib, g_displayMode ? 0x801 : 0x800));
+					boltPict2Pict(&_boothLetterSprite, memberAddr(_boothsBoltLib, _displayMode ? 0x801 : 0x800));
 				}
 
 				displayBooth(stFront);
-				g_lettersWon = 0;
+				_lettersWon = 0;
 				_xp->showCursor();
 				return false;
 
@@ -861,7 +864,7 @@ bool BoltEngine::handleButtonPress(int16 hotspot) {
 		} else {
 			switch (hotspot) {
 			case 3:
-				startCycle(g_boothSceneDesc);
+				startCycle(_boothSceneDesc);
 				_xp->startTimer(2000);
 				return true;
 
@@ -898,12 +901,12 @@ void BoltEngine::blastColors(byte **paletteTable, int16 index, int16 mode) {
 }
 
 void BoltEngine::setColors(int16 index) {
-	if (g_currentBoothScene == 9) {
+	if (_currentBoothScene == 9) {
 		switch (index) {
 		case 0:
 		case 1:
 		case 2:
-			blastColors(g_boothAnimDescs, index, 1);
+			blastColors(_boothAnimDescs, index, 1);
 			break;
 
 		case 3:
@@ -913,11 +916,11 @@ void BoltEngine::setColors(int16 index) {
 		case 5:
 		case 6:
 		case 7:
-			blastColors(g_boothAnimDescs, index - 2, 1);
+			blastColors(_boothAnimDescs, index - 2, 1);
 			break;
 
 		case 8:
-			displayPic(g_boothVisitSignOn, 0, 0, stFront);
+			displayPic(_boothVisitSignOn, 0, 0, stFront);
 			_xp->updateDisplay();
 			break;
 		}
@@ -926,33 +929,33 @@ void BoltEngine::setColors(int16 index) {
 		case 0:
 		case 1:
 		case 2:
-			blastColors(g_boothAnimDescs, index, 1);
+			blastColors(_boothAnimDescs, index, 1);
 			break;
 
 		case 3:
-			if (g_currentBoothScene == 9)
+			if (_currentBoothScene == 9)
 				break;
 
 			for (int16 i = 0; i < 4; i++)
-				blastColors(g_boothHotPalDescs, i, 1);
+				blastColors(_boothHotPalDescs, i, 1);
 
 			break;
 
 		case 4:
-			blastColors(g_boothAnimDescs, index - 1, 1);
+			blastColors(_boothAnimDescs, index - 1, 1);
 			break;
 		}
 	}
 }
 
 void BoltEngine::restoreColors(int16 index) {
-	if (g_currentBoothScene == 9) {
+	if (_currentBoothScene == 9) {
 		switch (index) {
 		case 0:
 		case 1:
 		case 2: {
-			byte *palData = g_savedPalettes[index];
-			byte *animDesc = g_boothAnimDescs[index];
+			byte *palData = _savedPalettes[index];
+			byte *animDesc = _boothAnimDescs[index];
 			_xp->setPalette((int16)READ_UINT16(animDesc + 2), (int16)READ_UINT16(animDesc), palData);
 			break;
 		}
@@ -964,14 +967,14 @@ void BoltEngine::restoreColors(int16 index) {
 		case 5:
 		case 6:
 		case 7: {
-			byte *palData = g_savedPalettes[index - 2];
-			byte *animDesc = g_boothAnimDescs[index - 2];
+			byte *palData = _savedPalettes[index - 2];
+			byte *animDesc = _boothAnimDescs[index - 2];
 			_xp->setPalette((int16)READ_UINT16(animDesc + 2), (int16)READ_UINT16(animDesc), palData);
 			break;
 		}
 
 		case 8:
-			displayPic(g_boothVisitSignOff, 0, 0, stFront);
+			displayPic(_boothVisitSignOff, 0, 0, stFront);
 			_xp->updateDisplay();
 			break;
 		}
@@ -980,24 +983,24 @@ void BoltEngine::restoreColors(int16 index) {
 		case 0:
 		case 1:
 		case 2: {
-			byte *palData = g_savedPalettes[index];
-			byte *animDesc = g_boothAnimDescs[index];
+			byte *palData = _savedPalettes[index];
+			byte *animDesc = _boothAnimDescs[index];
 			_xp->setPalette((int16)READ_UINT16(animDesc + 2), (int16)READ_UINT16(animDesc), palData);
 			break;
 		}
 
 		case 3:
 			for (int16 i = 0; i < 4; i++) {
-				byte *palData = g_savedHotPalettes[i];
-				byte *palDesc = g_boothHotPalDescs[i];
+				byte *palData = _savedHotPalettes[i];
+				byte *palDesc = _boothHotPalDescs[i];
 				_xp->setPalette((int16)READ_UINT16(palDesc + 2), (int16)READ_UINT16(palDesc), palData);
 			}
 			break;
 
 		case 4:
 		{
-			byte *palData = g_savedPalettes[index - 1];
-			byte *animDesc = g_boothAnimDescs[index - 1];
+			byte *palData = _savedPalettes[index - 1];
+			byte *animDesc = _boothAnimDescs[index - 1];
 			_xp->setPalette((int16)READ_UINT16(animDesc + 2), (int16)READ_UINT16(animDesc), palData);
 			break;
 		}
@@ -1006,20 +1009,20 @@ void BoltEngine::restoreColors(int16 index) {
 }
 
 void BoltEngine::loadColors() {
-	for (int16 i = 0; i < g_boothNumAnimations; i++) {
-		byte *animDesc = g_boothAnimDescs[i];
-		_xp->getPalette((int16)READ_UINT16(animDesc), (int16)READ_UINT16(animDesc + 2), g_savedPalettes[i]);
+	for (int16 i = 0; i < _boothNumAnimations; i++) {
+		byte *animDesc = _boothAnimDescs[i];
+		_xp->getPalette((int16)READ_UINT16(animDesc), (int16)READ_UINT16(animDesc + 2), _savedPalettes[i]);
 	}
 
-	if (g_currentBoothScene == 9) {
-		int16 last = g_boothNumAnimations - 1;
-		byte *animDesc = g_boothAnimDescs[last];
-		_xp->getPalette((int16)READ_UINT16(animDesc), (int16)READ_UINT16(animDesc + 2), g_savedPalettes[last]);
+	if (_currentBoothScene == 9) {
+		int16 last = _boothNumAnimations - 1;
+		byte *animDesc = _boothAnimDescs[last];
+		_xp->getPalette((int16)READ_UINT16(animDesc), (int16)READ_UINT16(animDesc + 2), _savedPalettes[last]);
 	}
 
 	for (int16 i = 0; i < 4; i++) {
-		byte *palDesc = g_boothHotPalDescs[i];
-		_xp->getPalette((int16)READ_UINT16(palDesc), (int16)READ_UINT16(palDesc + 2), g_savedHotPalettes[i]);
+		byte *palDesc = _boothHotPalDescs[i];
+		_xp->getPalette((int16)READ_UINT16(palDesc), (int16)READ_UINT16(palDesc + 2), _savedHotPalettes[i]);
 	}
 }
 
@@ -1029,116 +1032,116 @@ void BoltEngine::shiftColorMap(byte *colorMap, int16 delta) {
 }
 
 void BoltEngine::playBoothAV() {
-	switch (g_currentBoothScene) {
+	switch (_currentBoothScene) {
 	case 3:
-		playAVOverBooth(6 - (g_isDemo ? 3 : 0));
+		playAVOverBooth(6 - (_isDemo ? 3 : 0));
 		break;
 	case 4:
-		playAVOverBooth(7 - (g_isDemo ? 3 : 0));
+		playAVOverBooth(7 - (_isDemo ? 3 : 0));
 		break;
 	case 5:
-		playAVOverBooth(8 - (g_isDemo ? 3 : 0));
+		playAVOverBooth(8 - (_isDemo ? 3 : 0));
 		break;
 	case 6:
-		playAVOverBooth(9 - (g_isDemo ? 3 : 0));
+		playAVOverBooth(9 - (_isDemo ? 3 : 0));
 		break;
 	case 7:
-		playAVOverBooth(10 - (g_isDemo ? 3 : 0));
+		playAVOverBooth(10 - (_isDemo ? 3 : 0));
 		break;
 	case 8:
-		playAVOverBooth(11 - (g_isDemo ? 3 : 0));
+		playAVOverBooth(11 - (_isDemo ? 3 : 0));
 		break;
 	}
 }
 
 void BoltEngine::mainEntranceHelpBlink() {
-	g_tourStep++;
+	_tourStep++;
 
-	switch (g_tourStep) {
+	switch (_tourStep) {
 	case 1:
 		setColors(0);
 		setColors(1);
-		g_keyLeft = 1;
-		g_keyReleased = 1;
-		g_helpTimer = _xp->startTimer(500);
+		_keyLeft = 1;
+		_keyReleased = 1;
+		_helpTimer = _xp->startTimer(500);
 		break;
 
 	case 2:
-		g_keyLeft = 0;
-		g_keyReleased = 0;
+		_keyLeft = 0;
+		_keyReleased = 0;
 
-		if (!g_boothHotspotDescs[0].contains(g_cursorX, g_cursorY))
+		if (!_boothHotspotDescs[0].contains(_cursorX, _cursorY))
 			restoreColors(0);
 
-		if (!g_boothHotspotDescs[1].contains(g_cursorX, g_cursorY))
+		if (!_boothHotspotDescs[1].contains(_cursorX, _cursorY))
 			restoreColors(1);
 
 		return;
 
 	case 3:
 		setColors(7);
-		g_keyRight = 1;
-		g_keyReleased = 1;
-		g_helpTimer = _xp->startTimer(500);
+		_keyRight = 1;
+		_keyReleased = 1;
+		_helpTimer = _xp->startTimer(500);
 		break;
 
 	case 4:
-		g_keyRight = 0;
-		g_keyReleased = 0;
+		_keyRight = 0;
+		_keyReleased = 0;
 
-		if (!g_boothHotspotDescs[7] .contains(g_cursorX, g_cursorY))
+		if (!_boothHotspotDescs[7] .contains(_cursorX, _cursorY))
 			restoreColors(7);
 		return;
 
 	case 5:
 		setColors(6);
-		g_keyUp = 1;
-		g_keyReleased = 1;
-		g_helpTimer = _xp->startTimer(500);
+		_keyUp = 1;
+		_keyReleased = 1;
+		_helpTimer = _xp->startTimer(500);
 		break;
 
 	case 6:
-		g_keyUp = 0;
-		g_keyReleased = 0;
+		_keyUp = 0;
+		_keyReleased = 0;
 
-		if (!g_boothHotspotDescs[6].contains(g_cursorX, g_cursorY))
+		if (!_boothHotspotDescs[6].contains(_cursorX, _cursorY))
 			restoreColors(6);
 		return;
 
 	case 7:
 		setColors(5);
-		g_keyDown = 1;
-		g_keyReleased = 1;
-		g_helpTimer = _xp->startTimer(500);
+		_keyDown = 1;
+		_keyReleased = 1;
+		_helpTimer = _xp->startTimer(500);
 		break;
 
 	case 8:
-		g_keyDown = 0;
-		g_keyReleased = 0;
-		g_helpTimer = 0;
+		_keyDown = 0;
+		_keyReleased = 0;
+		_helpTimer = 0;
 
-		if (!g_boothHotspotDescs[5].contains(g_cursorX, g_cursorY))
+		if (!_boothHotspotDescs[5].contains(_cursorX, _cursorY))
 			restoreColors(5);
 		return;
 	}
 }
 
 void BoltEngine::boothHelpBlink() {
-	g_tourStep++;
+	_tourStep++;
 
-	switch (g_tourStep) {
+	switch (_tourStep) {
 	case 1:
 		setColors(3);
-		g_keyEnter = 1;
-		g_keyReleased = 1;
-		g_helpTimer = _xp->startTimer(500);
+		_keyEnter = 1;
+		_keyReleased = 1;
+		_helpTimer = _xp->startTimer(500);
 		break;
 
 	case 2:
-		g_keyEnter = 0;
-		g_keyReleased = 0;
+		_keyEnter = 0;
+		_keyReleased = 0;
 
-		if (!g_boothHotspotDescs[3].contains(g_cursorX, g_cursorY))
+		if (!_boothHotspotDescs[3].contains(_cursorX, _cursorY))
 			restoreColors(3);
 
 		return;
@@ -1146,20 +1149,20 @@ void BoltEngine::boothHelpBlink() {
 	case 3:
 		setColors(0);
 		setColors(1);
-		g_keyLeft = 1;
-		g_keyReleased = 1;
-		g_helpTimer = _xp->startTimer(500);
+		_keyLeft = 1;
+		_keyReleased = 1;
+		_helpTimer = _xp->startTimer(500);
 		break;
 
 	case 4:
-		g_keyLeft = 0;
-		g_keyReleased = 0;
-		g_helpTimer = 0;
+		_keyLeft = 0;
+		_keyReleased = 0;
+		_helpTimer = 0;
 
-		if (!g_boothHotspotDescs[0].contains(g_cursorX, g_cursorY))
+		if (!_boothHotspotDescs[0].contains(_cursorX, _cursorY))
 			restoreColors(0);
 
-		if (!g_boothHotspotDescs[1].contains(g_cursorX, g_cursorY))
+		if (!_boothHotspotDescs[1].contains(_cursorX, _cursorY))
 			restoreColors(1);
 
 		return;
@@ -1172,14 +1175,14 @@ void BoltEngine::tourPaletteCycleStep() {
 		-1,    0x736, 0x73B, 0x73F, 0x743, -1
 	};
 
-	g_tourStep++;
+	_tourStep++;
 
-	if ((g_tourStep & 1) == 0) {
+	if ((_tourStep & 1) == 0) {
 		_xp->stopCycle();
 		return;
 	}
 
-	int16 index = g_tourStep >> 1;
+	int16 index = _tourStep >> 1;
 	if (index > 13)
 		return;
 
@@ -1187,7 +1190,7 @@ void BoltEngine::tourPaletteCycleStep() {
 	if (resId == -1)
 		return;
 
-	startCycle(memberAddr(g_boothsBoltLib, resId));
+	startCycle(memberAddr(_boothsBoltLib, resId));
 }
 
 void BoltEngine::fadeToBlack(int16 steps) {
@@ -1237,61 +1240,61 @@ void BoltEngine::flushInput() {
 }
 
 int16 BoltEngine::winALetter(int16 prevBooth) {
-	g_lettersWon++;
+	_lettersWon++;
 
-	if (g_isDemo && g_lettersWon == 6)
+	if (_isDemo && _lettersWon == 6)
 		return 18;
 
-	if (g_lettersWon == 15) {
-		if (g_allLettersWonFlag) {
-			g_allLettersWonFlag = false;
-			g_lettersWon = 16;
+	if (_lettersWon == 15) {
+		if (_allLettersWonFlag) {
+			_allLettersWonFlag = false;
+			_lettersWon = 16;
 		} else {
-			g_allLettersWonFlag = true;
+			_allLettersWonFlag = true;
 		}
 	}
 
-	int16 avIndex = g_isDemo ? 25 : 31;
-	playAV(g_rtfHandle, g_lettersWon + avIndex, g_displayWidth, g_displayHeight, g_displayX, g_displayY);
+	int16 avIndex = _isDemo ? 25 : 31;
+	playAV(_rtfHandle, _lettersWon + avIndex, _displayWidth, _displayHeight, _displayX, _displayY);
 
-	if (g_lettersWon >= 15)
-		g_lettersWon = 0;
+	if (_lettersWon >= 15)
+		_lettersWon = 0;
 
 	// Return the booth front ID for the game just played
 	switch (prevBooth) {
 	case 10:
-		if (g_isDemo) {
-			g_huckWins++;
+		if (_isDemo) {
+			_huckWins++;
 		}
 
 		return 3; // HuckGame    -> HucksBooth
 	case 11:
-		if (g_isDemo) {
-			g_fredWins++;
+		if (_isDemo) {
+			_fredWins++;
 		}
 
 		return 4; // FredGame    -> FredsBooth
 	case 12:
-		if (g_isDemo) {
-			g_scoobyWins++;
+		if (_isDemo) {
+			_scoobyWins++;
 		}
 
 		return 5; // ScoobyGame  -> ScoobysBooth
 	case 13:
-		if (g_isDemo) {
-			g_yogiWins++;
+		if (_isDemo) {
+			_yogiWins++;
 		}
 
 		return 6; // YogiGame    -> YogisBooth
 	case 14:
-		if (g_isDemo) {
-			g_georgeWins++;
+		if (_isDemo) {
+			_georgeWins++;
 		}
 
 		return 7; // GeorgeGame  -> GeorgesBooth
 	case 15:
-		if (g_isDemo) {
-			g_topCatWins++;
+		if (_isDemo) {
+			_topCatWins++;
 		}
 
 		return 8; // TopCatGame  -> TopCatsBooth
@@ -1304,10 +1307,10 @@ int16 BoltEngine::endDemo(int16 prevBooth) {
 	_xp->hideCursor();
 	_xp->setTransparency(false);
 
-	int16 baseId = (g_displayMode != 0) ? 0x0801 : 0x0800;
-	int16 memberId = baseId + (g_lettersWon << 8);
-	displayPic(getBOLTMember(g_boothsBoltLib, memberId), g_displayX, g_displayY, stFront);
-	displayColors(getBOLTMember(g_boothsBoltLib, 0x718), stFront, 0);
+	int16 baseId = (_displayMode != 0) ? 0x0801 : 0x0800;
+	int16 memberId = baseId + (_lettersWon << 8);
+	displayPic(getBOLTMember(_boothsBoltLib, memberId), _displayX, _displayY, stFront);
+	displayColors(getBOLTMember(_boothsBoltLib, 0x718), stFront, 0);
 
 	_xp->updateDisplay();
 
diff --git a/engines/bolt/booths/fred.cpp b/engines/bolt/booths/fred.cpp
index ceaa40d8ebe..dd2158b1b78 100644
--- a/engines/bolt/booths/fred.cpp
+++ b/engines/bolt/booths/fred.cpp
@@ -41,64 +41,64 @@ int16 BoltEngine::fredGame(int16 prevBooth) {
 bool BoltEngine::initFred() {
 	const char *path = assetPath("fred.blt");
 
-	if (!openBOLTLib(&g_fredBoltLib, &g_fredBoltCallbacks, path))
+	if (!openBOLTLib(&_fredBoltLib, &_fredBoltCallbacks, path))
 		return false;
 
-	if (!getBOLTGroup(g_fredBoltLib, 0, 1))
+	if (!getBOLTGroup(_fredBoltLib, 0, 1))
 		return false;
 
 	// Load assets...
-	g_fredBackground = memberAddr(g_fredBoltLib, (g_displayMode != 0) ? 5 : 4);
+	_fredBackground = memberAddr(_fredBoltLib, (_displayMode != 0) ? 5 : 4);
 
-	g_fredBalloonString = memberAddr(g_fredBoltLib, 6);
+	_fredBalloonString = memberAddr(_fredBoltLib, 6);
 
-	g_fredFacingLeftRect = memberAddr(g_fredBoltLib, 0);
-	g_fredFacingRightRect = memberAddr(g_fredBoltLib, 1);
-	g_fredTurningRect = memberAddr(g_fredBoltLib, 2);
-	g_fredBalloonRect = memberAddr(g_fredBoltLib, 3);
+	_fredFacingLeftRect = memberAddr(_fredBoltLib, 0);
+	_fredFacingRightRect = memberAddr(_fredBoltLib, 1);
+	_fredTurningRect = memberAddr(_fredBoltLib, 2);
+	_fredBalloonRect = memberAddr(_fredBoltLib, 3);
 
-	g_fredHelpEntries = memberAddr(g_fredBoltLib, 0x38);
-	g_fredPlayButton = memberAddr(g_fredBoltLib, 0x39);
-	g_fredTimer = 0;
+	_fredHelpEntries = memberAddr(_fredBoltLib, 0x38);
+	_fredPlayButton = memberAddr(_fredBoltLib, 0x39);
+	_fredTimer = 0;
 
 	for (int i = 0; i < 10; i++)
-		g_fredSprites[i] = memberAddr(g_fredBoltLib, 0x28 + i);
+		_fredSprites[i] = memberAddr(_fredBoltLib, 0x28 + i);
 
-	getFredSoundInfo(g_fredBoltLib, 0x3A, &g_fredSounds[0]);
-	getFredSoundInfo(g_fredBoltLib, 0x3B, &g_fredSounds[1]);
-	getFredSoundInfo(g_fredBoltLib, 0x3C, &g_fredSounds[2]);
-	getFredSoundInfo(g_fredBoltLib, 0x3D, &g_fredSounds[3]);
+	getFredSoundInfo(_fredBoltLib, 0x3A, &_fredSounds[0]);
+	getFredSoundInfo(_fredBoltLib, 0x3B, &_fredSounds[1]);
+	getFredSoundInfo(_fredBoltLib, 0x3C, &_fredSounds[2]);
+	getFredSoundInfo(_fredBoltLib, 0x3D, &_fredSounds[3]);
 
-	g_fredLoopSound = nullptr;
-	g_fredCurrentSound = nullptr;
-	g_fredPendingLoop = nullptr;
-	g_fredPendingOneShot = nullptr;
+	_fredLoopSound = nullptr;
+	_fredCurrentSound = nullptr;
+	_fredPendingLoop = nullptr;
+	_fredPendingOneShot = nullptr;
 
 	// Init Fred sprite struct...
-	g_fredSprite.flags = 1;
-	g_fredSprite.direction = 0;
-	g_fredSprite.xPos = 0xA000; // Fixed-point coordinate
-	g_fredSprite.yPos = 0x1600;	// Fixed-point coordinate
-	g_fredSprite.speed = 0x600;
-	g_fredSprite.pathTable = nullptr;
+	_fredSprite.flags = 1;
+	_fredSprite.direction = 0;
+	_fredSprite.xPos = 0xA000; // Fixed-point coordinate
+	_fredSprite.yPos = 0x1600;	// Fixed-point coordinate
+	_fredSprite.speed = 0x600;
+	_fredSprite.pathTable = nullptr;
 
-	setFredAnimMode(&g_fredSprite, 0);
+	setFredAnimMode(&_fredSprite, 0);
 
 	// Load save data...
-	if (!vLoad(&g_fredSaveData, g_fredSaveFile)) {
-		g_fredSaveData[0] = 0; // Balloon catch count
-		g_fredSaveData[1] = 0; // Level data group index, increments on level complete, caps at 9
-		g_fredSaveData[2] = 0; // Palette group index, increments on level complete, wraps at 10
+	if (!vLoad(&_fredSaveData, "FredBC")) {
+		_fredSaveData[0] = 0; // Balloon catch count
+		_fredSaveData[1] = 0; // Level data group index, increments on level complete, caps at 9
+		_fredSaveData[2] = 0; // Palette group index, increments on level complete, wraps at 10
 	}
 
-	if (!initFredLevel(g_fredSaveData[1], g_fredSaveData[2]))
+	if (!initFredLevel(_fredSaveData[1], _fredSaveData[2]))
 		return false;
 
-	g_fredLevelIndex = 0;
-	while (g_fredLevelIndex < g_fredSaveData[0]) {
-		FredEntityState *entry = g_fredEntitiesTable[g_fredLevelIndex + 1];
+	_fredLevelIndex = 0;
+	while (_fredLevelIndex < _fredSaveData[0]) {
+		FredEntityState *entry = _fredEntitiesTable[_fredLevelIndex + 1];
 		setFredAnimMode(entry, 10);
-		g_fredLevelIndex++;
+		_fredLevelIndex++;
 	}
 
 	// Flush timer events...
@@ -109,50 +109,50 @@ bool BoltEngine::initFred() {
 	_xp->setTransparency(false);
 
 	// Display background on back and front buffers...
-	displayColors(g_fredPalette, stBack, 0);
-	displayPic(g_fredBackground, g_displayX, g_displayY, stFront);
+	displayColors(_fredPalette, stBack, 0);
+	displayPic(_fredBackground, _displayX, _displayY, stFront);
 	_xp->updateDisplay();
 
 	_xp->setTransparency(true);
-	displayColors(g_fredPalette, stFront, 0);
-	displayColors(g_fredPalette, stBack, 1);
-	displayPic(g_fredBackground, g_displayX, g_displayY, stBack);
+	displayColors(_fredPalette, stFront, 0);
+	displayColors(_fredPalette, stBack, 1);
+	displayPic(_fredBackground, _displayX, _displayY, stBack);
 
 	renderFredScene();
 	_xp->updateDisplay();
 
-	_xp->startCycle(g_fredCycleSpecs);
+	_xp->startCycle(_fredCycleSpecs);
 	_xp->setFrameRate(15);
 
 	return true;
 }
 
 void BoltEngine::cleanUpFred() {
-	int16 savedLevel = g_fredSaveData[1]; // Level data group index
-	int16 savedPalette = g_fredSaveData[2]; // Palette group index
+	int16 savedLevel = _fredSaveData[1]; // Level data group index
+	int16 savedPalette = _fredSaveData[2]; // Palette group index
 
-	g_fredSaveData[0] = g_fredLevelIndex;
+	_fredSaveData[0] = _fredLevelIndex;
 
-	byte *levelPtr = g_fredLevelPtr;
-	if (READ_UINT16(levelPtr + 0x0A) <= g_fredLevelIndex) {
-		g_fredSaveData[1]++;
-		if (g_fredSaveData[1] >= 10)
-			g_fredSaveData[1] = 9;
+	byte *levelPtr = _fredLevelPtr;
+	if (READ_UINT16(levelPtr + 0x0A) <= _fredLevelIndex) {
+		_fredSaveData[1]++;
+		if (_fredSaveData[1] >= 10)
+			_fredSaveData[1] = 9;
 
-		g_fredSaveData[2]++;
-		if (g_fredSaveData[2] >= 10)
-			g_fredSaveData[2] = 0;
+		_fredSaveData[2]++;
+		if (_fredSaveData[2] >= 10)
+			_fredSaveData[2] = 0;
 
-		g_fredSaveData[0] = 0;
+		_fredSaveData[0] = 0;
 	}
 
-	vSave(&g_fredSaveData, sizeof(g_fredSaveData), g_fredSaveFile);
+	vSave(&_fredSaveData, sizeof(_fredSaveData), "FredBC");
 
 	_xp->stopCycle();
 	termFredLevel(savedLevel, savedPalette);
 
-	freeBOLTGroup(g_fredBoltLib, 0, 1);
-	closeBOLTLib(&g_fredBoltLib);
+	freeBOLTGroup(_fredBoltLib, 0, 1);
+	closeBOLTLib(&_fredBoltLib);
 
 	_xp->setFrameRate(0);
 	_xp->fillDisplay(0, stFront);
@@ -161,49 +161,49 @@ void BoltEngine::cleanUpFred() {
 
 bool BoltEngine::initFredLevel(int16 levelGroup, int16 palGroup) {
 	// Load level data group...
-	if (!getBOLTGroup(g_fredBoltLib, levelGroup * 0x200 + 0x100, 1))
+	if (!getBOLTGroup(_fredBoltLib, levelGroup * 0x200 + 0x100, 1))
 		return false;
 
 	// Load palette group...
-	if (!getBOLTGroup(g_fredBoltLib, palGroup * 0x200 + 0x200, 1)) {
-		freeBOLTGroup(g_fredBoltLib, levelGroup * 0x200 + 0x100, 1);
+	if (!getBOLTGroup(_fredBoltLib, palGroup * 0x200 + 0x200, 1)) {
+		freeBOLTGroup(_fredBoltLib, levelGroup * 0x200 + 0x100, 1);
 		return false;
 	}
 
 	// Level descriptor member...
-	g_fredLevelPtr = memberAddr(g_fredBoltLib, levelGroup * 0x200 + 0x100);
+	_fredLevelPtr = memberAddr(_fredBoltLib, levelGroup * 0x200 + 0x100);
 
 	// Palette member...
-	g_fredPalette = memberAddr(g_fredBoltLib, palGroup * 0x200 + 0x200);
+	_fredPalette = memberAddr(_fredBoltLib, palGroup * 0x200 + 0x200);
 
 	// Cycle data...
-	g_fredCycleRaw = memberAddr(g_fredBoltLib, palGroup * 0x200 + 0x20A);
-	boltCycleToXPCycle(g_fredCycleRaw, g_fredCycleSpecs);
+	_fredCycleRaw = memberAddr(_fredBoltLib, palGroup * 0x200 + 0x20A);
+	boltCycleToXPCycle(_fredCycleRaw, _fredCycleSpecs);
 
 	// Other resources...
-	g_fredBalloonSprite = memberAddr(g_fredBoltLib, palGroup * 0x200 + 0x209);
-	g_fredPathMatrix = memberAddr(g_fredBoltLib, levelGroup * 0x200 + 0x132);
-	g_fredRowBounds = memberAddr(g_fredBoltLib, levelGroup * 0x200 + 0x101);
+	_fredBalloonSprite = memberAddr(_fredBoltLib, palGroup * 0x200 + 0x209);
+	_fredPathMatrix = memberAddr(_fredBoltLib, levelGroup * 0x200 + 0x132);
+	_fredRowBounds = memberAddr(_fredBoltLib, levelGroup * 0x200 + 0x101);
 
 	// Allocate shuffle table: numRows * (numCols + 1)
-	uint16 numRows = READ_UINT16(g_fredLevelPtr + 4);
-	uint16 numCols = READ_UINT16(g_fredLevelPtr + 6);
+	uint16 numRows = READ_UINT16(_fredLevelPtr + 4);
+	uint16 numCols = READ_UINT16(_fredLevelPtr + 6);
 
-	g_fredShuffleTable = (byte *)_xp->allocMem(numRows * (numCols + 1));
-	if (!g_fredShuffleTable) {
+	_fredShuffleTable = (byte *)_xp->allocMem(numRows * (numCols + 1));
+	if (!_fredShuffleTable) {
 		termFredLevel(levelGroup, palGroup);
 		return false;
 	}
 
 	// Build and shuffle column indices for each row...
 	for (int16 row = 0; row < numRows; row++) {
-		numCols = READ_UINT16(g_fredLevelPtr + 6);
+		numCols = READ_UINT16(_fredLevelPtr + 6);
 		int16 stride = numCols + 1;
 		int16 rowBase = stride * row;
-		g_fredShuffleTable[rowBase] = 0;
+		_fredShuffleTable[rowBase] = 0;
 
 		for (int16 col = 0; col < numCols; col++) {
-			g_fredShuffleTable[rowBase + col + 1] = (byte)col;
+			_fredShuffleTable[rowBase + col + 1] = (byte)col;
 		}
 
 		// Fisher-Yates shuffle...
@@ -212,16 +212,16 @@ bool BoltEngine::initFredLevel(int16 levelGroup, int16 palGroup) {
 			int16 posA = rowBase + col + 1;
 			int16 posB = rowBase + randIdx + 1;
 
-			byte tmp = g_fredShuffleTable[posA];
-			g_fredShuffleTable[posA] = g_fredShuffleTable[posB];
-			g_fredShuffleTable[posB] = tmp;
+			byte tmp = _fredShuffleTable[posA];
+			_fredShuffleTable[posA] = _fredShuffleTable[posB];
+			_fredShuffleTable[posB] = tmp;
 		}
 	}
 
 	// Allocate balloon table...
-	uint16 numBalloons = READ_UINT16(g_fredLevelPtr + 0x0A);
-	g_fredEntitiesTable = (FredEntityState **)_xp->allocMem((numBalloons + 2) * sizeof(FredEntityState *));
-	if (!g_fredEntitiesTable) {
+	uint16 numBalloons = READ_UINT16(_fredLevelPtr + 0x0A);
+	_fredEntitiesTable = (FredEntityState **)_xp->allocMem((numBalloons + 2) * sizeof(FredEntityState *));
+	if (!_fredEntitiesTable) {
 		termFredLevel(levelGroup, palGroup);
 		return false;
 	}
@@ -236,17 +236,17 @@ bool BoltEngine::initFredLevel(int16 levelGroup, int16 palGroup) {
 		}
 
 		entry->flags = 0;
-		g_fredEntitiesTable[i + 1] = entry;
+		_fredEntitiesTable[i + 1] = entry;
 	}
 
 	// Sentinel: null pointer after last balloon...
-	g_fredEntitiesTable[numBalloons + 1] = nullptr;
+	_fredEntitiesTable[numBalloons + 1] = nullptr;
 
 	// First entry points to Fred's sprite...
-	g_fredEntitiesTable[0] = &g_fredSprite;
+	_fredEntitiesTable[0] = &_fredSprite;
 
 	// Allocate balloon type shuffle array...
-	uint16 numTypes = READ_UINT16(g_fredLevelPtr + 0x0C);
+	uint16 numTypes = READ_UINT16(_fredLevelPtr + 0x0C);
 	byte *typeShuf = (byte *)_xp->allocMem(numTypes);
 	if (!typeShuf) {
 		termFredLevel(levelGroup, palGroup);
@@ -270,49 +270,49 @@ bool BoltEngine::initFredLevel(int16 levelGroup, int16 palGroup) {
 
 		// Assign balloon type...
 		byte balloonType = typeShuf[i % numTypes] + 0x0B;
-		FredEntityState *balloonEntry = g_fredEntitiesTable[i + 1];
+		FredEntityState *balloonEntry = _fredEntitiesTable[i + 1];
 		setFredAnimMode(balloonEntry, balloonType);
 	}
 
 	_xp->freeMem(typeShuf);
 
-	g_fredBalloonSpawnDelay = calcBalloonSpawnDelay();
-	g_fredBalloonSearchIdx = 0;
-	g_fredLevelIndex = 0;
+	_fredBalloonSpawnDelay = calcBalloonSpawnDelay();
+	_fredBalloonSearchIdx = 0;
+	_fredLevelIndex = 0;
 
 	return true;
 }
 
 void BoltEngine::termFredLevel(int16 levelGroup, int16 palGroup) {
 	// Free balloon state structs...
-	if (g_fredEntitiesTable) {
-		uint16 numBalloons = READ_UINT16(g_fredLevelPtr + 0x0A);
+	if (_fredEntitiesTable) {
+		uint16 numBalloons = READ_UINT16(_fredLevelPtr + 0x0A);
 		for (int16 i = 0; i < numBalloons; i++) {
-			FredEntityState *entry = g_fredEntitiesTable[i + 1];
+			FredEntityState *entry = _fredEntitiesTable[i + 1];
 			if (entry) {
 				delete entry;
-				g_fredEntitiesTable[i + 1] = nullptr;
+				_fredEntitiesTable[i + 1] = nullptr;
 			}
 		}
 
-		delete[] g_fredEntitiesTable;
-		g_fredEntitiesTable = nullptr;
+		delete[] _fredEntitiesTable;
+		_fredEntitiesTable = nullptr;
 	}
 
 	// Free shuffle table...
-	if (g_fredShuffleTable) {
-		_xp->freeMem(g_fredShuffleTable);
-		g_fredShuffleTable = nullptr;
+	if (_fredShuffleTable) {
+		_xp->freeMem(_fredShuffleTable);
+		_fredShuffleTable = nullptr;
 	}
 
 	// Free BOLT groups...
-	freeBOLTGroup(g_fredBoltLib, palGroup * 0x200 + 0x200, 1);
-	freeBOLTGroup(g_fredBoltLib, levelGroup * 0x200 + 0x100, 1);
+	freeBOLTGroup(_fredBoltLib, palGroup * 0x200 + 0x200, 1);
+	freeBOLTGroup(_fredBoltLib, levelGroup * 0x200 + 0x100, 1);
 }
 
 void BoltEngine::swapFredAnimEntry() {
-	byte *data = g_boltCurrentMemberEntry->dataPtr;
-	uint32 decompSize = g_boltCurrentMemberEntry->decompSize;
+	byte *data = _boltCurrentMemberEntry->dataPtr;
+	uint32 decompSize = _boltCurrentMemberEntry->decompSize;
 	uint32 offset = 0;
 
 	while (offset < decompSize) {
@@ -323,8 +323,8 @@ void BoltEngine::swapFredAnimEntry() {
 }
 
 void BoltEngine::swapFredAnimDesc() {
-	byte *data = g_boltCurrentMemberEntry->dataPtr;
-	uint32 decompSize = g_boltCurrentMemberEntry->decompSize;
+	byte *data = _boltCurrentMemberEntry->dataPtr;
+	uint32 decompSize = _boltCurrentMemberEntry->decompSize;
 	uint32 offset = 0;
 	byte *ptr = data;
 	byte *colorCountPtr = data + 0x0E;
@@ -348,7 +348,7 @@ void BoltEngine::swapFredAnimDesc() {
 }
 
 void BoltEngine::swapFredLevelDesc() {
-	byte *data = g_boltCurrentMemberEntry->dataPtr;
+	byte *data = _boltCurrentMemberEntry->dataPtr;
 
 	WRITE_UINT16(data + 0x00, READ_BE_INT16(data + 0x00));
 	WRITE_UINT16(data + 0x02, READ_BE_INT16(data + 0x02));
@@ -366,7 +366,7 @@ int16 BoltEngine::playFred() {
 	int16 joystickX = 0;
 
 	// Show help on first play...
-	if (g_fredShowHelp != 0) {
+	if (_fredShowHelp != 0) {
 		if (!helpFred())
 			return 4;
 	}
@@ -389,8 +389,8 @@ int16 BoltEngine::playFred() {
 			case etMouseDown:
 			case etInactivity:
 				if (!allCaught) {
-					int16 helpMode = (g_fredSprite.animMode == 8 || g_fredSprite.animMode == 6) ? 1 : 0;
-					setFredAnimMode(&g_fredSprite, helpMode);
+					int16 helpMode = (_fredSprite.animMode == 8 || _fredSprite.animMode == 6) ? 1 : 0;
+					setFredAnimMode(&_fredSprite, helpMode);
 					if (!helpFred()) {
 						result = 4;
 						exitLoop = 1;
@@ -414,17 +414,17 @@ int16 BoltEngine::playFred() {
 		_xp->updateDisplay();
 
 		// Balloon spawn timer...
-		g_fredBalloonSpawnDelay--;
-		if (g_fredBalloonSpawnDelay == 0) {
+		_fredBalloonSpawnDelay--;
+		if (_fredBalloonSpawnDelay == 0) {
 			if (spawnBalloon())
-				g_fredBalloonSpawnDelay = calcBalloonSpawnDelay();
+				_fredBalloonSpawnDelay = calcBalloonSpawnDelay();
 			else
-				g_fredBalloonSpawnDelay = 1;
+				_fredBalloonSpawnDelay = 1;
 		}
 
 		// --- First pass: update Fred and balloons animations ---
 		int16 idx = 0;
-		FredEntityState *entry = g_fredEntitiesTable[idx];
+		FredEntityState *entry = _fredEntitiesTable[idx];
 		while (entry) {
 			if (entry->flags & 1) { // active
 				// Save previous position...
@@ -558,12 +558,12 @@ int16 BoltEngine::playFred() {
 			}
 
 			idx++;
-			entry = g_fredEntitiesTable[idx];
+			entry = _fredEntitiesTable[idx];
 		}
 
 		// --- Second pass: move balloons and Fred ---
 		idx = 0;
-		entry = g_fredEntitiesTable[idx];
+		entry = _fredEntitiesTable[idx];
 		while (entry) {
 			if (entry->flags & 1) {
 				int16 mode = entry->animMode;
@@ -623,7 +623,7 @@ int16 BoltEngine::playFred() {
 			}
 
 			idx++;
-			entry = g_fredEntitiesTable[idx];
+			entry = _fredEntitiesTable[idx];
 		}
 
 		// --- Skip collision check if all caught ---
@@ -632,20 +632,20 @@ int16 BoltEngine::playFred() {
 
 		// --- Get Fred's collision rect based on current mode ---
 		byte *fredRect;
-		switch (g_fredSprite.animMode) {
+		switch (_fredSprite.animMode) {
 		case 6:
 		case 8:
-			fredRect = g_fredFacingLeftRect; // facing/running left
+			fredRect = _fredFacingLeftRect; // facing/running left
 			break;
 		case 2:
 		case 4:
-			fredRect = g_fredFacingRightRect; // facing/running right
+			fredRect = _fredFacingRightRect; // facing/running right
 			break;
 		case 7:
-			fredRect = g_fredTurningRect; // turning left
+			fredRect = _fredTurningRect; // turning left
 			break;
 		case 3:
-			fredRect = g_fredTurningRect; // turning right
+			fredRect = _fredTurningRect; // turning right
 			break;
 		default:
 			fredRect = nullptr;
@@ -656,21 +656,21 @@ int16 BoltEngine::playFred() {
 			continue;
 
 		// Build Fred's bounding rect...
-		int16 fredX = ((int32)g_fredSprite.xPos >> 8) + READ_UINT16(fredRect);
-		int16 fredY = ((int32)g_fredSprite.yPos >> 8) + READ_UINT16(fredRect + 2);
+		int16 fredX = ((int32)_fredSprite.xPos >> 8) + READ_UINT16(fredRect);
+		int16 fredY = ((int32)_fredSprite.yPos >> 8) + READ_UINT16(fredRect + 2);
 		int16 fredW = READ_UINT16(fredRect + 4);
 		int16 fredH = READ_UINT16(fredRect + 6);
 
 		// --- Third pass: check collisions with balloons ---
 		idx = 0;
-		entry = g_fredEntitiesTable[idx];
+		entry = _fredEntitiesTable[idx];
 		while (entry) {
 			if ((entry->flags & 1) && entry->animMode == 0x0B) {
 				// Build balloon bounding rect...
-				int16 bx = ((int32)entry->xPos >> 8) + READ_UINT16(g_fredBalloonRect);
-				int16 by = ((int32)entry->yPos >> 8) + READ_UINT16(g_fredBalloonRect + 2);
-				int16 bw = READ_UINT16(g_fredBalloonRect + 4);
-				int16 bh = READ_UINT16(g_fredBalloonRect + 6);
+				int16 bx = ((int32)entry->xPos >> 8) + READ_UINT16(_fredBalloonRect);
+				int16 by = ((int32)entry->yPos >> 8) + READ_UINT16(_fredBalloonRect + 2);
+				int16 bw = READ_UINT16(_fredBalloonRect + 4);
+				int16 bh = READ_UINT16(_fredBalloonRect + 6);
 
 				// {x, y, w, h} rect overlap test...
 				Common::Rect balloonRect(bx, by, bx + bw, by + bh);
@@ -679,32 +679,32 @@ int16 BoltEngine::playFred() {
 				if (playerRect.intersects(balloonRect)) {
 					// Caught a balloon!
 					int16 catchMode;
-					if (g_fredSprite.animMode == 6 || g_fredSprite.animMode == 8 || g_fredSprite.animMode == 7)
+					if (_fredSprite.animMode == 6 || _fredSprite.animMode == 8 || _fredSprite.animMode == 7)
 						catchMode = 9; // catch facing left
 					else
 						catchMode = 5; // catch facing right
 
-					setFredAnimMode(&g_fredSprite, catchMode);
+					setFredAnimMode(&_fredSprite, catchMode);
 					setFredAnimMode(entry, 0x0A); // balloon disappearing
 
-					g_fredLevelIndex++;
-					if (g_fredLevelIndex >= READ_UINT16(g_fredLevelPtr + 0x0A)) {
+					_fredLevelIndex++;
+					if (_fredLevelIndex >= READ_UINT16(_fredLevelPtr + 0x0A)) {
 						allCaught = 1;
 						result = 0x10;
 					}
 
 					// Reset spawn timer...
 					if (spawnBalloon())
-						g_fredBalloonSpawnDelay = calcBalloonSpawnDelay();
+						_fredBalloonSpawnDelay = calcBalloonSpawnDelay();
 					else
-						g_fredBalloonSpawnDelay = 1;
+						_fredBalloonSpawnDelay = 1;
 
 					break; // Only catch one per frame...
 				}
 			}
 
 			idx++;
-			entry = g_fredEntitiesTable[idx];
+			entry = _fredEntitiesTable[idx];
 		}
 	}
 
@@ -714,7 +714,7 @@ int16 BoltEngine::playFred() {
 }
 
 int16 BoltEngine::helpFred() {
-	byte *firstEntry = getResolvedPtr(g_fredHelpEntries, 0);
+	byte *firstEntry = getResolvedPtr(_fredHelpEntries, 0);
 	byte *picDesc = getResolvedPtr(firstEntry, 4);
 
 	int16 cursorX = READ_UINT16(picDesc + 6) + READ_UINT16(picDesc + 0x0A) - 10;
@@ -729,10 +729,10 @@ int16 BoltEngine::helpFred() {
 	_xp->setInactivityTimer(0);
 	_xp->stopSound();
 
-	g_fredLoopSound = nullptr;
-	g_fredCurrentSound = nullptr;
-	g_fredPendingLoop = nullptr;
-	g_fredPendingOneShot = nullptr;
+	_fredLoopSound = nullptr;
+	_fredCurrentSound = nullptr;
+	_fredPendingLoop = nullptr;
+	_fredPendingOneShot = nullptr;
 
 	renderFredScene();
 
@@ -740,7 +740,7 @@ int16 BoltEngine::helpFred() {
 	int16 off = 0;
 	byte *entry;
 	while (true) {
-		entry = getResolvedPtr(g_fredHelpEntries, off);
+		entry = getResolvedPtr(_fredHelpEntries, off);
 		if (!entry)
 			break;
 		byte *pic = getResolvedPtr(entry, 4);
@@ -753,7 +753,7 @@ int16 BoltEngine::helpFred() {
 	// Save current palette for each entry, find playable entry...
 	off = 0;
 	while (true) {
-		entry = getResolvedPtr(g_fredHelpEntries, off);
+		entry = getResolvedPtr(_fredHelpEntries, off);
 		if (!entry)
 			break;
 
@@ -773,15 +773,15 @@ int16 BoltEngine::helpFred() {
 	_xp->showCursor();
 
 	// Highlight first entry...
-	g_fredHoveredEntry = getResolvedPtr(g_fredHelpEntries, 0);
-	hiliteFredHelpObject(getResolvedPtr(g_fredHelpEntries, 0), 1);
+	_fredHoveredEntry = getResolvedPtr(_fredHelpEntries, 0);
+	hiliteFredHelpObject(getResolvedPtr(_fredHelpEntries, 0), 1);
 
 	// Main help loop
 	while (exitResult == -1) {
 		// Handle audio playback...
 		if (isPlaying) {
 			if (!maintainAudioPlay(soundPending)) {
-				int16 highlight = (playableEntry == g_fredHoveredEntry) ? 1 : 0;
+				int16 highlight = (playableEntry == _fredHoveredEntry) ? 1 : 0;
 				hiliteFredHelpObject(playableEntry, highlight);
 				isPlaying = 0;
 			}
@@ -795,15 +795,15 @@ int16 BoltEngine::helpFred() {
 		switch (eventType) {
 		case etTimer: {
 			// Timer expired, toggle selected entry highlight...
-			if (!g_fredCurrentHelpObject)
+			if (!_fredCurrentHelpObject)
 				break;
 
-			if (g_fredTimer != eventData)
+			if (_fredTimer != eventData)
 				break;
 
-			g_fredTimer = _xp->startTimer(500);
+			_fredTimer = _xp->startTimer(500);
 
-			byte *sel = g_fredCurrentHelpObject;
+			byte *sel = _fredCurrentHelpObject;
 			int16 highlighted = (READ_UINT32(sel + 8) & 1) ? 0 : 1;
 			hiliteFredHelpObject(sel, highlighted);
 			break;
@@ -816,7 +816,7 @@ int16 BoltEngine::helpFred() {
 			off = 0;
 			byte *hitEntry = nullptr;
 			while (true) {
-				entry = getResolvedPtr(g_fredHelpEntries, off);
+				entry = getResolvedPtr(_fredHelpEntries, off);
 				if (!entry)
 					break;
 
@@ -835,20 +835,20 @@ int16 BoltEngine::helpFred() {
 			}
 
 			// Update hover state...
-			if (hitEntry == g_fredHoveredEntry)
+			if (hitEntry == _fredHoveredEntry)
 				break;
 
 			// Unhighlight previous...
-			if (g_fredHoveredEntry &&
-				g_fredHoveredEntry != g_fredCurrentHelpObject &&
-				!(g_fredHoveredEntry == playableEntry && isPlaying)) {
-				hiliteFredHelpObject(g_fredHoveredEntry, 0);
+			if (_fredHoveredEntry &&
+				_fredHoveredEntry != _fredCurrentHelpObject &&
+				!(_fredHoveredEntry == playableEntry && isPlaying)) {
+				hiliteFredHelpObject(_fredHoveredEntry, 0);
 			}
 
-			g_fredHoveredEntry = hitEntry;
+			_fredHoveredEntry = hitEntry;
 
 			// Highlight new...
-			if (hitEntry && hitEntry != g_fredCurrentHelpObject) {
+			if (hitEntry && hitEntry != _fredCurrentHelpObject) {
 				hiliteFredHelpObject(hitEntry, 1);
 			}
 
@@ -859,41 +859,41 @@ int16 BoltEngine::helpFred() {
 
 			// Stop current animation if playing...
 			if (isPlaying) {
-				if (g_fredTimer) {
-					_xp->killTimer(g_fredTimer);
-					g_fredTimer = 0;
+				if (_fredTimer) {
+					_xp->killTimer(_fredTimer);
+					_fredTimer = 0;
 				}
 
-				int16 hl = (playableEntry == g_fredHoveredEntry) ? 1 : 0;
+				int16 hl = (playableEntry == _fredHoveredEntry) ? 1 : 0;
 				hiliteFredHelpObject(playableEntry, hl);
 
-				hl = (g_fredCurrentHelpObject == g_fredHoveredEntry) ? 1 : 0;
-				hiliteFredHelpObject(g_fredCurrentHelpObject, hl);
+				hl = (_fredCurrentHelpObject == _fredHoveredEntry) ? 1 : 0;
+				hiliteFredHelpObject(_fredCurrentHelpObject, hl);
 
-				g_fredCurrentHelpObject = nullptr;
+				_fredCurrentHelpObject = nullptr;
 				stopAnimation();
 				isPlaying = 0;
 				justStopped = 1;
 			}
 
 			// Handle click on hovered entry...
-			if (!g_fredHoveredEntry)
+			if (!_fredHoveredEntry)
 				break;
 
-			uint32 entryType = READ_UINT32(g_fredHoveredEntry);
+			uint32 entryType = READ_UINT32(_fredHoveredEntry);
 
 			if (entryType == 0) {
 				// Exit help...
 				exitResult = 0;
 			} else if (entryType == 1) {
 				// Exit help and clear show-help flag...
-				g_fredShowHelp = 0;
+				_fredShowHelp = 0;
 				exitResult = 1;
 			} else if (entryType == 2) {
 				// Play animation...
 				if (!isPlaying && !justStopped) {
-					if (startAnimation(g_rtfHandle, 0x1B)) {
-						g_fredHelpStep = 0;
+					if (startAnimation(_rtfHandle, 0x1B)) {
+						_fredHelpStep = 0;
 						isPlaying = 1;
 					}
 				}
@@ -919,10 +919,10 @@ int16 BoltEngine::helpFred() {
 	// Restore all entries to highlight state matching hover
 	off = 0;
 	while (true) {
-		entry = getResolvedPtr(g_fredHelpEntries, off);
+		entry = getResolvedPtr(_fredHelpEntries, off);
 		if (!entry)
 			break;
-		int16 hl = (entry == g_fredHoveredEntry) ? 1 : 0;
+		int16 hl = (entry == _fredHoveredEntry) ? 1 : 0;
 		hiliteFredHelpObject(entry, hl);
 		off += 4;
 	}
@@ -932,7 +932,7 @@ int16 BoltEngine::helpFred() {
 	// Unhighlight all entries
 	off = 0;
 	while (true) {
-		entry = getResolvedPtr(g_fredHelpEntries, off);
+		entry = getResolvedPtr(_fredHelpEntries, off);
 		if (!entry)
 			break;
 		hiliteFredHelpObject(entry, 0);
@@ -967,37 +967,37 @@ void BoltEngine::hiliteFredHelpObject(byte *entry, int16 highlight) {
 }
 
 void BoltEngine::helpAnimStep() {
-	if (g_fredHelpStep < 0 || g_fredHelpStep >= 4)
+	if (_fredHelpStep < 0 || _fredHelpStep >= 4)
 		return;
 
 	// Unhighlight previous selected if not hovered...
-	int16 hl = (g_fredCurrentHelpObject == g_fredHoveredEntry) ? 1 : 0;
-	hiliteFredHelpObject(g_fredCurrentHelpObject, hl);
+	int16 hl = (_fredCurrentHelpObject == _fredHoveredEntry) ? 1 : 0;
+	hiliteFredHelpObject(_fredCurrentHelpObject, hl);
 
-	// On even steps, select entry from g_fredPlayButton table; on odd steps, deselect...
-	if (!(g_fredHelpStep & 1)) {
-		int16 idx = g_fredHelpStep >> 1;
-		g_fredCurrentHelpObject = getResolvedPtr(g_fredPlayButton, idx * 4);
+	// On even steps, select entry from _fredPlayButton table; on odd steps, deselect...
+	if (!(_fredHelpStep & 1)) {
+		int16 idx = _fredHelpStep >> 1;
+		_fredCurrentHelpObject = getResolvedPtr(_fredPlayButton, idx * 4);
 	} else {
-		g_fredCurrentHelpObject = nullptr;
+		_fredCurrentHelpObject = nullptr;
 	}
 
-	g_fredTimer = 0;
+	_fredTimer = 0;
 
 	// Highlight new selected entry...
-	hiliteFredHelpObject(g_fredCurrentHelpObject, 1);
+	hiliteFredHelpObject(_fredCurrentHelpObject, 1);
 
 	// Start/stop blink timer...
-	if (!(g_fredHelpStep & 1)) {
-		g_fredTimer = _xp->startTimer(500);
+	if (!(_fredHelpStep & 1)) {
+		_fredTimer = _xp->startTimer(500);
 	} else {
-		if (g_fredTimer) {
-			_xp->killTimer(g_fredTimer);
-			g_fredTimer = 0;
+		if (_fredTimer) {
+			_xp->killTimer(_fredTimer);
+			_fredTimer = 0;
 		}
 	}
 
-	g_fredHelpStep++;
+	_fredHelpStep++;
 }
 
 bool BoltEngine::spawnBalloon() {
@@ -1007,7 +1007,7 @@ bool BoltEngine::spawnBalloon() {
 
 	FredEntityState *entry;
 	while (true) {
-		entry = g_fredEntitiesTable[i];
+		entry = _fredEntitiesTable[i];
 		if (!entry)
 			break;
 
@@ -1022,12 +1022,12 @@ bool BoltEngine::spawnBalloon() {
 		return false;
 
 	// Find next free balloon slot starting from the search index...
-	int16 searchStart = g_fredBalloonSearchIdx;
+	int16 searchStart = _fredBalloonSearchIdx;
 	int16 wrapped = 0;
 	i = searchStart;
 
 	while (true) {
-		entry = g_fredEntitiesTable[i];
+		entry = _fredEntitiesTable[i];
 		if (!entry) {
 			wrapped = 1;
 			i = -1;
@@ -1046,22 +1046,22 @@ bool BoltEngine::spawnBalloon() {
 	if (i == searchStart && wrapped)
 		return false;
 
-	g_fredBalloonSearchIdx = i + 1;
+	_fredBalloonSearchIdx = i + 1;
 
 	// Pick row from shuffle table...
 	int16 row = selectBalloonRow();
 
-	uint16 numCols = READ_UINT16(g_fredLevelPtr + 6);
+	uint16 numCols = READ_UINT16(_fredLevelPtr + 6);
 	int16 stride = numCols + 1;
 	int16 rowBase = stride * row;
 
 	// Get next column from shuffle table...
-	byte *shuffleRow = g_fredShuffleTable + rowBase;
+	byte *shuffleRow = _fredShuffleTable + rowBase;
 	byte colCounter = shuffleRow[0];
-	byte col = g_fredShuffleTable[rowBase + colCounter + 1];
+	byte col = _fredShuffleTable[rowBase + colCounter + 1];
 
-	// Look up path table: g_fredPathMatrix[row][col]
-	byte *rowPaths = getResolvedPtr(g_fredPathMatrix, row * 4);
+	// Look up path table: _fredPathMatrix[row][col]
+	byte *rowPaths = getResolvedPtr(_fredPathMatrix, row * 4);
 	byte *pathTable = getResolvedPtr(rowPaths, col * 4);
 
 	// Assign path to balloon...
@@ -1088,25 +1088,25 @@ bool BoltEngine::spawnBalloon() {
 }
 
 int16 BoltEngine::calcBalloonSpawnDelay() {
-	int16 range = READ_UINT16(g_fredLevelPtr + 2);
-	int16 base = READ_UINT16(g_fredLevelPtr + 0);
+	int16 range = READ_UINT16(_fredLevelPtr + 2);
+	int16 base = READ_UINT16(_fredLevelPtr + 0);
 	return _xp->getRandom(range) + base;
 }
 
 int16 BoltEngine::selectBalloonRow() {
 	// Get Fred's center X position...
-	int32 fredX = g_fredSprite.xPos >> 8;
+	int32 fredX = _fredSprite.xPos >> 8;
 
 	// Get Fred's sprite width/2 to find center...
-	int16 frameIdx = g_fredSprite.frameIndex;
-	byte *spriteDesc = getResolvedPtr(g_fredSprite.animTable, frameIdx * 6);
+	int16 frameIdx = _fredSprite.frameIndex;
+	byte *spriteDesc = getResolvedPtr(_fredSprite.animTable, frameIdx * 6);
 	int16 halfWidth = READ_UINT16(spriteDesc + 0x0A) >> 1;
 	int16 fredCenter = (int16)fredX + halfWidth;
 
 	// Find which row Fred is in...
 	int16 row = 0;
-	byte *rowBounds = g_fredRowBounds; // Array of int16 x-values
-	uint16 numRows = READ_UINT16(g_fredLevelPtr + 4);
+	byte *rowBounds = _fredRowBounds; // Array of int16 x-values
+	uint16 numRows = READ_UINT16(_fredLevelPtr + 4);
 
 	while (row < numRows) {
 		if (READ_UINT16(rowBounds + row * 2) > fredCenter)
@@ -1115,7 +1115,7 @@ int16 BoltEngine::selectBalloonRow() {
 	}
 
 	// Pick random row, rejecting if too close to Fred's row...
-	int16 rowBias = READ_UINT16(g_fredLevelPtr + 8);
+	int16 rowBias = READ_UINT16(_fredLevelPtr + 8);
 	int16 randomRow;
 	do {
 		randomRow = _xp->getRandom(numRows);
@@ -1125,11 +1125,11 @@ int16 BoltEngine::selectBalloonRow() {
 }
 
 void BoltEngine::setFredAnimMode(FredEntityState *state, int16 mode) {
-	if (mode >= 0x0B && mode < 0x0B + (g_fredLevelPtr ? READ_UINT16(g_fredLevelPtr + 0x0C) : 0)) {
+	if (mode >= 0x0B && mode < 0x0B + (_fredLevelPtr ? READ_UINT16(_fredLevelPtr + 0x0C) : 0)) {
 		state->animMode = 0x0B;
 
 		int16 typeIdx = mode - 0x0B;
-		state->animTable = getResolvedPtr(g_fredBalloonSprite, typeIdx * 4);
+		state->animTable = getResolvedPtr(_fredBalloonSprite, typeIdx * 4);
 		state->frameIndex = 0;
 		state->frameCountdown = READ_UINT16(state->animTable + 4);
 		return;
@@ -1144,7 +1144,7 @@ void BoltEngine::setFredAnimMode(FredEntityState *state, int16 mode) {
 
 	// Standard Fred animation...
 	state->animMode = mode;
-	state->animTable = g_fredSprites[mode];
+	state->animTable = _fredSprites[mode];
 
 	state->frameIndex = 0;
 	state->frameCountdown = READ_UINT16(state->animTable + 4);
@@ -1152,22 +1152,22 @@ void BoltEngine::setFredAnimMode(FredEntityState *state, int16 mode) {
 	// Play sound based on mode...
 	switch (mode) {
 	case 3: // turn right
-		playFredSound(&g_fredSounds[2], nullptr);
+		playFredSound(&_fredSounds[2], nullptr);
 		break;
 	case 4: // run right
-		playFredSound(&g_fredSounds[0], &g_fredSounds[0]);
+		playFredSound(&_fredSounds[0], &_fredSounds[0]);
 		break;
 	case 5: // catch right
-		playFredSound(&g_fredSounds[3], nullptr);
+		playFredSound(&_fredSounds[3], nullptr);
 		break;
 	case 7: // turn left
-		playFredSound(&g_fredSounds[2], nullptr);
+		playFredSound(&_fredSounds[2], nullptr);
 		break;
 	case 8: // run left
-		playFredSound(&g_fredSounds[1], &g_fredSounds[1]);
+		playFredSound(&_fredSounds[1], &_fredSounds[1]);
 		break;
 	case 9: // catch left
-		playFredSound(&g_fredSounds[3], nullptr);
+		playFredSound(&_fredSounds[3], nullptr);
 		break;
 	default: // 0, 1, 2, 6
 		playFredSound(nullptr, nullptr);
@@ -1181,7 +1181,7 @@ void BoltEngine::renderFredScene() {
 	int16 idx = 0;
 
 	while (true) {
-		FredEntityState *entry = g_fredEntitiesTable[idx];
+		FredEntityState *entry = _fredEntitiesTable[idx];
 		if (!entry)
 			break;
 
@@ -1196,7 +1196,7 @@ void BoltEngine::renderFredScene() {
 
 			// If balloon (mode 0x0B), draw string below...
 			if (entry->animMode == 0x0B) {
-				displayPic(g_fredBalloonString, x + 10, y + 25, stFront);
+				displayPic(_fredBalloonString, x + 10, y + 25, stFront);
 			}
 		}
 
@@ -1210,23 +1210,23 @@ void BoltEngine::getFredSoundInfo(BOLTLib *lib, int16 memberId, SoundInfo *sound
 }
 
 void BoltEngine::playFredSound(SoundInfo *oneShot, SoundInfo *loop) {
-	g_fredPendingOneShot = oneShot;
-	g_fredPendingLoop = loop; 
+	_fredPendingOneShot = oneShot;
+	_fredPendingLoop = loop; 
 
-	if (g_fredLoopSound != nullptr || loop == nullptr) {
-		// Check if current loop is g_fredSounds[2] and new one-shot is g_fredSounds[1]
+	if (_fredLoopSound != nullptr || loop == nullptr) {
+		// Check if current loop is _fredSounds[2] and new one-shot is _fredSounds[1]
 		// (sound transition check)
-		if (g_fredCurrentSound == &g_fredSounds[3] && g_fredPendingOneShot == &g_fredSounds[2]) {
+		if (_fredCurrentSound == &_fredSounds[3] && _fredPendingOneShot == &_fredSounds[2]) {
 			// Allow transition without stopping...
 		} else {
-			g_fredLoopSound = nullptr;
-			g_fredCurrentSound = nullptr;
+			_fredLoopSound = nullptr;
+			_fredCurrentSound = nullptr;
 			_xp->stopSound();
 		}
 	}
 
 	// If there's a pending sound and nothing currently playing, trigger it!
-	if (g_fredPendingOneShot != nullptr && g_fredCurrentSound == nullptr) {
+	if (_fredPendingOneShot != nullptr && _fredCurrentSound == nullptr) {
 		updateFredSound();
 	}
 }
@@ -1234,29 +1234,29 @@ void BoltEngine::playFredSound(SoundInfo *oneShot, SoundInfo *loop) {
 void BoltEngine::updateFredSound() {
 	bool startLoop = false;
 
-	if (g_fredLoopSound != nullptr) {
+	if (_fredLoopSound != nullptr) {
 		// Loop sound active, continue with it...
-		g_fredCurrentSound = g_fredLoopSound;
-	} else if (g_fredPendingOneShot != nullptr) {
+		_fredCurrentSound = _fredLoopSound;
+	} else if (_fredPendingOneShot != nullptr) {
 		// Start pending one-shot...
-		g_fredCurrentSound = g_fredPendingOneShot;
-		g_fredLoopSound = g_fredPendingLoop;
-		startLoop = (g_fredPendingLoop != nullptr);
-		g_fredPendingLoop = 0;
-		g_fredPendingOneShot = 0;
+		_fredCurrentSound = _fredPendingOneShot;
+		_fredLoopSound = _fredPendingLoop;
+		startLoop = (_fredPendingLoop != nullptr);
+		_fredPendingLoop = 0;
+		_fredPendingOneShot = 0;
 	} else {
 		// Nothing to play...
-		g_fredCurrentSound = nullptr;
+		_fredCurrentSound = nullptr;
 	}
 
-	if (g_fredCurrentSound != nullptr) {
+	if (_fredCurrentSound != nullptr) {
 		// Play the current sound...
-		SoundInfo *snd = g_fredCurrentSound;
+		SoundInfo *snd = _fredCurrentSound;
 		_xp->playSound(snd->data, snd->size, 22050);
 
 		if (startLoop) {
 			// Queue loop sound twice...
-			SoundInfo *loopSnd = g_fredLoopSound;
+			SoundInfo *loopSnd = _fredLoopSound;
 			_xp->playSound(loopSnd->data, loopSnd->size, 22050);
 			_xp->playSound(loopSnd->data, loopSnd->size, 22050);
 		}
diff --git a/engines/bolt/booths/george.cpp b/engines/bolt/booths/george.cpp
index 9fb7c82aec6..12f816bcd8f 100644
--- a/engines/bolt/booths/george.cpp
+++ b/engines/bolt/booths/george.cpp
@@ -39,50 +39,50 @@ int16 BoltEngine::georgeGame(int16 prevBooth) {
 }
 
 bool BoltEngine::initGeorge() {
-	if (!openBOLTLib(&g_georgeBoltLib, &g_georgeBoltCallbacks, assetPath("george.blt")))
+	if (!openBOLTLib(&_georgeBoltLib, &_georgeBoltCallbacks, assetPath("george.blt")))
 		return false;
 
-	if (!getBOLTGroup(g_georgeBoltLib, 0, 1))
+	if (!getBOLTGroup(_georgeBoltLib, 0, 1))
 		return false;
 
-	g_georgeBgPic = memberAddr(g_georgeBoltLib, (g_displayMode != 0) ? 1 : 0);
-	g_georgeHelpObjects = memberAddr(g_georgeBoltLib, 0x1F);
-	g_georgeHelpSequence = memberAddr(g_georgeBoltLib, 0x20);
+	_georgeBgPic = memberAddr(_georgeBoltLib, (_displayMode != 0) ? 1 : 0);
+	_georgeHelpObjects = memberAddr(_georgeBoltLib, 0x1F);
+	_georgeHelpSequence = memberAddr(_georgeBoltLib, 0x20);
 
-	g_georgeCarPics[0] = memberAddr(g_georgeBoltLib, 0x10);
-	g_georgeCarPics[1] = memberAddr(g_georgeBoltLib, 0x11);
-	g_georgeCarPics[2] = memberAddr(g_georgeBoltLib, 0x12);
-	g_georgeCollisionRect = memberAddr(g_georgeBoltLib, 0x13);
+	_georgeCarPics[0] = memberAddr(_georgeBoltLib, 0x10);
+	_georgeCarPics[1] = memberAddr(_georgeBoltLib, 0x11);
+	_georgeCarPics[2] = memberAddr(_georgeBoltLib, 0x12);
+	_georgeCollisionRect = memberAddr(_georgeBoltLib, 0x13);
 
-	getGeorgeSoundInfo(g_georgeBoltLib, 0x16, &g_georgeSoundCarTumble, 6);
-	getGeorgeSoundInfo(g_georgeBoltLib, 0x15, &g_georgeSoundCarLoopHi, 0);
-	getGeorgeSoundInfo(g_georgeBoltLib, 0x14, &g_georgeSoundCarLoopLo, 0);
-	getGeorgeSoundInfo(g_georgeBoltLib, 0x17, &g_georgeSoundCarStartUp, 3);
-	getGeorgeSoundInfo(g_georgeBoltLib, 0x18, &g_georgeSoundCarGoesAway, 3);
+	getGeorgeSoundInfo(_georgeBoltLib, 0x16, &_georgeSoundCarTumble, 6);
+	getGeorgeSoundInfo(_georgeBoltLib, 0x15, &_georgeSoundCarLoopHi, 0);
+	getGeorgeSoundInfo(_georgeBoltLib, 0x14, &_georgeSoundCarLoopLo, 0);
+	getGeorgeSoundInfo(_georgeBoltLib, 0x17, &_georgeSoundCarStartUp, 3);
+	getGeorgeSoundInfo(_georgeBoltLib, 0x18, &_georgeSoundCarGoesAway, 3);
 
-	g_georgeSoundCurrent = nullptr;
-	g_georgeSoundToPlay = nullptr;
-	g_georgeSoundNext = nullptr;
-	g_georgeSoundQueued = nullptr;
-	g_georgeSoundChannelCounter = 0;
+	_georgeSoundCurrent = nullptr;
+	_georgeSoundToPlay = nullptr;
+	_georgeSoundNext = nullptr;
+	_georgeSoundQueued = nullptr;
+	_georgeSoundChannelCounter = 0;
 
 	// Load save data...
-	if (!vLoad(&g_georgeSaveData, g_georgeSaveFileName)) {
-		g_georgeSaveData[0] = 0;
-		g_georgeSaveData[1] = 0;
-		g_georgeSaveData[2] = 0;
+	if (!vLoad(&_georgeSaveData, "GeorgeBE")) {
+		_georgeSaveData[0] = 0;
+		_georgeSaveData[1] = 0;
+		_georgeSaveData[2] = 0;
 	}
 
-	if (!initGeorgeLevel(g_georgeSaveData[1], g_georgeSaveData[2])) {
-		freeBOLTGroup(g_georgeBoltLib, 0, 1);
+	if (!initGeorgeLevel(_georgeSaveData[1], _georgeSaveData[2])) {
+		freeBOLTGroup(_georgeBoltLib, 0, 1);
 		return false;
 	}
 
 	// Initialize satellites from list...
-	g_georgeCollectedSatellitesNum = 0;
-	while (g_georgeCollectedSatellitesNum < g_georgeSaveData[0]) {
-		setSatelliteAnimMode(g_georgeEntityList[g_georgeNumSatellites + g_georgeCollectedSatellitesNum], 4, 0);
-		g_georgeCollectedSatellitesNum++;
+	_georgeCollectedSatellitesNum = 0;
+	while (_georgeCollectedSatellitesNum < _georgeSaveData[0]) {
+		setSatelliteAnimMode(_georgeEntityList[_georgeNumSatellites + _georgeCollectedSatellitesNum], 4, 0);
+		_georgeCollectedSatellitesNum++;
 	}
 
 	// Drain timer events...
@@ -93,23 +93,23 @@ bool BoltEngine::initGeorge() {
 	_xp->setTransparency(false);
 
 	// Display palette and background to front...
-	displayColors(g_georgePalette, stBack, 0);
-	displayPic(g_georgeBgPic, g_displayX, g_displayY, stFront);
+	displayColors(_georgePalette, stBack, 0);
+	displayPic(_georgeBgPic, _displayX, _displayY, stFront);
 	_xp->updateDisplay();
 
 	_xp->setTransparency(true);
 
 	// Display palette to both surfaces...
-	displayColors(g_georgePalette, stFront, 0);
-	displayColors(g_georgePalette, stBack, 1);
+	displayColors(_georgePalette, stFront, 0);
+	displayColors(_georgePalette, stBack, 1);
 
 	// Display background to back surface...
-	displayPic(g_georgeBgPic, g_displayX, g_displayY, stBack);
+	displayPic(_georgeBgPic, _displayX, _displayY, stBack);
 
 	drawFlyingObjects();
 
 	_xp->updateDisplay();
-	_xp->startCycle(g_georgePalCycleSpecs);
+	_xp->startCycle(_georgePalCycleSpecs);
 	_xp->setFrameRate(12);
 
 	return true;
@@ -117,35 +117,35 @@ bool BoltEngine::initGeorge() {
 
 void BoltEngine::cleanUpGeorge() {
 	// Save current progress...
-	int16 savedLevel = g_georgeSaveData[1];
-	int16 savedVariant = g_georgeSaveData[2];
+	int16 savedLevel = _georgeSaveData[1];
+	int16 savedVariant = _georgeSaveData[2];
 
-	g_georgeSaveData[0] = g_georgeCollectedSatellitesNum;
+	_georgeSaveData[0] = _georgeCollectedSatellitesNum;
 
 	// Check if collection threshold for current level was met...
-	if (g_georgeCollectedSatellitesNum >= g_georgeThresholds[0]) {
+	if (_georgeCollectedSatellitesNum >= _georgeThresholds[0]) {
 		// Advance level...
-		g_georgeSaveData[1]++;
-		if (g_georgeSaveData[1] >= 10)
-			g_georgeSaveData[1] = 9;
+		_georgeSaveData[1]++;
+		if (_georgeSaveData[1] >= 10)
+			_georgeSaveData[1] = 9;
 
 		// Advance variant...
-		g_georgeSaveData[2]++;
-		if (g_georgeSaveData[2] >= 10)
-			g_georgeSaveData[2] = 0;
+		_georgeSaveData[2]++;
+		if (_georgeSaveData[2] >= 10)
+			_georgeSaveData[2] = 0;
 
 		// Reset collected count for next level...
-		g_georgeSaveData[0] = 0;
+		_georgeSaveData[0] = 0;
 	}
 
-	vSave(g_georgeSaveData, sizeof(g_georgeSaveData), g_georgeSaveFileName);
+	vSave(_georgeSaveData, sizeof(_georgeSaveData), "GeorgeBE");
 
 	_xp->stopCycle();
 
 	termGeorgeLevel(savedLevel, savedVariant);
 
-	freeBOLTGroup(g_georgeBoltLib, 0, 1);
-	closeBOLTLib(&g_georgeBoltLib);
+	freeBOLTGroup(_georgeBoltLib, 0, 1);
+	closeBOLTLib(&_georgeBoltLib);
 
 	_xp->setFrameRate(0);
 	_xp->fillDisplay(0, stFront);
@@ -156,33 +156,33 @@ bool BoltEngine::initGeorgeLevel(int16 level, int16 variant) {
 	int16 levelGroup = (level * 2) << 8 | 0x100;
 	int16 variantGroup = (variant * 2) << 8 | 0x200;
 
-	if (!getBOLTGroup(g_georgeBoltLib, levelGroup, 1))
+	if (!getBOLTGroup(_georgeBoltLib, levelGroup, 1))
 		return false;
 
-	if (!getBOLTGroup(g_georgeBoltLib, variantGroup, 1)) {
-		freeBOLTGroup(g_georgeBoltLib, levelGroup, 1);
+	if (!getBOLTGroup(_georgeBoltLib, variantGroup, 1)) {
+		freeBOLTGroup(_georgeBoltLib, levelGroup, 1);
 		return false;
 	}
 
-	g_georgeThresholds = (int16 *)memberAddr(g_georgeBoltLib, levelGroup);
-	g_georgePalette = memberAddr(g_georgeBoltLib, variantGroup);
-	g_georgePalCycleRawData = memberAddr(g_georgeBoltLib, variantGroup + 1);
-	boltCycleToXPCycle(g_georgePalCycleRawData, g_georgePalCycleSpecs);
+	_georgeThresholds = (int16 *)memberAddr(_georgeBoltLib, levelGroup);
+	_georgePalette = memberAddr(_georgeBoltLib, variantGroup);
+	_georgePalCycleRawData = memberAddr(_georgeBoltLib, variantGroup + 1);
+	boltCycleToXPCycle(_georgePalCycleRawData, _georgePalCycleSpecs);
 
-	g_georgeSatelliteGfx = memberAddr(g_georgeBoltLib, variantGroup + 0x12);
-	g_georgeAsteroidGfx = memberAddr(g_georgeBoltLib, variantGroup + 0x26);
-	g_georgeSatelliteThresholds = (int16 *)memberAddr(g_georgeBoltLib, levelGroup + 0x01);
-	g_georgeAsteroidThresholds = (int16 *)memberAddr(g_georgeBoltLib, levelGroup + 0x02);
-	g_georgeSatelliteCollisionRects = memberAddr(g_georgeBoltLib, variantGroup + 0x19);
-	g_georgeAsteroidCollisionRects = memberAddr(g_georgeBoltLib, variantGroup + 0x2A);
-	g_georgeSatellitePaths = memberAddr(g_georgeBoltLib, levelGroup + 0x27);
-	g_georgeAsteroidPaths = memberAddr(g_georgeBoltLib, levelGroup + 0x3C);
+	_georgeSatelliteGfx = memberAddr(_georgeBoltLib, variantGroup + 0x12);
+	_georgeAsteroidGfx = memberAddr(_georgeBoltLib, variantGroup + 0x26);
+	_georgeSatelliteThresholds = (int16 *)memberAddr(_georgeBoltLib, levelGroup + 0x01);
+	_georgeAsteroidThresholds = (int16 *)memberAddr(_georgeBoltLib, levelGroup + 0x02);
+	_georgeSatelliteCollisionRects = memberAddr(_georgeBoltLib, variantGroup + 0x19);
+	_georgeAsteroidCollisionRects = memberAddr(_georgeBoltLib, variantGroup + 0x2A);
+	_georgeSatellitePaths = memberAddr(_georgeBoltLib, levelGroup + 0x27);
+	_georgeAsteroidPaths = memberAddr(_georgeBoltLib, levelGroup + 0x3C);
 
 	// Allocate and build asteroid shuffle table...
-	int16 numAsteroidRows = g_georgeThresholds[3];
-	int16 numAsteroidCols = g_georgeThresholds[4];
-	g_georgeSatelliteShuffleTable = (byte *)_xp->allocMem((uint32)(numAsteroidCols + 1) * (numAsteroidRows));
-	if (!g_georgeSatelliteShuffleTable) {
+	int16 numAsteroidRows = _georgeThresholds[3];
+	int16 numAsteroidCols = _georgeThresholds[4];
+	_georgeSatelliteShuffleTable = (byte *)_xp->allocMem((uint32)(numAsteroidCols + 1) * (numAsteroidRows));
+	if (!_georgeSatelliteShuffleTable) {
 		termGeorgeLevel(level, variant);
 		return false;
 	}
@@ -190,76 +190,76 @@ bool BoltEngine::initGeorgeLevel(int16 level, int16 variant) {
 	// Fill each row with sequential values 0..numAsteroidCols, then shuffle...
 	for (int16 row = 0; row < numAsteroidRows; row++) {
 		int16 rowBase = (numAsteroidCols + 1) * row;
-		g_georgeSatelliteShuffleTable[rowBase] = 0;
+		_georgeSatelliteShuffleTable[rowBase] = 0;
 
 		for (int16 col = 0; col < numAsteroidCols; col++)
-			g_georgeSatelliteShuffleTable[rowBase + col + 1] = (byte)col;
+			_georgeSatelliteShuffleTable[rowBase + col + 1] = (byte)col;
 
 		// Fisher-Yates shuffle...
 		for (int16 i = 0; i < numAsteroidCols; i++) {
 			int16 j = _xp->getRandom(numAsteroidCols);
-			byte tmp = g_georgeSatelliteShuffleTable[rowBase + i + 1];
-			g_georgeSatelliteShuffleTable[rowBase + i + 1] = g_georgeSatelliteShuffleTable[rowBase + j + 1];
-			g_georgeSatelliteShuffleTable[rowBase + j + 1] = tmp;
+			byte tmp = _georgeSatelliteShuffleTable[rowBase + i + 1];
+			_georgeSatelliteShuffleTable[rowBase + i + 1] = _georgeSatelliteShuffleTable[rowBase + j + 1];
+			_georgeSatelliteShuffleTable[rowBase + j + 1] = tmp;
 		}
 	}
 
 	// Allocate and build satellite shuffle table...
-	int16 numSatelliteRows = g_georgeThresholds[9];
-	int16 numSatelliteCols = g_georgeThresholds[10];
-	g_georgeAsteroidShuffleTable = (byte *)_xp->allocMem((uint32)(numSatelliteCols + 1) * (numSatelliteRows));
-	if (!g_georgeAsteroidShuffleTable) {
+	int16 numSatelliteRows = _georgeThresholds[9];
+	int16 numSatelliteCols = _georgeThresholds[10];
+	_georgeAsteroidShuffleTable = (byte *)_xp->allocMem((uint32)(numSatelliteCols + 1) * (numSatelliteRows));
+	if (!_georgeAsteroidShuffleTable) {
 		termGeorgeLevel(level, variant);
 		return false;
 	}
 
 	for (int16 row = 0; row < numSatelliteRows; row++) {
 		int16 rowBase = (numSatelliteCols + 1) * row;
-		g_georgeAsteroidShuffleTable[rowBase] = 0;
+		_georgeAsteroidShuffleTable[rowBase] = 0;
 
 		for (int16 col = 0; col < numSatelliteCols; col++)
-			g_georgeAsteroidShuffleTable[rowBase + col + 1] = (byte)col;
+			_georgeAsteroidShuffleTable[rowBase + col + 1] = (byte)col;
 
 		for (int16 i = 0; i < numSatelliteCols; i++) {
 			int16 j = _xp->getRandom(numSatelliteCols);
-			byte tmp = g_georgeAsteroidShuffleTable[rowBase + i + 1];
-			g_georgeAsteroidShuffleTable[rowBase + i + 1] = g_georgeAsteroidShuffleTable[rowBase + j + 1];
-			g_georgeAsteroidShuffleTable[rowBase + j + 1] = tmp;
+			byte tmp = _georgeAsteroidShuffleTable[rowBase + i + 1];
+			_georgeAsteroidShuffleTable[rowBase + i + 1] = _georgeAsteroidShuffleTable[rowBase + j + 1];
+			_georgeAsteroidShuffleTable[rowBase + j + 1] = tmp;
 		}
 	}
 
 	// Compute satellite list indices...
-	g_georgeNumSatellites = 0;
-	int16 numSatellites = g_georgeThresholds[0];
-	g_georgeFirstAsteroidIdx = numSatellites;
+	_georgeNumSatellites = 0;
+	int16 numSatellites = _georgeThresholds[0];
+	_georgeFirstAsteroidIdx = numSatellites;
 
-	int16 numAsteroids = g_georgeThresholds[11];
+	int16 numAsteroids = _georgeThresholds[11];
 	if (numAsteroids < 1)
 		numAsteroids = 1;
 
-	g_georgeCarIdx = g_georgeFirstAsteroidIdx + numAsteroids;
-	g_georgeTotalSatellites = g_georgeCarIdx + 1;
+	_georgeCarIdx = _georgeFirstAsteroidIdx + numAsteroids;
+	_georgeTotalSatellites = _georgeCarIdx + 1;
 
 	// Allocate satellite list...
-	g_georgeEntityList = (GeorgeEntityState **)_xp->allocMem((g_georgeTotalSatellites + 1) * sizeof(GeorgeEntityState *));
-	if (!g_georgeEntityList) {
+	_georgeEntityList = (GeorgeEntityState **)_xp->allocMem((_georgeTotalSatellites + 1) * sizeof(GeorgeEntityState *));
+	if (!_georgeEntityList) {
 		termGeorgeLevel(level, variant);
 		return false;
 	}
 
-	for (int16 i = 0; i < g_georgeTotalSatellites; i++) {
-		g_georgeEntityList[i] = new GeorgeEntityState();
-		if (!g_georgeEntityList[i]) {
+	for (int16 i = 0; i < _georgeTotalSatellites; i++) {
+		_georgeEntityList[i] = new GeorgeEntityState();
+		if (!_georgeEntityList[i]) {
 			termGeorgeLevel(level, variant);
 			return false;
 		}
 
-		g_georgeEntityList[i]->flags = 0;
+		_georgeEntityList[i]->flags = 0;
 	}
 
-	g_georgeEntityList[g_georgeTotalSatellites] = nullptr;
+	_georgeEntityList[_georgeTotalSatellites] = nullptr;
 
-	GeorgeEntityState *carSat = g_georgeEntityList[g_georgeCarIdx];
+	GeorgeEntityState *carSat = _georgeEntityList[_georgeCarIdx];
 	assert(carSat);
 
 	carSat->flags = 1;
@@ -274,7 +274,7 @@ bool BoltEngine::initGeorgeLevel(int16 level, int16 variant) {
 	carSat->pathIndex = 0;
 	setGeorgeAnimMode(carSat, 0);
 
-	int32 numObjectTypes = g_georgeThresholds[6];
+	int32 numObjectTypes = _georgeThresholds[6];
 	byte *objShuf = (byte *)_xp->allocMem(numObjectTypes);
 	if (!objShuf) {
 		termGeorgeLevel(level, variant);
@@ -297,13 +297,13 @@ bool BoltEngine::initGeorgeLevel(int16 level, int16 variant) {
 		}
 
 		int16 animType = objShuf[i % numObjectTypes];
-		setSatelliteAnimMode(g_georgeEntityList[g_georgeNumSatellites + i], 3, animType);
+		setSatelliteAnimMode(_georgeEntityList[_georgeNumSatellites + i], 3, animType);
 	}
 
 	_xp->freeMem(objShuf);
 
 	// Allocate asteroid shuffled list...
-	int16 numAsteroidTypes = g_georgeThresholds[11];
+	int16 numAsteroidTypes = _georgeThresholds[11];
 	if (numAsteroidTypes < 1)
 		numAsteroidTypes = 1;
 
@@ -327,84 +327,84 @@ bool BoltEngine::initGeorgeLevel(int16 level, int16 variant) {
 		}
 
 		int16 animType = astShuf[i % numAsteroidTypes];
-		setAsteroidAnimMode(g_georgeEntityList[g_georgeFirstAsteroidIdx + i], 5, animType);
+		setAsteroidAnimMode(_georgeEntityList[_georgeFirstAsteroidIdx + i], 5, animType);
 	}
 
 	_xp->freeMem(astShuf);
 
 	// Allocate and load sound list for satellites...
-	g_georgeSatelliteSoundList = (SoundInfo **)_xp->allocMem(numObjectTypes * sizeof(SoundInfo *));
-	if (!g_georgeSatelliteSoundList) {
+	_georgeSatelliteSoundList = (SoundInfo **)_xp->allocMem(numObjectTypes * sizeof(SoundInfo *));
+	if (!_georgeSatelliteSoundList) {
 		termGeorgeLevel(level, variant);
 		return false;
 	}
 
 	for (int16 i = 0; i < numObjectTypes; i++) {
-		g_georgeSatelliteSoundList[i] = new SoundInfo();
-		if (!g_georgeSatelliteSoundList[i]) {
+		_georgeSatelliteSoundList[i] = new SoundInfo();
+		if (!_georgeSatelliteSoundList[i]) {
 			termGeorgeLevel(level, variant);
 			return false;
 		}
 
 		int16 member = variantGroup | ((i + 0x1A) & 0xFF);
-		getGeorgeSoundInfo(g_georgeBoltLib, member, g_georgeSatelliteSoundList[i], 6);
+		getGeorgeSoundInfo(_georgeBoltLib, member, _georgeSatelliteSoundList[i], 6);
 	}
 
-	g_georgeSatelliteWait = 1;
-	g_georgeSatelliteSearchIdx = 0;
-	g_georgeAsteroidSearchIdx = 0;
-	g_georgeAsteroidWait = getRandomAsteroidWait();
-	g_georgeHitSearchIdx = 0;
-	g_georgeCollectedSatellitesNum = 0;
+	_georgeSatelliteWait = 1;
+	_georgeSatelliteSearchIdx = 0;
+	_georgeAsteroidSearchIdx = 0;
+	_georgeAsteroidWait = getRandomAsteroidWait();
+	_georgeHitSearchIdx = 0;
+	_georgeCollectedSatellitesNum = 0;
 
 	return true;
 }
 
 void BoltEngine::termGeorgeLevel(int16 level, int16 variant) {
-	if (g_georgeEntityList) {
-		for (int16 i = 0; i < g_georgeTotalSatellites; i++) {
-			GeorgeEntityState *sat = g_georgeEntityList[i];
+	if (_georgeEntityList) {
+		for (int16 i = 0; i < _georgeTotalSatellites; i++) {
+			GeorgeEntityState *sat = _georgeEntityList[i];
 			if (sat) {
 				delete sat;
-				g_georgeEntityList[i] = nullptr;
+				_georgeEntityList[i] = nullptr;
 			}
 		}
 
-		_xp->freeMem(g_georgeEntityList);
-		g_georgeEntityList = nullptr;
+		_xp->freeMem(_georgeEntityList);
+		_georgeEntityList = nullptr;
 	}
 
-	if (g_georgeSatelliteSoundList) {
-		int32 numObjectTypes = g_georgeThresholds[6];
+	if (_georgeSatelliteSoundList) {
+		int32 numObjectTypes = _georgeThresholds[6];
 		for (int16 i = 0; i < numObjectTypes; i++) {
-			SoundInfo *snd = g_georgeSatelliteSoundList[i];
+			SoundInfo *snd = _georgeSatelliteSoundList[i];
 			if (snd) {
 				delete snd;
-				g_georgeSatelliteSoundList[i] = nullptr;
+				_georgeSatelliteSoundList[i] = nullptr;
 			}
 		}
 
-		_xp->freeMem(g_georgeSatelliteSoundList);
-		g_georgeSatelliteSoundList = nullptr;
+		_xp->freeMem(_georgeSatelliteSoundList);
+		_georgeSatelliteSoundList = nullptr;
 	}
 
-	if (g_georgeAsteroidShuffleTable) {
-		_xp->freeMem(g_georgeAsteroidShuffleTable);
-		g_georgeAsteroidShuffleTable = nullptr;
+	if (_georgeAsteroidShuffleTable) {
+		_xp->freeMem(_georgeAsteroidShuffleTable);
+		_georgeAsteroidShuffleTable = nullptr;
 	}
 
-	if (g_georgeSatelliteShuffleTable) {
-		_xp->freeMem(g_georgeSatelliteShuffleTable);
-		g_georgeSatelliteShuffleTable = nullptr;
+	if (_georgeSatelliteShuffleTable) {
+		_xp->freeMem(_georgeSatelliteShuffleTable);
+		_georgeSatelliteShuffleTable = nullptr;
 	}
 
-	freeBOLTGroup(g_georgeBoltLib, (variant * 2) << 8 | 0x200, 1);
-	freeBOLTGroup(g_georgeBoltLib, (level * 2) << 8 | 0x100, 1);
+	freeBOLTGroup(_georgeBoltLib, (variant * 2) << 8 | 0x200, 1);
+	freeBOLTGroup(_georgeBoltLib, (level * 2) << 8 | 0x100, 1);
 }
 
 void BoltEngine::swapGeorgeFrameArray() {
-	byte *data = g_boltCurrentMemberEntry->dataPtr;
-	uint32 size = g_boltCurrentMemberEntry->decompSize;
+	byte *data = _boltCurrentMemberEntry->dataPtr;
+	uint32 size = _boltCurrentMemberEntry->decompSize;
 	uint32 off = 0;
 
 	while ((int32)(size - off) > 0) {
@@ -415,8 +415,8 @@ void BoltEngine::swapGeorgeFrameArray() {
 }
 
 void BoltEngine::swapGeorgeHelpEntry() {
-	byte *data = g_boltCurrentMemberEntry->dataPtr;
-	uint32 size = g_boltCurrentMemberEntry->decompSize;
+	byte *data = _boltCurrentMemberEntry->dataPtr;
+	uint32 size = _boltCurrentMemberEntry->decompSize;
 	uint32 off = 0;
 	uint16 palCountOff = (uint16)(off + 0x10);
 
@@ -436,7 +436,7 @@ void BoltEngine::swapGeorgeHelpEntry() {
 }
 
 void BoltEngine::swapGeorgeThresholds() {
-	byte *data = g_boltCurrentMemberEntry->dataPtr;
+	byte *data = _boltCurrentMemberEntry->dataPtr;
 	for (int16 i = 0; i < 12; i++)
 		WRITE_UINT16(data + i * 2, READ_BE_UINT16(data + i * 2));
 }
@@ -449,7 +449,7 @@ int16 BoltEngine::playGeorge() {
 	int16 firstFrame = 1;
 	int32 joyY = 0;
 
-	if (g_georgeHelpActive) {
+	if (_georgeHelpActive) {
 		if (!helpGeorge())
 			return 7;
 	}
@@ -505,13 +505,13 @@ int16 BoltEngine::playGeorge() {
 		if (flyActive) {
 			if (firstFrame) {
 				firstFrame = 0;
-				playGeorgeSound(&g_georgeSoundCarStartUp, &g_georgeSoundCarLoopHi);
+				playGeorgeSound(&_georgeSoundCarStartUp, &_georgeSoundCarLoopHi);
 			}
 
 			if (joyY) {
 				flyActive = 0;
 			} else {
-				GeorgeEntityState *carSat = g_georgeEntityList[g_georgeCarIdx];
+				GeorgeEntityState *carSat = _georgeEntityList[_georgeCarIdx];
 				int16 carY = (int16)(carSat->y >> 8);
 
 				if (carY > 0xD0)
@@ -533,28 +533,28 @@ int16 BoltEngine::playGeorge() {
 			}
 		} else {
 			if (!winSeq) {
-				GeorgeEntityState *carSat = g_georgeEntityList[g_georgeCarIdx];
+				GeorgeEntityState *carSat = _georgeEntityList[_georgeCarIdx];
 				carSat->accelY = (joyY * 3) << 8;
 
-				if (--g_georgeSatelliteWait == 0) {
+				if (--_georgeSatelliteWait == 0) {
 					if (spawnSatellite())
-						g_georgeSatelliteWait = getRandomSatelliteWait();
+						_georgeSatelliteWait = getRandomSatelliteWait();
 					else
-						g_georgeSatelliteWait = 1;
+						_georgeSatelliteWait = 1;
 				}
 
-				if (--g_georgeAsteroidWait == 0) {
+				if (--_georgeAsteroidWait == 0) {
 					if (spawnAsteroid())
-						g_georgeAsteroidWait = getRandomAsteroidWait();
+						_georgeAsteroidWait = getRandomAsteroidWait();
 					else
-						g_georgeAsteroidWait = 1;
+						_georgeAsteroidWait = 1;
 				}
 			}
 		}
 
 		// ---- Pass 1: animate all satellites ----
 		for (int16 i = 0;; i++) {
-			GeorgeEntityState *sat = g_georgeEntityList[i];
+			GeorgeEntityState *sat = _georgeEntityList[i];
 			if (!sat)
 				break;
 			if (!(sat->flags & 1))
@@ -588,7 +588,7 @@ int16 BoltEngine::playGeorge() {
 
 		// ---- Pass 2: physics ----
 		for (int16 i = 0;; i++) {
-			GeorgeEntityState *sat = g_georgeEntityList[i];
+			GeorgeEntityState *sat = _georgeEntityList[i];
 			if (!sat)
 				break;
 			if (!(sat->flags & 1))
@@ -626,11 +626,11 @@ int16 BoltEngine::playGeorge() {
 
 				if (!winSeq) {
 					if (sat->velY <= 0) {
-						if (g_georgeSoundToPlay != &g_georgeSoundCarLoopHi)
-							playGeorgeSound(&g_georgeSoundCarLoopHi, &g_georgeSoundCarLoopHi);
+						if (_georgeSoundToPlay != &_georgeSoundCarLoopHi)
+							playGeorgeSound(&_georgeSoundCarLoopHi, &_georgeSoundCarLoopHi);
 					} else {
-						if (g_georgeSoundToPlay != &g_georgeSoundCarLoopLo)
-							playGeorgeSound(&g_georgeSoundCarLoopLo, &g_georgeSoundCarLoopLo);
+						if (_georgeSoundToPlay != &_georgeSoundCarLoopLo)
+							playGeorgeSound(&_georgeSoundCarLoopLo, &_georgeSoundCarLoopLo);
 					}
 				} else {
 					int32 xExit = (int32)0x1C2 << 8;
@@ -663,16 +663,16 @@ int16 BoltEngine::playGeorge() {
 
 		// ---- Pass 3: collision detection ----
 		if (!winSeq) {
-			GeorgeEntityState *carSat = g_georgeEntityList[g_georgeCarIdx];
+			GeorgeEntityState *carSat = _georgeEntityList[_georgeCarIdx];
 
 			byte *carFrameData = getResolvedPtr(carSat->animTable, carSat->frameIndex * 6);
-			int16 carX1 = (int16)(carSat->x >> 8) + READ_UINT16(g_georgeCollisionRect + 0x00) + READ_UINT16(carFrameData + 0x06);
-			int16 carY1 = (int16)(carSat->y >> 8) + READ_UINT16(g_georgeCollisionRect + 0x02) + READ_UINT16(carFrameData + 0x08);
-			int16 carW = READ_UINT16(g_georgeCollisionRect + 0x04);
-			int16 carH = READ_UINT16(g_georgeCollisionRect + 0x06);
+			int16 carX1 = (int16)(carSat->x >> 8) + READ_UINT16(_georgeCollisionRect + 0x00) + READ_UINT16(carFrameData + 0x06);
+			int16 carY1 = (int16)(carSat->y >> 8) + READ_UINT16(_georgeCollisionRect + 0x02) + READ_UINT16(carFrameData + 0x08);
+			int16 carW = READ_UINT16(_georgeCollisionRect + 0x04);
+			int16 carH = READ_UINT16(_georgeCollisionRect + 0x06);
 
 			for (int16 i = 0;; i++) {
-				GeorgeEntityState *sat = g_georgeEntityList[i];
+				GeorgeEntityState *sat = _georgeEntityList[i];
 				if (!sat)
 					break;
 
@@ -683,7 +683,7 @@ int16 BoltEngine::playGeorge() {
 					if (carSat->animMode != 0)
 						continue;
 
-					byte *collRect = getResolvedPtr(g_georgeSatelliteCollisionRects, sat->variant * 4);
+					byte *collRect = getResolvedPtr(_georgeSatelliteCollisionRects, sat->variant * 4);
 					byte *satFrameData = getResolvedPtr(sat->animTable, sat->frameIndex * 6);
 
 					int16 sx1 = (int16)(sat->x >> 8) + READ_UINT16(collRect + 0x00) + READ_UINT16(satFrameData + 0x06);
@@ -698,19 +698,19 @@ int16 BoltEngine::playGeorge() {
 
 					setGeorgeAnimMode(carSat, 1);
 					setSatelliteAnimMode(sat, 4, 0);
-					g_georgeCollectedSatellitesNum++;
+					_georgeCollectedSatellitesNum++;
 
 					if (spawnSatellite())
-						g_georgeSatelliteWait = getRandomSatelliteWait();
+						_georgeSatelliteWait = getRandomSatelliteWait();
 					else
-						g_georgeSatelliteWait = 1;
+						_georgeSatelliteWait = 1;
 
-					playGeorgeSound(g_georgeSatelliteSoundList[sat->variant], nullptr);
+					playGeorgeSound(_georgeSatelliteSoundList[sat->variant], nullptr);
 				} else if (sat->animMode == 5) {
 					if (carSat->animMode != 0 && carSat->animMode != 1)
 						continue;
 
-					byte *collRects = getResolvedPtr(g_georgeAsteroidCollisionRects, sat->variant * 4);
+					byte *collRects = getResolvedPtr(_georgeAsteroidCollisionRects, sat->variant * 4);
 					byte *satFrameData = getResolvedPtr(sat->animTable, sat->frameIndex * 6);
 
 					int16 sx1 = (int16)(sat->x >> 8) + READ_UINT16(collRects + 0x00) + READ_UINT16(satFrameData + 0x06);
@@ -726,31 +726,31 @@ int16 BoltEngine::playGeorge() {
 					setGeorgeAnimMode(carSat, 2);
 
 					if (confirmAsteroidHitTest())
-						g_georgeCollectedSatellitesNum--;
+						_georgeCollectedSatellitesNum--;
 
-					playGeorgeSound(&g_georgeSoundCarTumble, nullptr);
+					playGeorgeSound(&_georgeSoundCarTumble, nullptr);
 					break;
 				}
 			}
 		}
 
 		if (!winSeq) {
-			if (g_georgeCollectedSatellitesNum >= g_georgeThresholds[0]) {
-				if (g_georgeSoundToPlay &&
-					g_georgeSoundToPlay->priority > g_georgeSoundCarGoesAway.priority)
+			if (_georgeCollectedSatellitesNum >= _georgeThresholds[0]) {
+				if (_georgeSoundToPlay &&
+					_georgeSoundToPlay->priority > _georgeSoundCarGoesAway.priority)
 					continue;
 
-				if (g_georgeSoundQueued &&
-					g_georgeSoundQueued->priority > g_georgeSoundCarGoesAway.priority)
+				if (_georgeSoundQueued &&
+					_georgeSoundQueued->priority > _georgeSoundCarGoesAway.priority)
 					continue;
 
 				bool allDone = true;
 				for (int16 i = 0;; i++) {
-					GeorgeEntityState *sat = g_georgeEntityList[i];
+					GeorgeEntityState *sat = _georgeEntityList[i];
 					if (!sat)
 						break;
 
-					if (i == g_georgeCarIdx)
+					if (i == _georgeCarIdx)
 						continue;
 
 					if (sat->flags & 1) {
@@ -763,10 +763,10 @@ int16 BoltEngine::playGeorge() {
 					// We've won!
 					winSeq = 1;
 					returnCode = 0x10;
-					GeorgeEntityState *carSat = g_georgeEntityList[g_georgeCarIdx];
+					GeorgeEntityState *carSat = _georgeEntityList[_georgeCarIdx];
 					carSat->accelX = 0x200;
 					carSat->accelY = 0;
-					playGeorgeSound(&g_georgeSoundCarGoesAway, nullptr);
+					playGeorgeSound(&_georgeSoundCarGoesAway, nullptr);
 				}
 			}
 		}
@@ -780,7 +780,7 @@ int16 BoltEngine::playGeorge() {
 }
 
 int16 BoltEngine::helpGeorge() {
-	byte *firstObj = getResolvedPtr(g_georgeHelpObjects, 0);
+	byte *firstObj = getResolvedPtr(_georgeHelpObjects, 0);
 	byte *firstInfo = getResolvedPtr(firstObj, 0x04);
 	int16 curX = READ_UINT16(firstInfo + 0x06) + READ_UINT16(firstInfo + 0x0A) - 10;
 	int16 curY = READ_UINT16(firstInfo + 0x08) + READ_UINT16(firstInfo + 0x0C) - 10;
@@ -795,12 +795,12 @@ int16 BoltEngine::helpGeorge() {
 	_xp->setInactivityTimer(0);
 	_xp->stopSound();
 
-	g_georgeSoundCurrent = g_georgeSoundToPlay = g_georgeSoundNext = g_georgeSoundQueued = nullptr;
+	_georgeSoundCurrent = _georgeSoundToPlay = _georgeSoundNext = _georgeSoundQueued = nullptr;
 
 	drawFlyingObjects();
 
 	for (int16 i = 0;; i++) {
-		byte *obj = getResolvedPtr(g_georgeHelpObjects, i * 4);
+		byte *obj = getResolvedPtr(_georgeHelpObjects, i * 4);
 		if (!obj)
 			break;
 
@@ -811,7 +811,7 @@ int16 BoltEngine::helpGeorge() {
 	_xp->updateDisplay();
 
 	for (int16 i = 0;; i++) {
-		byte *obj = getResolvedPtr(g_georgeHelpObjects, i * 4);
+		byte *obj = getResolvedPtr(_georgeHelpObjects, i * 4);
 		if (!obj)
 			break;
 
@@ -827,15 +827,15 @@ int16 BoltEngine::helpGeorge() {
 	_xp->setCursorPos(curX, curY);
 	_xp->setCursorColor(0, 0, 0xFF);
 	_xp->showCursor();
-	g_georgeActiveHelpObject = getResolvedPtr(g_georgeHelpObjects, 0);
-	hiliteGeorgeHelpObject(g_georgeActiveHelpObject, 1);
+	_georgeActiveHelpObject = getResolvedPtr(_georgeHelpObjects, 0);
+	hiliteGeorgeHelpObject(_georgeActiveHelpObject, 1);
 
 	// ---- Event loop ----
 	while (!shouldQuit()) {
 		if (animPlaying) {
 			audioTick = maintainAudioPlay(audioTick);
 			if (!audioTick) {
-				bool sameAsHover = (hoveredObj == g_georgeActiveHelpObject);
+				bool sameAsHover = (hoveredObj == _georgeActiveHelpObject);
 				hiliteGeorgeHelpObject(hoveredObj, sameAsHover ? 1 : 0);
 				animPlaying = 0;
 			}
@@ -847,16 +847,16 @@ int16 BoltEngine::helpGeorge() {
 
 		switch (eventType) {
 		case etTimer: {
-			if (!g_georgePrevActiveHelpObject)
+			if (!_georgePrevActiveHelpObject)
 				break;
 
-			if (g_georgeHelpTimer != eventData)
+			if (_georgeHelpTimer != eventData)
 				break;
 
-			g_georgeHelpTimer = _xp->startTimer(500);
+			_georgeHelpTimer = _xp->startTimer(500);
 			
-				bool lit = !(READ_UINT32(g_georgePrevActiveHelpObject + 0x08) & 1u);
-				hiliteGeorgeHelpObject(g_georgePrevActiveHelpObject, lit ? 1 : 0);
+				bool lit = !(READ_UINT32(_georgePrevActiveHelpObject + 0x08) & 1u);
+				hiliteGeorgeHelpObject(_georgePrevActiveHelpObject, lit ? 1 : 0);
 			
 			break;
 		}
@@ -867,7 +867,7 @@ int16 BoltEngine::helpGeorge() {
 
 			byte *hit = nullptr;
 			for (int16 i = 0;; i++) {
-				byte *obj = getResolvedPtr(g_georgeHelpObjects, i * 4);
+				byte *obj = getResolvedPtr(_georgeHelpObjects, i * 4);
 				if (!obj)
 					break;
 
@@ -883,25 +883,25 @@ int16 BoltEngine::helpGeorge() {
 				}
 			}
 
-			if (hit == g_georgeActiveHelpObject)
+			if (hit == _georgeActiveHelpObject)
 				break;
 
-			if (g_georgeActiveHelpObject) {
-				if (g_georgeActiveHelpObject != g_georgePrevActiveHelpObject) {
-					bool keepLit = (g_georgeActiveHelpObject == hoveredObj && animPlaying);
+			if (_georgeActiveHelpObject) {
+				if (_georgeActiveHelpObject != _georgePrevActiveHelpObject) {
+					bool keepLit = (_georgeActiveHelpObject == hoveredObj && animPlaying);
 					if (!keepLit)
-						hiliteGeorgeHelpObject(g_georgeActiveHelpObject, 0);
+						hiliteGeorgeHelpObject(_georgeActiveHelpObject, 0);
 				}
 			}
 
-			g_georgeActiveHelpObject = hit;
+			_georgeActiveHelpObject = hit;
 			if (!hit)
 				break;
 
-			if (hit == g_georgePrevActiveHelpObject)
+			if (hit == _georgePrevActiveHelpObject)
 				break;
 
-			hiliteGeorgeHelpObject(g_georgeActiveHelpObject, 1);
+			hiliteGeorgeHelpObject(_georgeActiveHelpObject, 1);
 			break;
 		}
 
@@ -909,33 +909,33 @@ int16 BoltEngine::helpGeorge() {
 			animReady = 0;
 
 			if (animPlaying) {
-				if (g_georgeHelpTimer) {
-					_xp->killTimer(g_georgeHelpTimer);
-					g_georgeHelpTimer = 0;
+				if (_georgeHelpTimer) {
+					_xp->killTimer(_georgeHelpTimer);
+					_georgeHelpTimer = 0;
 				}
 
-				bool hovLit = (hoveredObj == g_georgeActiveHelpObject);
+				bool hovLit = (hoveredObj == _georgeActiveHelpObject);
 				hiliteGeorgeHelpObject(hoveredObj, hovLit ? 1 : 0);
-				bool actLit = (g_georgePrevActiveHelpObject == g_georgeActiveHelpObject);
-				hiliteGeorgeHelpObject(g_georgePrevActiveHelpObject, actLit ? 1 : 0);
+				bool actLit = (_georgePrevActiveHelpObject == _georgeActiveHelpObject);
+				hiliteGeorgeHelpObject(_georgePrevActiveHelpObject, actLit ? 1 : 0);
 
-				g_georgePrevActiveHelpObject = nullptr;
+				_georgePrevActiveHelpObject = nullptr;
 				stopAnimation();
 				animPlaying = 0;
 				animReady = 1;
 			}
 
-			if (!g_georgeActiveHelpObject)
+			if (!_georgeActiveHelpObject)
 				break;
 
-			switch (READ_UINT16(g_georgeActiveHelpObject)) {
+			switch (READ_UINT16(_georgeActiveHelpObject)) {
 			case 0:
 			case 1:
-				if (READ_UINT16(g_georgeActiveHelpObject) == 1) {
-					g_georgeHelpActive = 0;
+				if (READ_UINT16(_georgeActiveHelpObject) == 1) {
+					_georgeHelpActive = 0;
 				}
 
-				exitCode = READ_UINT16(g_georgeActiveHelpObject);
+				exitCode = READ_UINT16(_georgeActiveHelpObject);
 				break;
 			case 2:
 				if (animPlaying)
@@ -944,8 +944,8 @@ int16 BoltEngine::helpGeorge() {
 				if (animReady)
 					break;
 
-				if (startAnimation(g_rtfHandle, 30)) {
-					g_georgeHelpStep = 0;
+				if (startAnimation(_rtfHandle, 30)) {
+					_georgeHelpStep = 0;
 					animPlaying = 1;
 				}
 
@@ -974,18 +974,18 @@ int16 BoltEngine::helpGeorge() {
 
 			// Highlight hovered object, unhighlight the rest...
 			for (int16 i = 0;; i++) {
-				byte *obj = getResolvedPtr(g_georgeHelpObjects, i * 4);
+				byte *obj = getResolvedPtr(_georgeHelpObjects, i * 4);
 				if (!obj)
 					break;
 
-				bool isHovered = (obj == g_georgeActiveHelpObject);
+				bool isHovered = (obj == _georgeActiveHelpObject);
 				hiliteGeorgeHelpObject(obj, isHovered ? 1 : 0);
 			}
 			_xp->updateDisplay();
 
 			// Unhighlight all...
 			for (int16 i = 0;; i++) {
-				byte *obj = getResolvedPtr(g_georgeHelpObjects, i * 4);
+				byte *obj = getResolvedPtr(_georgeHelpObjects, i * 4);
 				if (!obj)
 					break;
 
@@ -1019,40 +1019,40 @@ void BoltEngine::hiliteGeorgeHelpObject(byte *entry, int16 highlight) {
 }
 
 void BoltEngine::advanceHelpAnimation() {
-	if (g_georgeHelpStep < 0 || g_georgeHelpStep >= 4)
+	if (_georgeHelpStep < 0 || _georgeHelpStep >= 4)
 		return;
 
-	bool isHovered = (g_georgePrevActiveHelpObject == g_georgeActiveHelpObject);
-	hiliteGeorgeHelpObject(g_georgePrevActiveHelpObject, isHovered ? 1 : 0);
+	bool isHovered = (_georgePrevActiveHelpObject == _georgeActiveHelpObject);
+	hiliteGeorgeHelpObject(_georgePrevActiveHelpObject, isHovered ? 1 : 0);
 
 	byte *newActive;
-	if (!(g_georgeHelpStep & 1)) {
-		newActive = getResolvedPtr(g_georgeHelpSequence, (g_georgeHelpStep >> 1) * 4);
+	if (!(_georgeHelpStep & 1)) {
+		newActive = getResolvedPtr(_georgeHelpSequence, (_georgeHelpStep >> 1) * 4);
 	} else {
 		newActive = nullptr;
 	}
 
-	g_georgePrevActiveHelpObject = newActive;
-	g_georgeHelpTimer = 0;
+	_georgePrevActiveHelpObject = newActive;
+	_georgeHelpTimer = 0;
 
-	hiliteGeorgeHelpObject(g_georgePrevActiveHelpObject, 1);
+	hiliteGeorgeHelpObject(_georgePrevActiveHelpObject, 1);
 
-	if (!(g_georgeHelpStep & 1)) {
-		g_georgeHelpTimer = _xp->startTimer(500);
+	if (!(_georgeHelpStep & 1)) {
+		_georgeHelpTimer = _xp->startTimer(500);
 	} else {
-		if (g_georgeHelpTimer) {
-			_xp->killTimer(g_georgeHelpTimer);
-			g_georgeHelpTimer = 0;
+		if (_georgeHelpTimer) {
+			_xp->killTimer(_georgeHelpTimer);
+			_georgeHelpTimer = 0;
 		}
 	}
 
-	g_georgeHelpStep++;
+	_georgeHelpStep++;
 }
 bool BoltEngine::spawnSatellite() {
 	// Count active satellites (type 3)...
 	int16 activeCount = 0;
 	for (int16 i = 0;; i++) {
-		GeorgeEntityState *sat = g_georgeEntityList[i];
+		GeorgeEntityState *sat = _georgeEntityList[i];
 		if (!sat)
 			break;
 
@@ -1065,13 +1065,13 @@ bool BoltEngine::spawnSatellite() {
 		return false;
 
 	// Find next inactive satellite slot...
-	int16 startIdx = g_georgeSatelliteSearchIdx;
+	int16 startIdx = _georgeSatelliteSearchIdx;
 	int16 idx = startIdx;
 	bool wrapped = false;
 	GeorgeEntityState *candidate = nullptr;
 
 	while (true) {
-		GeorgeEntityState *s = g_georgeEntityList[idx];
+		GeorgeEntityState *s = _georgeEntityList[idx];
 		if (!s) {
 			wrapped = true;
 			idx = -1;
@@ -1085,25 +1085,25 @@ bool BoltEngine::spawnSatellite() {
 			return false;
 	}
 
-	g_georgeSatelliteSearchIdx = idx + 1;
+	_georgeSatelliteSearchIdx = idx + 1;
 
 	// Get a shuffle-table row for this asteroid...
 	int16 row = getRandomAsteroidRow();
-	int16 numCols = g_georgeThresholds[4];
+	int16 numCols = _georgeThresholds[4];
 	int16 rowBase = (numCols + 1) * row;
-	int16 colIdx = g_georgeSatelliteShuffleTable[rowBase];
-	byte animIdx = g_georgeSatelliteShuffleTable[rowBase + colIdx + 1];
+	int16 colIdx = _georgeSatelliteShuffleTable[rowBase];
+	byte animIdx = _georgeSatelliteShuffleTable[rowBase + colIdx + 1];
 
 	// Get the motion path...
-	byte *rowPaths = getResolvedPtr(g_georgeSatellitePaths, row * 4);
+	byte *rowPaths = getResolvedPtr(_georgeSatellitePaths, row * 4);
 	byte *path = getResolvedPtr(rowPaths, animIdx * 4);
 
 	candidate->pathTable = path;
 
 	colIdx++;
-	g_georgeSatelliteShuffleTable[rowBase] = (byte)colIdx;
+	_georgeSatelliteShuffleTable[rowBase] = (byte)colIdx;
 	if (colIdx >= numCols)
-		g_georgeSatelliteShuffleTable[rowBase] = 0;
+		_georgeSatelliteShuffleTable[rowBase] = 0;
 
 	// Set initial position from pic's embedded coords...
 	candidate->pathIndex = 0;
@@ -1116,19 +1116,19 @@ bool BoltEngine::spawnSatellite() {
 }
 
 int16 BoltEngine::getRandomSatelliteWait() {
-	int16 range = g_georgeThresholds[2];
-	int16 topY = g_georgeThresholds[1];
+	int16 range = _georgeThresholds[2];
+	int16 topY = _georgeThresholds[1];
 	return _xp->getRandom(range) + topY;
 }
 
 int16 BoltEngine::getRandomAsteroidRow() {
-	GeorgeEntityState *carSat = g_georgeEntityList[g_georgeCarIdx];
+	GeorgeEntityState *carSat = _georgeEntityList[_georgeCarIdx];
 	int16 carY = carSat->y >> 8;
 
 	// Find which row the car is in by scanning frame Y thresholds...
-	int16 numRows = g_georgeThresholds[3];
+	int16 numRows = _georgeThresholds[3];
 	int16 carRow = 0;
-	int16 *thresholds = g_georgeSatelliteThresholds;
+	int16 *thresholds = _georgeSatelliteThresholds;
 
 	for (int16 i = 0; i < numRows; i++) {
 		if (thresholds[i] > carY)
@@ -1138,10 +1138,10 @@ int16 BoltEngine::getRandomAsteroidRow() {
 	}
 
 	// Pick a random row, retrying if too close to car's row...
-	int16 exclusionBand = g_georgeThresholds[5];
+	int16 exclusionBand = _georgeThresholds[5];
 	int16 randRow;
 	do {
-		randRow = _xp->getRandom(g_georgeThresholds[3]);
+		randRow = _xp->getRandom(_georgeThresholds[3]);
 	} while (randRow >= (carRow - exclusionBand) && randRow < (carRow + exclusionBand));
 
 	return randRow;
@@ -1149,12 +1149,12 @@ int16 BoltEngine::getRandomAsteroidRow() {
 
 bool BoltEngine::confirmAsteroidHitTest() {
 	GeorgeEntityState *candidate = nullptr;
-	int16 startIdx = g_georgeHitSearchIdx;
+	int16 startIdx = _georgeHitSearchIdx;
 	int16 idx = startIdx;
 	bool wrapped = false;
 
 	while (true) {
-		GeorgeEntityState *s = g_georgeEntityList[idx];
+		GeorgeEntityState *s = _georgeEntityList[idx];
 		if (!s) {
 			wrapped = true;
 			idx = -1;
@@ -1168,7 +1168,7 @@ bool BoltEngine::confirmAsteroidHitTest() {
 			return false;
 	}
 
-	g_georgeHitSearchIdx = idx + 1;
+	_georgeHitSearchIdx = idx + 1;
 
 	setSatelliteAnimMode(candidate, 3, candidate->variant);
 
@@ -1179,7 +1179,7 @@ bool BoltEngine::spawnAsteroid() {
 	// Count active asteroids (type 5)...
 	int16 activeCount = 0;
 	for (int16 i = 0;; i++) {
-		GeorgeEntityState *sat = g_georgeEntityList[i];
+		GeorgeEntityState *sat = _georgeEntityList[i];
 		if (!sat)
 			break;
 
@@ -1192,13 +1192,13 @@ bool BoltEngine::spawnAsteroid() {
 		return false;
 
 	// Find next inactive asteroid slot...
-	int16 startIdx = g_georgeAsteroidSearchIdx;
+	int16 startIdx = _georgeAsteroidSearchIdx;
 	int16 idx = startIdx;
 	bool wrapped = false;
 	GeorgeEntityState *candidate = nullptr;
 
 	while (true) {
-		GeorgeEntityState *s = g_georgeEntityList[idx];
+		GeorgeEntityState *s = _georgeEntityList[idx];
 		if (!s) {
 			wrapped = true;
 			idx = -1;
@@ -1212,24 +1212,24 @@ bool BoltEngine::spawnAsteroid() {
 			return false;
 	}
 
-	g_georgeAsteroidSearchIdx = idx + 1;
+	_georgeAsteroidSearchIdx = idx + 1;
 
 	// Get shuffle table row for this asteroid...
 	int16 row = getAsteroidRow();
-	int16 numCols = g_georgeThresholds[10];
+	int16 numCols = _georgeThresholds[10];
 	int16 rowBase = (numCols + 1) * row;
-	int16 colIdx = g_georgeAsteroidShuffleTable[rowBase];
-	byte animIdx = g_georgeAsteroidShuffleTable[rowBase + colIdx + 1];
+	int16 colIdx = _georgeAsteroidShuffleTable[rowBase];
+	byte animIdx = _georgeAsteroidShuffleTable[rowBase + colIdx + 1];
 
 	// Assign motion path...
-	byte *rowPaths = getResolvedPtr(g_georgeAsteroidPaths, row * 4);
+	byte *rowPaths = getResolvedPtr(_georgeAsteroidPaths, row * 4);
 	byte *path = getResolvedPtr(rowPaths, animIdx * 4);
 
 	candidate->pathTable = path;
 	colIdx++;
-	g_georgeAsteroidShuffleTable[rowBase] = (byte)colIdx;
+	_georgeAsteroidShuffleTable[rowBase] = (byte)colIdx;
 	if (colIdx >= numCols)
-		g_georgeAsteroidShuffleTable[rowBase] = 0;
+		_georgeAsteroidShuffleTable[rowBase] = 0;
 
 	// Set initial position from pic's embedded coords...
 	candidate->pathIndex = 0;
@@ -1242,19 +1242,19 @@ bool BoltEngine::spawnAsteroid() {
 }
 
 int16 BoltEngine::getRandomAsteroidWait() {
-    int16 base  = g_georgeThresholds[7];
-    int16 range = g_georgeThresholds[8];
+    int16 base  = _georgeThresholds[7];
+    int16 range = _georgeThresholds[8];
     return _xp->getRandom(range) + base;
 }
 
 int16 BoltEngine::getAsteroidRow() {
-	GeorgeEntityState *carSat = g_georgeEntityList[g_georgeCarIdx];
+	GeorgeEntityState *carSat = _georgeEntityList[_georgeCarIdx];
 	int16 carY = carSat->y >> 8;
 
 	// Find which row the car is in by scanning frame Y thresholds...
-	int16 numRows = g_georgeThresholds[9];
+	int16 numRows = _georgeThresholds[9];
 	int16 row = 0;
-	int16 *thresholds = g_georgeAsteroidThresholds;
+	int16 *thresholds = _georgeAsteroidThresholds;
 
 	for (int16 i = 0; i < numRows; i++) {
 		if (thresholds[i] > carY)
@@ -1269,7 +1269,7 @@ int16 BoltEngine::getAsteroidRow() {
 void BoltEngine::setGeorgeAnimMode(GeorgeEntityState *entity, int16 mode) {
 	entity->animMode = mode;
 	entity->variant = 0;
-	entity->animTable = g_georgeCarPics[mode];
+	entity->animTable = _georgeCarPics[mode];
 	entity->frameIndex = 0;
 	entity->frameCountdown = READ_UINT16(entity->animTable + 0x04);
 }
@@ -1284,7 +1284,7 @@ void BoltEngine::setSatelliteAnimMode(GeorgeEntityState *entity, int16 mode, int
 
 	entity->animMode = mode;
 	entity->variant = variant;
-	entity->animTable = getResolvedPtr(g_georgeSatelliteGfx, variant * 4);
+	entity->animTable = getResolvedPtr(_georgeSatelliteGfx, variant * 4);
 	entity->frameIndex = 0;
 	entity->frameCountdown = READ_UINT16(entity->animTable + 0x04);
 }
@@ -1292,7 +1292,7 @@ void BoltEngine::setSatelliteAnimMode(GeorgeEntityState *entity, int16 mode, int
 void BoltEngine::setAsteroidAnimMode(GeorgeEntityState *entity, int16 mode, int16 variant) {
 	entity->animMode = mode;
 	entity->variant = variant;
-	entity->animTable = getResolvedPtr(g_georgeAsteroidGfx, variant * 4);
+	entity->animTable = getResolvedPtr(_georgeAsteroidGfx, variant * 4);
 	entity->frameIndex = 0;
 	entity->frameCountdown = READ_UINT16(entity->animTable + 0x04);
 }
@@ -1301,7 +1301,7 @@ void BoltEngine::drawFlyingObjects() {
 	_xp->fillDisplay(0, stFront);
 
 	for (int16 i = 0;; i++) {
-		GeorgeEntityState *sat = g_georgeEntityList[i];
+		GeorgeEntityState *sat = _georgeEntityList[i];
 		if (!sat)
 			break;
 
@@ -1321,25 +1321,25 @@ void BoltEngine::getGeorgeSoundInfo(BOLTLib *boltLib, int16 member, SoundInfo *o
 	outInfo->data = memberAddr(boltLib, member);
 	outInfo->size = memberSize(boltLib, member);
 	outInfo->priority = priority;
-	outInfo->channel = g_georgeSoundChannelCounter++;
+	outInfo->channel = _georgeSoundChannelCounter++;
 }
 
 void BoltEngine::playGeorgeSound(SoundInfo *newSound, SoundInfo *nextSound) {
 	// If a queued sound exists and has higher priority than new sound, don't replace it...
-	if (newSound && g_georgeSoundQueued) {
-		if (g_georgeSoundQueued->priority > newSound->priority)
+	if (newSound && _georgeSoundQueued) {
+		if (_georgeSoundQueued->priority > newSound->priority)
 			return;
 	}
 
 	// Queue the new sounds...
-	g_georgeSoundQueued = newSound;
-	g_georgeSoundNext = nextSound;
+	_georgeSoundQueued = newSound;
+	_georgeSoundNext = nextSound;
 
 	// If currently playing sound has higher priority than new one, keep it...
-	if (g_georgeSoundToPlay && newSound) {
-		if (g_georgeSoundToPlay->priority > g_georgeSoundQueued->priority) {
+	if (_georgeSoundToPlay && newSound) {
+		if (_georgeSoundToPlay->priority > _georgeSoundQueued->priority) {
 			// If something is queued but nothing playing, kick off playback now...
-			if (g_georgeSoundQueued && !g_georgeSoundToPlay)
+			if (_georgeSoundQueued && !_georgeSoundToPlay)
 				updateGeorgeSound();
 
 			return;
@@ -1348,46 +1348,46 @@ void BoltEngine::playGeorgeSound(SoundInfo *newSound, SoundInfo *nextSound) {
 
 	// New sound wins...
 	_xp->stopSound();
-	g_georgeSoundCurrent = nullptr;
-	g_georgeSoundToPlay = nullptr;
+	_georgeSoundCurrent = nullptr;
+	_georgeSoundToPlay = nullptr;
 
 	// If something is queued but nothing playing, kick off playback now...
-	if (g_georgeSoundQueued && !g_georgeSoundToPlay)
+	if (_georgeSoundQueued && !_georgeSoundToPlay)
 		updateGeorgeSound();
 }
 
 void BoltEngine::updateGeorgeSound() {
 	int16 playBoth = 0;
 
-	if (g_georgeSoundCurrent != nullptr) {
+	if (_georgeSoundCurrent != nullptr) {
 		// Current sound still set, promote directly to play slot...
-		g_georgeSoundToPlay = g_georgeSoundCurrent;
-	} else if (g_georgeSoundQueued != nullptr) {
+		_georgeSoundToPlay = _georgeSoundCurrent;
+	} else if (_georgeSoundQueued != nullptr) {
 		// Nothing current, promote queued sound...
-		g_georgeSoundToPlay = g_georgeSoundQueued;
-		g_georgeSoundCurrent = g_georgeSoundNext;
+		_georgeSoundToPlay = _georgeSoundQueued;
+		_georgeSoundCurrent = _georgeSoundNext;
 
-		if (g_georgeSoundNext != nullptr)
+		if (_georgeSoundNext != nullptr)
 			playBoth = 1;
 
-		g_georgeSoundNext = nullptr;
-		g_georgeSoundQueued = nullptr;
+		_georgeSoundNext = nullptr;
+		_georgeSoundQueued = nullptr;
 	} else {
-		g_georgeSoundToPlay = nullptr;
+		_georgeSoundToPlay = nullptr;
 	}
 
-	if (g_georgeSoundToPlay == nullptr)
+	if (_georgeSoundToPlay == nullptr)
 		return;
 
 	// Play primary sound...
-	_xp->playSound(g_georgeSoundToPlay->data, g_georgeSoundToPlay->size, 22050);
+	_xp->playSound(_georgeSoundToPlay->data, _georgeSoundToPlay->size, 22050);
 
 	if (!playBoth)
 		return;
 
 	// Queue secondary sound twice...
-	_xp->playSound(g_georgeSoundCurrent->data, g_georgeSoundCurrent->size, 22050);
-	_xp->playSound(g_georgeSoundCurrent->data, g_georgeSoundCurrent->size, 22050);
+	_xp->playSound(_georgeSoundCurrent->data, _georgeSoundCurrent->size, 22050);
+	_xp->playSound(_georgeSoundCurrent->data, _georgeSoundCurrent->size, 22050);
 }
 
 } // End of namespace Bolt
diff --git a/engines/bolt/booths/huck.cpp b/engines/bolt/booths/huck.cpp
index ce66c678125..8efc0886589 100644
--- a/engines/bolt/booths/huck.cpp
+++ b/engines/bolt/booths/huck.cpp
@@ -26,77 +26,77 @@
 namespace Bolt {
 
 void BoltEngine::playSoundMapHuck(int16 memberId) {
-	byte *soundData = getBOLTMember(g_huckBoltLib, memberId);
-	uint32 soundSize = memberSize(g_huckBoltLib, memberId);
+	byte *soundData = getBOLTMember(_huckBoltLib, memberId);
+	uint32 soundSize = memberSize(_huckBoltLib, memberId);
 	if (soundData) {
 		_xp->playSound(soundData, soundSize, 22050);
-		g_huckSoundPlaying++;
+		_huckSoundPlaying++;
 	}
 }
 
 void BoltEngine::waitSoundMapHuck() {
-	if (!g_huckSoundPlaying)
+	if (!_huckSoundPlaying)
 		return;
 
 	uint32 dummy;
 	while (_xp->getEvent(etSound, &dummy) == etEmpty);
-	g_huckSoundPlaying--;
+	_huckSoundPlaying--;
 }
 
 void BoltEngine::setHuckColors(int16 which) {
 	if (which == 0) {
-		int16 count = g_huckPalRange[1] - g_huckPalRange[0] + 1;
-		_xp->setPalette(count, g_huckPalRange[0], g_huckPalHighlight0);
+		int16 count = _huckPalRange[1] - _huckPalRange[0] + 1;
+		_xp->setPalette(count, _huckPalRange[0], _huckPalHighlight0);
 	} else if (which == 1) {
-		int16 count = g_huckPalRange[5] - g_huckPalRange[4] + 1;
-		_xp->setPalette(count, g_huckPalRange[4], g_huckPalHighlight1);
+		int16 count = _huckPalRange[5] - _huckPalRange[4] + 1;
+		_xp->setPalette(count, _huckPalRange[4], _huckPalHighlight1);
 	}
 }
 
 void BoltEngine::restoreHuckColors(int16 which) {
 	if (which == 0) {
-		int16 count = g_huckPalRange[1] - g_huckPalRange[0] + 1;
-		_xp->setPalette(count, g_huckPalRange[0], g_huckPalSave0);
+		int16 count = _huckPalRange[1] - _huckPalRange[0] + 1;
+		_xp->setPalette(count, _huckPalRange[0], _huckPalSave0);
 	} else if (which == 1) {
-		int16 count = g_huckPalRange[5] - g_huckPalRange[4] + 1;
-		_xp->setPalette(count, g_huckPalRange[4], g_huckPalSave1);
+		int16 count = _huckPalRange[5] - _huckPalRange[4] + 1;
+		_xp->setPalette(count, _huckPalRange[4], _huckPalSave1);
 	}
 }
 
 void BoltEngine::startHuckShuffleTimer() {
-	if (g_huckShuffleTimer) {
-		_xp->killTimer(g_huckShuffleTimer);
-		g_huckShuffleTimer = 0;
+	if (_huckShuffleTimer) {
+		_xp->killTimer(_huckShuffleTimer);
+		_huckShuffleTimer = 0;
 	}
 
-	if (g_huckState.giftCount > 2) {
-		int16 speed = READ_UINT16(g_huckGiftPic + g_huckScrollOffset * 2 + 0x6E);
+	if (_huckState.giftCount > 2) {
+		int16 speed = READ_UINT16(_huckGiftPic + _huckScrollOffset * 2 + 0x6E);
 		int32 ms = (int32)speed * 1000 / 60;
-		g_huckShuffleTimer = _xp->startTimer((int16)ms);
+		_huckShuffleTimer = _xp->startTimer((int16)ms);
 	}
 }
 
 void BoltEngine::drawGift(int16 slot) {
-	byte *giftSprite = memberAddr(g_huckBoltLib, g_huckState.drawTable1[slot]);
+	byte *giftSprite = memberAddr(_huckBoltLib, _huckState.drawTable1[slot]);
 	int16 sprStride = READ_UINT16(giftSprite + 0x0A);
 	int16 sprHeight = READ_UINT16(giftSprite + 0x0C);
 
-	memset(g_huckScratchPic.pixelData, 0, (int32)sprStride * sprHeight);
+	memset(_huckScratchPic.pixelData, 0, (int32)sprStride * sprHeight);
 
-	int16 slotX = READ_UINT16(g_huckGiftPic + slot * 2 + 0x3E);
-	int16 slotY = READ_UINT16(g_huckGiftPic + slot * 2 + 0x0E);
+	int16 slotX = READ_UINT16(_huckGiftPic + slot * 2 + 0x3E);
+	int16 slotY = READ_UINT16(_huckGiftPic + slot * 2 + 0x0E);
 	Common::Rect targetRect(slotX, slotY, slotX + sprHeight, slotY + sprStride);
 
-	int16 giftCount = READ_UINT16(g_huckGiftPic);
+	int16 giftCount = READ_UINT16(_huckGiftPic);
 	for (int16 i = 0; i < giftCount; i++) {
-		if (g_huckState.drawTable2[i] != 0)
+		if (_huckState.drawTable2[i] != 0)
 			continue;
 
-		byte *otherSprite = memberAddr(g_huckBoltLib, g_huckState.drawTable1[i]);
+		byte *otherSprite = memberAddr(_huckBoltLib, _huckState.drawTable1[i]);
 		int16 otherStride = READ_UINT16(otherSprite + 0x0A);
 		int16 otherHeight = READ_UINT16(otherSprite + 0x0C);
-		int16 ox = READ_UINT16(g_huckGiftPic + i * 2 + 0x3E);
-		int16 oy = READ_UINT16(g_huckGiftPic + i * 2 + 0x0E);
+		int16 ox = READ_UINT16(_huckGiftPic + i * 2 + 0x3E);
+		int16 oy = READ_UINT16(_huckGiftPic + i * 2 + 0x0E);
 		Common::Rect otherRect(ox, oy, ox + otherHeight, oy + otherStride);
 
 		Common::Rect isect;
@@ -106,113 +106,113 @@ void BoltEngine::drawGift(int16 slot) {
 		byte *srcPixels = getResolvedPtr(otherSprite, 0x12);
 		byte *src = srcPixels + (isect.left - ox) * otherStride + (isect.top - oy);
 
-		byte *dst = g_huckScratchPic.pixelData + (isect.left - slotX) * sprStride + (isect.top - slotY);
+		byte *dst = _huckScratchPic.pixelData + (isect.left - slotX) * sprStride + (isect.top - slotY);
 
 		int16 blitWidth = isect.bottom - isect.top;
 		int16 blitHeight = isect.right - isect.left;
 		_xp->maskBlit(src, otherStride, dst, sprStride, blitWidth, blitHeight);
 	}
 
-	if ((g_huckState.hasCycle && g_huckState.selected1Slot == slot) || (g_huckState.selectionPending && g_huckState.selected2Slot == slot)) {
-		byte *hlSprite = memberAddr(g_huckBoltLib, READ_UINT16(g_huckGiftPic + 0x06));
+	if ((_huckState.hasCycle && _huckState.selected1Slot == slot) || (_huckState.selectionPending && _huckState.selected2Slot == slot)) {
+		byte *hlSprite = memberAddr(_huckBoltLib, READ_UINT16(_huckGiftPic + 0x06));
 		int16 hlStride = READ_UINT16(hlSprite + 0x0A);
 		int16 hlHeight = READ_UINT16(hlSprite + 0x0C);
 		byte *hlPixels = getResolvedPtr(hlSprite, 0x12);
-		int16 hlOffsetX = READ_UINT16(g_huckGiftPic + 0x0A);
-		int16 hlOffsetY = READ_UINT16(g_huckGiftPic + 0x08);
+		int16 hlOffsetX = READ_UINT16(_huckGiftPic + 0x0A);
+		int16 hlOffsetY = READ_UINT16(_huckGiftPic + 0x08);
 
-		byte *dst = g_huckScratchPic.pixelData + hlOffsetX * sprStride + hlOffsetY;
+		byte *dst = _huckScratchPic.pixelData + hlOffsetX * sprStride + hlOffsetY;
 
 		_xp->maskBlit(hlPixels, hlStride, dst, sprStride, hlStride, hlHeight);
 	}
 
-	g_huckScratchPic.width = sprStride;
-	g_huckScratchPic.height = sprHeight;
-	g_huckScratchPic.palette = nullptr;
-	g_huckScratchPic.flags = 0;
+	_huckScratchPic.width = sprStride;
+	_huckScratchPic.height = sprHeight;
+	_huckScratchPic.palette = nullptr;
+	_huckScratchPic.flags = 0;
 
-	_xp->displayPic(&g_huckScratchPic, slotY, slotX, stFront);
+	_xp->displayPic(&_huckScratchPic, slotY, slotX, stFront);
 }
 
 void BoltEngine::drawHuckGifts() {
 	_xp->fillDisplay(0, stFront);
 
-	int16 giftCount = READ_UINT16(g_huckGiftPic);
+	int16 giftCount = READ_UINT16(_huckGiftPic);
 
 	for (int16 slot = 0; slot < giftCount; slot++) {
-		if (g_huckState.drawTable2[slot] != 0)
+		if (_huckState.drawTable2[slot] != 0)
 			continue;
 
-		byte *gifPtr = memberAddr(g_huckBoltLib, g_huckState.drawTable1[slot]);
-		int16 x = READ_UINT16(g_huckGiftPic + slot * 2 + 0x0E);
-		int16 y = READ_UINT16(g_huckGiftPic + slot * 2 + 0x3E);
+		byte *gifPtr = memberAddr(_huckBoltLib, _huckState.drawTable1[slot]);
+		int16 x = READ_UINT16(_huckGiftPic + slot * 2 + 0x0E);
+		int16 y = READ_UINT16(_huckGiftPic + slot * 2 + 0x3E);
 		displayPic(gifPtr, x, y, stFront);
 	}
 
-	if (g_huckState.hasCycle) {
-		int16 x = READ_UINT16(g_huckGiftPic + g_huckState.selected1Slot * 2 + 0x0E) + READ_UINT16(g_huckGiftPic + 0x08);
-		int16 y = READ_UINT16(g_huckGiftPic + g_huckState.selected1Slot * 2 + 0x3E) + READ_UINT16(g_huckGiftPic + 0x0A);
-		int16 hlMember = READ_UINT16(g_huckGiftPic + 0x06);
-		byte *hlPtr = memberAddr(g_huckBoltLib, hlMember);
+	if (_huckState.hasCycle) {
+		int16 x = READ_UINT16(_huckGiftPic + _huckState.selected1Slot * 2 + 0x0E) + READ_UINT16(_huckGiftPic + 0x08);
+		int16 y = READ_UINT16(_huckGiftPic + _huckState.selected1Slot * 2 + 0x3E) + READ_UINT16(_huckGiftPic + 0x0A);
+		int16 hlMember = READ_UINT16(_huckGiftPic + 0x06);
+		byte *hlPtr = memberAddr(_huckBoltLib, hlMember);
 		displayPic(hlPtr, x, y, stFront);
 	}
 
-	if (g_huckState.selectionPending) {
-		int16 x = READ_UINT16(g_huckGiftPic + g_huckState.selected2Slot * 2 + 0x0E) + READ_UINT16(g_huckGiftPic + 0x08);
-		int16 y = READ_UINT16(g_huckGiftPic + g_huckState.selected2Slot * 2 + 0x3E) + READ_UINT16(g_huckGiftPic + 0x0A);
-		int16 hlMember = READ_UINT16(g_huckGiftPic + 0x06);
-		byte *hlPtr = memberAddr(g_huckBoltLib, hlMember);
+	if (_huckState.selectionPending) {
+		int16 x = READ_UINT16(_huckGiftPic + _huckState.selected2Slot * 2 + 0x0E) + READ_UINT16(_huckGiftPic + 0x08);
+		int16 y = READ_UINT16(_huckGiftPic + _huckState.selected2Slot * 2 + 0x3E) + READ_UINT16(_huckGiftPic + 0x0A);
+		int16 hlMember = READ_UINT16(_huckGiftPic + 0x06);
+		byte *hlPtr = memberAddr(_huckBoltLib, hlMember);
 		displayPic(hlPtr, x, y, stFront);
 	}
 }
 
 void BoltEngine::checkHuckLevelComplete() {
-	if (g_huckState.giftCount == 2) {
+	if (_huckState.giftCount == 2) {
 		// Last pair matched, level complete!
-		g_huckExitFlag = 1;
-		g_huckState.levelComplete = 1;
-		g_huckState.levelNumber++;
+		_huckExitFlag = 1;
+		_huckState.levelComplete = 1;
+		_huckState.levelNumber++;
 
-		if (g_huckState.levelNumber > 10)
-			g_huckState.levelNumber = 10;
+		if (_huckState.levelNumber > 10)
+			_huckState.levelNumber = 10;
 
 		_xp->fillDisplay(0, stFront);
 	} else {
 		// More pairs remain, redraw the matched slots as empty...
-		drawGift(g_huckState.selected1Slot);
-		drawGift(g_huckState.selected2Slot);
+		drawGift(_huckState.selected1Slot);
+		drawGift(_huckState.selected2Slot);
 	}
 
-	g_huckState.selected1Slot = -1;
-	g_huckState.selected2Slot = -1;
-	g_huckState.giftCount -= 2;
+	_huckState.selected1Slot = -1;
+	_huckState.selected2Slot = -1;
+	_huckState.giftCount -= 2;
 
 	_xp->updateDisplay();
 
 	waitSoundMapHuck();
-	g_huckScrollOffset++;
+	_huckScrollOffset++;
 	startHuckShuffleTimer();
 }
 
 bool BoltEngine::initHuckDisplay() {
-	byte *palPtr = memberAddr(g_huckBoltLib, READ_UINT16(g_huckBgPic));
+	byte *palPtr = memberAddr(_huckBoltLib, READ_UINT16(_huckBgPic));
 
-	g_huckHotSpotCount = 0;
-	g_huckScreensaverTimer = 0;
-	g_huckBlinkTimer = 0;
-	g_huckExitFlag = 0;
+	_huckHotSpotCount = 0;
+	_huckScreensaverTimer = 0;
+	_huckBlinkTimer = 0;
+	_huckExitFlag = 0;
 
 	int32 maxArea = 0;
-	int16 giftCount = READ_UINT16(g_huckGiftPic);
+	int16 giftCount = READ_UINT16(_huckGiftPic);
 	for (int16 i = 0; i < giftCount; i++) {
-		byte *spr = memberAddr(g_huckBoltLib, g_huckState.drawTable1[i]);
+		byte *spr = memberAddr(_huckBoltLib, _huckState.drawTable1[i]);
 		int32 area = (int32)READ_UINT16(spr + 0x0A) * READ_UINT16(spr + 0x0C);
 		if (area > maxArea)
 			maxArea = area;
 	}
 
-	g_huckScratchPic.pixelData = (byte *)_xp->allocMem(maxArea);
-	if (!g_huckScratchPic.pixelData)
+	_huckScratchPic.pixelData = (byte *)_xp->allocMem(maxArea);
+	if (!_huckScratchPic.pixelData)
 		return false;
 
 	uint32 dummy;
@@ -221,81 +221,81 @@ bool BoltEngine::initHuckDisplay() {
 	_xp->stopCycle();
 	_xp->setTransparency(false);
 	displayColors(palPtr, stBack, 1);
-	displayPic(g_huckBgDisplayPic, g_displayX, g_displayY, stFront);
+	displayPic(_huckBgDisplayPic, _displayX, _displayY, stFront);
 	_xp->updateDisplay();
 
 	_xp->setTransparency(true);
 	displayColors(palPtr, stFront, 0);
 	displayColors(palPtr, stBack, 1);
-	displayPic(g_huckBgDisplayPic, g_displayX, g_displayY, stBack);
+	displayPic(_huckBgDisplayPic, _displayX, _displayY, stBack);
 	_xp->fillDisplay(0, stFront);
 	_xp->updateDisplay();
 
 	drawHuckGifts();
 	_xp->updateDisplay();
 
-	if (g_huckState.hasCycle) {
+	if (_huckState.hasCycle) {
 		XPCycleState cycleSpec[4];
-		byte *cycleData = memberAddr(g_huckBoltLib, READ_UINT16(g_huckGiftPic + 0x0C));
+		byte *cycleData = memberAddr(_huckBoltLib, READ_UINT16(_huckGiftPic + 0x0C));
 		boltCycleToXPCycle(cycleData, cycleSpec);
 		_xp->startCycle(cycleSpec);
 	}
 
-	g_huckCursorY = 0x78;
-	g_huckCursorX = 0xC0;
+	_huckCursorY = 0x78;
+	_huckCursorX = 0xC0;
 	_xp->setCursorPos(0x78, 0xC0);
 	_xp->setCursorColor(0xFF, 0xFF, 0xFF);
 	_xp->showCursor();
 
 	// Read palette ranges from gift pic data...
-	g_huckPalRange[0] = READ_UINT16(g_huckGiftPic + 0x8C) + 0x80;
-	g_huckPalRange[1] = READ_UINT16(g_huckGiftPic + 0x8E) + 0x80;
-	g_huckPalRange[2] = READ_UINT16(g_huckGiftPic + 0x90) + 0x80;
-	g_huckPalRange[3] = READ_UINT16(g_huckGiftPic + 0x92) + 0x80;
-	g_huckPalRange[4] = READ_UINT16(g_huckGiftPic + 0x9E) + 0x80;
-	g_huckPalRange[5] = READ_UINT16(g_huckGiftPic + 0xA0) + 0x80;
-	g_huckPalRange[6] = READ_UINT16(g_huckGiftPic + 0xA2) + 0x80;
-	g_huckPalRange[7] = READ_UINT16(g_huckGiftPic + 0xA4) + 0x80;
+	_huckPalRange[0] = READ_UINT16(_huckGiftPic + 0x8C) + 0x80;
+	_huckPalRange[1] = READ_UINT16(_huckGiftPic + 0x8E) + 0x80;
+	_huckPalRange[2] = READ_UINT16(_huckGiftPic + 0x90) + 0x80;
+	_huckPalRange[3] = READ_UINT16(_huckGiftPic + 0x92) + 0x80;
+	_huckPalRange[4] = READ_UINT16(_huckGiftPic + 0x9E) + 0x80;
+	_huckPalRange[5] = READ_UINT16(_huckGiftPic + 0xA0) + 0x80;
+	_huckPalRange[6] = READ_UINT16(_huckGiftPic + 0xA2) + 0x80;
+	_huckPalRange[7] = READ_UINT16(_huckGiftPic + 0xA4) + 0x80;
 
 	// Save original palette ranges, load highlight palette ranges...
-	_xp->getPalette(g_huckPalRange[0], g_huckPalRange[1] - g_huckPalRange[0] + 1, g_huckPalSave0);
-	_xp->getPalette(g_huckPalRange[2], g_huckPalRange[3] - g_huckPalRange[2] + 1, g_huckPalHighlight0);
-	_xp->getPalette(g_huckPalRange[4], g_huckPalRange[5] - g_huckPalRange[4] + 1, g_huckPalSave1);
-	_xp->getPalette(g_huckPalRange[6], g_huckPalRange[7] - g_huckPalRange[6] + 1, g_huckPalHighlight1);
+	_xp->getPalette(_huckPalRange[0], _huckPalRange[1] - _huckPalRange[0] + 1, _huckPalSave0);
+	_xp->getPalette(_huckPalRange[2], _huckPalRange[3] - _huckPalRange[2] + 1, _huckPalHighlight0);
+	_xp->getPalette(_huckPalRange[4], _huckPalRange[5] - _huckPalRange[4] + 1, _huckPalSave1);
+	_xp->getPalette(_huckPalRange[6], _huckPalRange[7] - _huckPalRange[6] + 1, _huckPalHighlight1);
 
 	return true;
 }
 
 bool BoltEngine::loadHuckResources() {
-	int16 stateIdx = g_huckState.levelNumber - 1;
+	int16 stateIdx = _huckState.levelNumber - 1;
 	int16 giftGroupId = (stateIdx << 10) + 0x100;
-	g_huckGiftGroupId = giftGroupId;
+	_huckGiftGroupId = giftGroupId;
 
-	if (!getBOLTGroup(g_huckBoltLib, giftGroupId, 1))
+	if (!getBOLTGroup(_huckBoltLib, giftGroupId, 1))
 		return false;
 
-	g_huckGiftPic = memberAddr(g_huckBoltLib, giftGroupId);
+	_huckGiftPic = memberAddr(_huckBoltLib, giftGroupId);
 
 	for (int16 i = 0; i < 11; i++) {
-		int16 *speed = (int16 *)(g_huckGiftPic + 0x6E + i * 2);
+		int16 *speed = (int16 *)(_huckGiftPic + 0x6E + i * 2);
 		if (*speed < 30)
 			*speed = 30;
 	}
 
 	// Load background display pic...
-	int16 bgMember = (g_displayMode != 0) ? READ_UINT16(g_huckGiftPic + 4) : READ_UINT16(g_huckGiftPic + 2);
-	g_huckBgDisplayPic = memberAddr(g_huckBoltLib, bgMember);
+	int16 bgMember = (_displayMode != 0) ? READ_UINT16(_huckGiftPic + 4) : READ_UINT16(_huckGiftPic + 2);
+	_huckBgDisplayPic = memberAddr(_huckBoltLib, bgMember);
 
 	// Determine variant...
-	int16 slot = g_huckState.slotIndex[stateIdx];
-	int16 variant = g_huckGlobal[stateIdx * 3 + slot];
+	int16 slot = _huckState.slotIndex[stateIdx];
+	int16 variant = _huckGlobal[stateIdx * 3 + slot];
 	int16 variantGroupId = ((stateIdx * 4 + variant + 1) << 8) + 0x100;
-	g_huckVariantGroupId = variantGroupId;
+	_huckVariantGroupId = variantGroupId;
 
-	if (!getBOLTGroup(g_huckBoltLib, variantGroupId, 1))
+	if (!getBOLTGroup(_huckBoltLib, variantGroupId, 1))
 		return false;
 
-	g_huckBgPic = memberAddr(g_huckBoltLib, variantGroupId);
+	_huckBgPic = memberAddr(_huckBoltLib, variantGroupId);
 
 	return true;
 }
@@ -303,54 +303,54 @@ bool BoltEngine::loadHuckResources() {
 void BoltEngine::unloadHuckResources() {
 	_xp->stopCycle();
 	_xp->hideCursor();
-	freeBOLTGroup(g_huckBoltLib, g_huckVariantGroupId, 1);
-	freeBOLTGroup(g_huckBoltLib, g_huckGiftGroupId, 1);
+	freeBOLTGroup(_huckBoltLib, _huckVariantGroupId, 1);
+	freeBOLTGroup(_huckBoltLib, _huckGiftGroupId, 1);
 }
 
 bool BoltEngine::initHuckLevel() {
 	// Advance slot variant (wraps 0..2)
-	int16 stateIdx = g_huckState.levelNumber - 1;
-	g_huckState.slotIndex[stateIdx]++;
-	if (g_huckState.slotIndex[stateIdx] >= 3)
-		g_huckState.slotIndex[stateIdx] = 0;
+	int16 stateIdx = _huckState.levelNumber - 1;
+	_huckState.slotIndex[stateIdx]++;
+	if (_huckState.slotIndex[stateIdx] >= 3)
+		_huckState.slotIndex[stateIdx] = 0;
 
 	if (!loadHuckResources())
 		return false;
 
-	g_huckState.giftCount = READ_UINT16(g_huckGiftPic);
-	g_huckState.levelComplete = 0;
-	g_huckState.hasCycle = 0;
-	g_huckState.selectionPending = 0;
-	g_huckState.selected1Slot = -1;
-	g_huckState.selected2Slot = -1;
+	_huckState.giftCount = READ_UINT16(_huckGiftPic);
+	_huckState.levelComplete = 0;
+	_huckState.hasCycle = 0;
+	_huckState.selectionPending = 0;
+	_huckState.selected1Slot = -1;
+	_huckState.selected2Slot = -1;
 
 	for (int16 i = 0; i < 24; i++) {
-		g_huckState.drawTable1[i] = 0;
-		g_huckState.drawTable2[i] = 0;
+		_huckState.drawTable1[i] = 0;
+		_huckState.drawTable2[i] = 0;
 	}
 
-	int16 baseId = READ_UINT16(g_huckBgPic + 2);
+	int16 baseId = READ_UINT16(_huckBgPic + 2);
 
 	// Distribute gift pairs into random empty slots...
 	int16 count = 0;
-	int16 giftCount = READ_UINT16(g_huckGiftPic);
+	int16 giftCount = READ_UINT16(_huckGiftPic);
 	while (count < giftCount) {
 		int16 slot = _xp->getRandom(giftCount);
-		while (g_huckState.drawTable1[slot] > 0) {
+		while (_huckState.drawTable1[slot] > 0) {
 			slot++;
 			if (slot >= giftCount)
 				slot = 0;
 		}
 
 		// Pairs share the same member ID
-		g_huckState.drawTable1[slot] = count / 2 + baseId;
+		_huckState.drawTable1[slot] = count / 2 + baseId;
 		count++;
 	}
 
 	if (!initHuckDisplay())
 		return false;
 
-	g_huckScrollOffset = 0;
+	_huckScrollOffset = 0;
 	startHuckShuffleTimer();
 	return true;
 }
@@ -362,8 +362,8 @@ bool BoltEngine::resumeHuckLevel() {
 	if (!initHuckDisplay())
 		return false;
 
-	if (g_huckState.giftCount > 2) {
-		g_huckScrollOffset = (READ_UINT16(g_huckGiftPic) - g_huckState.giftCount) >> 1;
+	if (_huckState.giftCount > 2) {
+		_huckScrollOffset = (READ_UINT16(_huckGiftPic) - _huckState.giftCount) >> 1;
 		startHuckShuffleTimer();
 	}
 
@@ -372,26 +372,26 @@ bool BoltEngine::resumeHuckLevel() {
 
 bool BoltEngine::initHuck() {
 	_xp->randomize();
-	g_huckSoundPlaying = 0;
+	_huckSoundPlaying = 0;
 
-	if (!vLoad(&g_huckGlobal, "HuckGlobal")) {
+	if (!vLoad(&_huckGlobal, "HuckGlobal")) {
 		// First run, generate random variant permutations for all 10 levels...
 		for (int16 level = 0; level < 10; level++) {
-			g_huckState.slotIndex[level] = -1;
+			_huckState.slotIndex[level] = -1;
 
 			// Fill row with sentinel value 3...
 			for (int16 j = 0; j < 3; j++)
-				g_huckGlobal[level * 3 + j] = 3;
+				_huckGlobal[level * 3 + j] = 3;
 
 			// Place variants 0, 1, 2 into random empty slots...
 			for (int16 variant = 0; variant < 3; variant++) {
 				int16 slot = _xp->getRandom(3);
-				while (g_huckGlobal[level * 3 + slot] != 3) {
+				while (_huckGlobal[level * 3 + slot] != 3) {
 					slot++;
 					if (slot >= 3)
 						slot = 0;
 				}
-				g_huckGlobal[level * 3 + slot] = variant;
+				_huckGlobal[level * 3 + slot] = variant;
 			}
 		}
 	}
@@ -399,38 +399,38 @@ bool BoltEngine::initHuck() {
 	byte huckStateBuf[0x86] = { 0 };
 
 	if (!vLoad(&huckStateBuf, "Huck")) {
-		g_huckState.levelNumber = 1;
+		_huckState.levelNumber = 1;
 		return initHuckLevel();
 	}
 
 	Common::SeekableReadStream *huckStateReadStream = new Common::MemoryReadStream(huckStateBuf, sizeof(huckStateBuf), DisposeAfterUse::NO);
 
-	g_huckState.levelNumber = huckStateReadStream->readSint16BE();
+	_huckState.levelNumber = huckStateReadStream->readSint16BE();
 
 	for (int i = 0; i < 10; i++)
-		g_huckState.slotIndex[i] = huckStateReadStream->readSint16BE();
+		_huckState.slotIndex[i] = huckStateReadStream->readSint16BE();
 
-	g_huckState.levelComplete = huckStateReadStream->readSint16BE();
+	_huckState.levelComplete = huckStateReadStream->readSint16BE();
 
 	for (int i = 0; i < 24; i++)
-		g_huckState.drawTable1[i] = huckStateReadStream->readSint16BE();
+		_huckState.drawTable1[i] = huckStateReadStream->readSint16BE();
 
 	for (int i = 0; i < 24; i++)
-		g_huckState.drawTable2[i] = huckStateReadStream->readSint16BE();
+		_huckState.drawTable2[i] = huckStateReadStream->readSint16BE();
 
-	g_huckState.giftCount = huckStateReadStream->readSint16BE();
-	g_huckState.hasCycle = huckStateReadStream->readSint16BE();
-	g_huckState.selectionPending = huckStateReadStream->readSint16BE();
-	g_huckState.selected1Slot = huckStateReadStream->readSint16BE();
-	g_huckState.selected2Slot = huckStateReadStream->readSint16BE();
-	g_huckState.selected1SpriteId = huckStateReadStream->readSint16BE();
-	g_huckState.selected2SpriteId = huckStateReadStream->readSint16BE();
+	_huckState.giftCount = huckStateReadStream->readSint16BE();
+	_huckState.hasCycle = huckStateReadStream->readSint16BE();
+	_huckState.selectionPending = huckStateReadStream->readSint16BE();
+	_huckState.selected1Slot = huckStateReadStream->readSint16BE();
+	_huckState.selected2Slot = huckStateReadStream->readSint16BE();
+	_huckState.selected1SpriteId = huckStateReadStream->readSint16BE();
+	_huckState.selected2SpriteId = huckStateReadStream->readSint16BE();
 
 	// Sanity check: should be exactly 0x86
 	assert(huckStateReadStream->pos() == 0x86);
 	delete huckStateReadStream;
 
-	if (g_huckState.levelComplete)
+	if (_huckState.levelComplete)
 		return initHuckLevel();
 	else
 		return resumeHuckLevel();
@@ -446,50 +446,50 @@ void BoltEngine::huckToggleBlinking(int16 *state, int16 which) {
 
 void BoltEngine::huckUpdateHotSpots(int16 x, int16 y) {
 	Common::Rect helpRect(
-		READ_UINT16(g_huckGiftPic + 0x84), READ_UINT16(g_huckGiftPic + 0x88),
-		READ_UINT16(g_huckGiftPic + 0x86), READ_UINT16(g_huckGiftPic + 0x8A));
+		READ_UINT16(_huckGiftPic + 0x84), READ_UINT16(_huckGiftPic + 0x88),
+		READ_UINT16(_huckGiftPic + 0x86), READ_UINT16(_huckGiftPic + 0x8A));
 
 	Common::Rect exitRect(
-		READ_UINT16(g_huckGiftPic + 0x96), READ_UINT16(g_huckGiftPic + 0x9A),
-		READ_UINT16(g_huckGiftPic + 0x98), READ_UINT16(g_huckGiftPic + 0x9C));
+		READ_UINT16(_huckGiftPic + 0x96), READ_UINT16(_huckGiftPic + 0x9A),
+		READ_UINT16(_huckGiftPic + 0x98), READ_UINT16(_huckGiftPic + 0x9C));
 
 	if (helpRect.contains(x, y)) {
-		if (!g_huckScreensaverTimer && !g_huckHotSpotCount)
+		if (!_huckScreensaverTimer && !_huckHotSpotCount)
 			setHuckColors(0);
 	} else {
-		if (!g_huckScreensaverTimer && !g_huckHotSpotCount)
+		if (!_huckScreensaverTimer && !_huckHotSpotCount)
 			restoreHuckColors(0);
 	}
 
 	if (exitRect.contains(x, y)) {
-		if (!g_huckBlinkTimer)
+		if (!_huckBlinkTimer)
 			setHuckColors(1);
 
 		return;
 	} else {
-		if (!g_huckBlinkTimer)
+		if (!_huckBlinkTimer)
 			restoreHuckColors(1);
 	}
 }
 
 int16 BoltEngine::findGift(int16 x, int16 y) {
-	byte *sprite0 = memberAddr(g_huckBoltLib, g_huckState.drawTable1[0]);
+	byte *sprite0 = memberAddr(_huckBoltLib, _huckState.drawTable1[0]);
 	int16 sprH = READ_UINT16(sprite0 + 0x0A);
 	int16 sprW = READ_UINT16(sprite0 + 0x0C);
 
-	int16 giftCount = READ_UINT16(g_huckGiftPic);
+	int16 giftCount = READ_UINT16(_huckGiftPic);
 
 	// Iterate in reverse, topmost gift wins...
 	for (int16 slot = giftCount - 1; slot >= 0; slot--) {
-		if (g_huckState.drawTable2[slot] != 0)
+		if (_huckState.drawTable2[slot] != 0)
 			continue;
 
-		int16 slotY = READ_UINT16(g_huckGiftPic + slot * 2 + 0x3E);
-		int16 slotX = READ_UINT16(g_huckGiftPic + slot * 2 + 0x0E);
+		int16 slotY = READ_UINT16(_huckGiftPic + slot * 2 + 0x3E);
+		int16 slotX = READ_UINT16(_huckGiftPic + slot * 2 + 0x0E);
 		int16 slotY2 = slotY + sprH - 1;
 		int16 slotX2 = slotX + sprW - 1;
 
-		byte *sprite = memberAddr(g_huckBoltLib, g_huckState.drawTable1[slot]);
+		byte *sprite = memberAddr(_huckBoltLib, _huckState.drawTable1[slot]);
 
 		if (y < slotY || y > slotY2)
 			continue;
@@ -510,36 +510,36 @@ bool BoltEngine::handleGiftSelect(int16 x, int16 y) {
 	if (slot == -1)
 		return false;
 
-	if (!g_huckState.hasCycle) {
+	if (!_huckState.hasCycle) {
 		// No selection yet, select first gift...
-		g_huckState.hasCycle = 1;
-		g_huckState.selected1SpriteId = g_huckState.drawTable1[slot];
-		g_huckState.selected1Slot = slot;
-		playSoundMapHuck((int16)READ_UINT16(g_huckGiftPic + 0xA8));
+		_huckState.hasCycle = 1;
+		_huckState.selected1SpriteId = _huckState.drawTable1[slot];
+		_huckState.selected1Slot = slot;
+		playSoundMapHuck((int16)READ_UINT16(_huckGiftPic + 0xA8));
 		drawGift(slot);
 		_xp->updateDisplay();
 
 		// Start palette cycle for highlight...
 		XPCycleState cycleSpec[4];
-		byte *cycleData = memberAddr(g_huckBoltLib, (int16)READ_UINT16(g_huckGiftPic + 0x0C));
+		byte *cycleData = memberAddr(_huckBoltLib, (int16)READ_UINT16(_huckGiftPic + 0x0C));
 		boltCycleToXPCycle(cycleData, cycleSpec);
 		_xp->startCycle(cycleSpec);
 
-	} else if (!g_huckState.selectionPending) {
-		if (g_huckState.selected1Slot == slot) {
+	} else if (!_huckState.selectionPending) {
+		if (_huckState.selected1Slot == slot) {
 			// Same gift clicked again, deselect...
-			g_huckState.hasCycle = 0;
-			g_huckState.selected1Slot = -1;
-			playSoundMapHuck((int16)READ_UINT16(g_huckGiftPic + 0xAA));
+			_huckState.hasCycle = 0;
+			_huckState.selected1Slot = -1;
+			playSoundMapHuck((int16)READ_UINT16(_huckGiftPic + 0xAA));
 			drawGift(slot);
 			_xp->updateDisplay();
 			_xp->stopCycle();
 		} else {
 			// Different gift, select second...
-			g_huckState.selectionPending = 1;
-			g_huckState.selected2SpriteId = g_huckState.drawTable1[slot];
-			g_huckState.selected2Slot = slot;
-			playSoundMapHuck((int16)READ_UINT16(g_huckGiftPic + 0xAC));
+			_huckState.selectionPending = 1;
+			_huckState.selected2SpriteId = _huckState.drawTable1[slot];
+			_huckState.selected2Slot = slot;
+			playSoundMapHuck((int16)READ_UINT16(_huckGiftPic + 0xAC));
 			drawGift(slot);
 			_xp->updateDisplay();
 		}
@@ -556,45 +556,45 @@ void BoltEngine::huckHandleActionButton(int16 x, int16 y) {
 	bool var_6 = false;
 	bool var_4 = false;
 
-	if (g_huckHotSpotCount != 0) {
-		g_huckHotSpotCount = 0;
+	if (_huckHotSpotCount != 0) {
+		_huckHotSpotCount = 0;
 		var_6 = true;
 		stopAnimation();
 
-		if (g_huckBlinkTimer) {
-			_xp->killTimer(g_huckBlinkTimer);
-			g_huckBlinkTimer = 0;
+		if (_huckBlinkTimer) {
+			_xp->killTimer(_huckBlinkTimer);
+			_huckBlinkTimer = 0;
 		}
 
-		huckUpdateHotSpots(g_huckCursorX, g_huckCursorY);
+		huckUpdateHotSpots(_huckCursorX, _huckCursorY);
 	}
 
 	Common::Rect helpRect(
-		READ_UINT16(g_huckGiftPic + 0x84), READ_UINT16(g_huckGiftPic + 0x88),
-		READ_UINT16(g_huckGiftPic + 0x86), READ_UINT16(g_huckGiftPic + 0x8A));
+		READ_UINT16(_huckGiftPic + 0x84), READ_UINT16(_huckGiftPic + 0x88),
+		READ_UINT16(_huckGiftPic + 0x86), READ_UINT16(_huckGiftPic + 0x8A));
 
 	if (helpRect.contains(x, y)) {
 		var_4 = true;
-		if (g_huckScreensaverTimer) {
-			_xp->killTimer(g_huckScreensaverTimer);
-			g_huckScreensaverTimer = 0;
+		if (_huckScreensaverTimer) {
+			_xp->killTimer(_huckScreensaverTimer);
+			_huckScreensaverTimer = 0;
 			setHuckColors(0);
 		}
 
-		if (!g_huckHotSpotCount && !var_6) {
-			g_huckHotSpotCount = startAnimation(g_rtfHandle, 0x1A);
-			g_huckActionState = 0;
+		if (!_huckHotSpotCount && !var_6) {
+			_huckHotSpotCount = startAnimation(_rtfHandle, 0x1A);
+			_huckActionState = 0;
 		}
 	} else {
 		Common::Rect exitRect(
-			READ_UINT16(g_huckGiftPic + 0x96), READ_UINT16(g_huckGiftPic + 0x9A),
-			READ_UINT16(g_huckGiftPic + 0x98), READ_UINT16(g_huckGiftPic + 0x9C));
+			READ_UINT16(_huckGiftPic + 0x96), READ_UINT16(_huckGiftPic + 0x9A),
+			READ_UINT16(_huckGiftPic + 0x98), READ_UINT16(_huckGiftPic + 0x9C));
 
 		if (exitRect.contains(x, y)) {
 			setHuckColors(1);
 			restoreHuckColors(0);
-			g_huckReturnBooth = 3;
-			g_huckExitFlag = 1;
+			_huckReturnBooth = 3;
+			_huckExitFlag = 1;
 		}
 	}
 
@@ -602,9 +602,9 @@ void BoltEngine::huckHandleActionButton(int16 x, int16 y) {
 		var_4 = true;
 
 	if (var_4) {
-		if (g_huckScreensaverTimer) {
-			_xp->killTimer(g_huckScreensaverTimer);
-			g_huckScreensaverTimer = 0;
+		if (_huckScreensaverTimer) {
+			_xp->killTimer(_huckScreensaverTimer);
+			_huckScreensaverTimer = 0;
 			restoreHuckColors(0);
 		}
 
@@ -613,17 +613,17 @@ void BoltEngine::huckHandleActionButton(int16 x, int16 y) {
 }
 
 void BoltEngine::giftSwap() {
-	if (g_huckState.giftCount <= 2)
+	if (_huckState.giftCount <= 2)
 		return;
 
 	// Determine pool size excluding selected gifts...
 	int16 pool;
-	if (g_huckState.selectionPending)
-		pool = g_huckState.giftCount - 2;
-	else if (g_huckState.hasCycle)
-		pool = g_huckState.giftCount - 1;
+	if (_huckState.selectionPending)
+		pool = _huckState.giftCount - 2;
+	else if (_huckState.hasCycle)
+		pool = _huckState.giftCount - 1;
 	else
-		pool = g_huckState.giftCount;
+		pool = _huckState.giftCount;
 
 	// Pick two distinct random indices into the eligible pool...
 	int16 randA = _xp->getRandom(pool);
@@ -638,14 +638,14 @@ void BoltEngine::giftSwap() {
 	int16 slotA = -1;
 	int16 slotB = 0;
 	int16 counter = -1;
-	for (int16 i = 0, j = 0; j <= READ_UINT16(g_huckGiftPic); i++, j++) {
-		if (g_huckState.drawTable2[j] != 0)
+	for (int16 i = 0, j = 0; j <= READ_UINT16(_huckGiftPic); i++, j++) {
+		if (_huckState.drawTable2[j] != 0)
 			continue;
 
-		if (j == g_huckState.selected1Slot)
+		if (j == _huckState.selected1Slot)
 			continue;
 
-		if (j == g_huckState.selected2Slot)
+		if (j == _huckState.selected2Slot)
 			continue;
 
 		counter++;
@@ -658,20 +658,20 @@ void BoltEngine::giftSwap() {
 	}
 
 	// If the two chosen slots have the same sprite, find a different slotB...
-	if (g_huckState.drawTable1[slotA] == g_huckState.drawTable1[slotB]) {
+	if (_huckState.drawTable1[slotA] == _huckState.drawTable1[slotB]) {
 		// Walk forward from slotB, skipping invalid candidates...
 		while (true) {
 			slotB++;
-			if (slotB >= READ_UINT16(g_huckGiftPic))
+			if (slotB >= READ_UINT16(_huckGiftPic))
 				slotB = 0;
 
-			if (g_huckState.drawTable2[slotB] != 0)
+			if (_huckState.drawTable2[slotB] != 0)
 				continue;
 
-			if (slotB == g_huckState.selected2Slot)
+			if (slotB == _huckState.selected2Slot)
 				continue;
 
-			if (slotB == g_huckState.selected1Slot)
+			if (slotB == _huckState.selected1Slot)
 				continue;
 
 			if (slotB == slotA)
@@ -682,37 +682,37 @@ void BoltEngine::giftSwap() {
 	}
 
 	// Swap entries in draw table...
-	int16 tmp = g_huckState.drawTable1[slotA];
-	g_huckState.drawTable1[slotA] = g_huckState.drawTable1[slotB];
-	g_huckState.drawTable1[slotB] = tmp;
+	int16 tmp = _huckState.drawTable1[slotA];
+	_huckState.drawTable1[slotA] = _huckState.drawTable1[slotB];
+	_huckState.drawTable1[slotB] = tmp;
 
 	drawGift(slotA);
 	drawGift(slotB);
 }
 
 void BoltEngine::resolveHuckSelection() {
-	if (!g_huckState.selectionPending)
+	if (!_huckState.selectionPending)
 		return;
 
 	_xp->stopCycle();
 
-	if (g_huckState.selected1SpriteId == g_huckState.selected2SpriteId) {
+	if (_huckState.selected1SpriteId == _huckState.selected2SpriteId) {
 		// Match, play match sound, mark both slots as matched...
-		playSoundMapHuck((int16)READ_UINT16(g_huckGiftPic + 0xAE));
-		g_huckState.drawTable2[g_huckState.selected1Slot] = 1;
-		g_huckState.drawTable2[g_huckState.selected2Slot] = 1;
-		g_huckState.hasCycle = 0;
-		g_huckState.selectionPending = 0;
+		playSoundMapHuck((int16)READ_UINT16(_huckGiftPic + 0xAE));
+		_huckState.drawTable2[_huckState.selected1Slot] = 1;
+		_huckState.drawTable2[_huckState.selected2Slot] = 1;
+		_huckState.hasCycle = 0;
+		_huckState.selectionPending = 0;
 		checkHuckLevelComplete();
 	} else {
 		// Mismatch, play mismatch sound, redraw both gifts unselected...
-		playSoundMapHuck((int16)READ_UINT16(g_huckGiftPic + 0xB0));
-		g_huckState.hasCycle = 0;
-		g_huckState.selectionPending = 0;
-		drawGift(g_huckState.selected1Slot);
-		drawGift(g_huckState.selected2Slot);
-		g_huckState.selected1Slot = -1;
-		g_huckState.selected2Slot = -1;
+		playSoundMapHuck((int16)READ_UINT16(_huckGiftPic + 0xB0));
+		_huckState.hasCycle = 0;
+		_huckState.selectionPending = 0;
+		drawGift(_huckState.selected1Slot);
+		drawGift(_huckState.selected2Slot);
+		_huckState.selected1Slot = -1;
+		_huckState.selected2Slot = -1;
 		waitSoundMapHuck();
 	}
 }
@@ -720,11 +720,11 @@ void BoltEngine::resolveHuckSelection() {
 void BoltEngine::handleEvent(int16 eventType, uint32 eventData) {
 	switch (eventType) {
 	case etMouseDown:
-		huckHandleActionButton(g_huckCursorX, g_huckCursorY);
+		huckHandleActionButton(_huckCursorX, _huckCursorY);
 		resolveHuckSelection();
 
 		// The original does:
-		// if (g_huckExitFlag)
+		// if (_huckExitFlag)
 		// 	return;
 		//
 		// and then breaks, which is redundant...
@@ -732,21 +732,21 @@ void BoltEngine::handleEvent(int16 eventType, uint32 eventData) {
 		break;
 
 	case etMouseMove:
-		g_huckCursorX = (int16)(eventData >> 16);
-		g_huckCursorY = (int16)(eventData & -1);
-		huckUpdateHotSpots(g_huckCursorX, g_huckCursorY);
+		_huckCursorX = (int16)(eventData >> 16);
+		_huckCursorY = (int16)(eventData & -1);
+		huckUpdateHotSpots(_huckCursorX, _huckCursorY);
 		break;
 
 	case etTimer: {
-		if (eventData == g_huckScreensaverTimer) {
-			huckToggleBlinking(&g_huckScreensaverFlag, 0);
-			int16 ms = (int16)((int32)READ_UINT16(g_huckGiftPic + 0x94) * 1000 / 60);
-			g_huckScreensaverTimer = _xp->startTimer(ms);
-		} else if (eventData == g_huckBlinkTimer) {
-			huckToggleBlinking(&g_huckBlinkFlag, 1);
-			int16 ms = (int16)((int32)READ_UINT16(g_huckGiftPic + 0x94) * 1000 / 60);
-			g_huckBlinkTimer = _xp->startTimer(ms);
-		} else if (eventData == g_huckShuffleTimer) {
+		if (eventData == _huckScreensaverTimer) {
+			huckToggleBlinking(&_huckScreensaverFlag, 0);
+			int16 ms = (int16)((int32)READ_UINT16(_huckGiftPic + 0x94) * 1000 / 60);
+			_huckScreensaverTimer = _xp->startTimer(ms);
+		} else if (eventData == _huckBlinkTimer) {
+			huckToggleBlinking(&_huckBlinkFlag, 1);
+			int16 ms = (int16)((int32)READ_UINT16(_huckGiftPic + 0x94) * 1000 / 60);
+			_huckBlinkTimer = _xp->startTimer(ms);
+		} else if (eventData == _huckShuffleTimer) {
 			giftSwap();
 			_xp->updateDisplay();
 			startHuckShuffleTimer();
@@ -756,11 +756,11 @@ void BoltEngine::handleEvent(int16 eventType, uint32 eventData) {
 	}
 
 	case etInactivity: {
-		if (!g_huckScreensaverTimer) {
-			int16 ms = (int16)((int32)READ_UINT16(g_huckGiftPic + 0x94) * 1000 / 60);
-			g_huckScreensaverTimer = _xp->startTimer(ms);
-			g_huckScreensaverFlag = 0;
-			huckToggleBlinking(&g_huckScreensaverFlag, 0);
+		if (!_huckScreensaverTimer) {
+			int16 ms = (int16)((int32)READ_UINT16(_huckGiftPic + 0x94) * 1000 / 60);
+			_huckScreensaverTimer = _xp->startTimer(ms);
+			_huckScreensaverFlag = 0;
+			huckToggleBlinking(&_huckScreensaverFlag, 0);
 			_xp->setInactivityTimer(1800);
 		} else {
 			bool exitLoop = false;
@@ -771,8 +771,8 @@ void BoltEngine::handleEvent(int16 eventType, uint32 eventData) {
 				int16 innerType = _xp->getEvent(etEmpty, &innerData);
 				switch (innerType) {
 				case etSound:
-					if (g_huckSoundPlaying > 0)
-						g_huckSoundPlaying--;
+					if (_huckSoundPlaying > 0)
+						_huckSoundPlaying--;
 
 					break;
 				default:
@@ -790,28 +790,28 @@ void BoltEngine::handleEvent(int16 eventType, uint32 eventData) {
 	}
 
 	case etSound: {
-		if (g_huckHotSpotCount != 0) {
-			g_huckHotSpotCount = maintainAudioPlay(1);
-			if (g_huckHotSpotCount == 0)
-				huckUpdateHotSpots(g_huckCursorX, g_huckCursorY);
+		if (_huckHotSpotCount != 0) {
+			_huckHotSpotCount = maintainAudioPlay(1);
+			if (_huckHotSpotCount == 0)
+				huckUpdateHotSpots(_huckCursorX, _huckCursorY);
 		}
 
-		if (g_huckSoundPlaying > 0)
-			g_huckSoundPlaying--;
+		if (_huckSoundPlaying > 0)
+			_huckSoundPlaying--;
 
 		break;
 	}
 
 	case etTrigger: {
-		g_huckActionState++;
-		if (g_huckActionState == 1) {
-			int16 ms = (int16)((int32)READ_UINT16(g_huckGiftPic + 0x94) * 1000 / 60);
-			g_huckBlinkTimer = _xp->startTimer(ms);
-			g_huckBlinkFlag = 0;
-			huckToggleBlinking(&g_huckBlinkFlag, 1);
-		} else if (g_huckActionState == 2) {
-			_xp->killTimer(g_huckBlinkTimer);
-			g_huckBlinkTimer = 0;
+		_huckActionState++;
+		if (_huckActionState == 1) {
+			int16 ms = (int16)((int32)READ_UINT16(_huckGiftPic + 0x94) * 1000 / 60);
+			_huckBlinkTimer = _xp->startTimer(ms);
+			_huckBlinkFlag = 0;
+			huckToggleBlinking(&_huckBlinkFlag, 1);
+		} else if (_huckActionState == 2) {
+			_xp->killTimer(_huckBlinkTimer);
+			_huckBlinkTimer = 0;
 			restoreHuckColors(1);
 		}
 
@@ -824,10 +824,10 @@ void BoltEngine::handleEvent(int16 eventType, uint32 eventData) {
 }
 
 void BoltEngine::playHuck() {
-	g_huckReturnBooth = 0x10;
+	_huckReturnBooth = 0x10;
 
 	while (!shouldQuit()) {
-		if (g_huckExitFlag)
+		if (_huckExitFlag)
 			break;
 
 		uint32 eventData = 0;
@@ -836,51 +836,51 @@ void BoltEngine::playHuck() {
 		if (eventType)
 			handleEvent(eventType, eventData);
 
-		if (g_huckHotSpotCount != 0) {
-			g_huckHotSpotCount = maintainAudioPlay(0);
-			if (g_huckHotSpotCount == 0)
-				huckUpdateHotSpots(g_huckCursorX, g_huckCursorY);
+		if (_huckHotSpotCount != 0) {
+			_huckHotSpotCount = maintainAudioPlay(0);
+			if (_huckHotSpotCount == 0)
+				huckUpdateHotSpots(_huckCursorX, _huckCursorY);
 		}
 	}
 
 	if (shouldQuit())
-		g_huckReturnBooth = 0;
+		_huckReturnBooth = 0;
 }
 
 void BoltEngine::cleanUpHuck() {
 	unloadHuckResources();
 
-	if (g_huckScratchPic.pixelData) {
-		_xp->freeMem(g_huckScratchPic.pixelData);
-		g_huckScratchPic.pixelData = nullptr;
-		g_huckScratchPic.palette = nullptr;
+	if (_huckScratchPic.pixelData) {
+		_xp->freeMem(_huckScratchPic.pixelData);
+		_huckScratchPic.pixelData = nullptr;
+		_huckScratchPic.palette = nullptr;
 	}
 
-	vSave(&g_huckGlobal, sizeof(g_huckGlobal), "HuckGlobal");
+	vSave(&_huckGlobal, sizeof(_huckGlobal), "HuckGlobal");
 
 	byte huckSaveBuf[0x86] = { 0 };
 	Common::MemoryWriteStream *huckStateWriteStream = new Common::MemoryWriteStream(huckSaveBuf, sizeof(huckSaveBuf));
 
-	huckStateWriteStream->writeSint16BE(g_huckState.levelNumber);
+	huckStateWriteStream->writeSint16BE(_huckState.levelNumber);
 
 	for (int i = 0; i < 10; i++)
-		huckStateWriteStream->writeSint16BE(g_huckState.slotIndex[i]);
+		huckStateWriteStream->writeSint16BE(_huckState.slotIndex[i]);
 
-	huckStateWriteStream->writeSint16BE(g_huckState.levelComplete);
+	huckStateWriteStream->writeSint16BE(_huckState.levelComplete);
 
 	for (int i = 0; i < 24; i++)
-		huckStateWriteStream->writeSint16BE(g_huckState.drawTable1[i]);
+		huckStateWriteStream->writeSint16BE(_huckState.drawTable1[i]);
 
 	for (int i = 0; i < 24; i++)
-		huckStateWriteStream->writeSint16BE(g_huckState.drawTable2[i]);
+		huckStateWriteStream->writeSint16BE(_huckState.drawTable2[i]);
 
-	huckStateWriteStream->writeSint16BE(g_huckState.giftCount);
-	huckStateWriteStream->writeSint16BE(g_huckState.hasCycle);
-	huckStateWriteStream->writeSint16BE(g_huckState.selectionPending);
-	huckStateWriteStream->writeSint16BE(g_huckState.selected1Slot);
-	huckStateWriteStream->writeSint16BE(g_huckState.selected2Slot);
-	huckStateWriteStream->writeSint16BE(g_huckState.selected1SpriteId);
-	huckStateWriteStream->writeSint16BE(g_huckState.selected2SpriteId);
+	huckStateWriteStream->writeSint16BE(_huckState.giftCount);
+	huckStateWriteStream->writeSint16BE(_huckState.hasCycle);
+	huckStateWriteStream->writeSint16BE(_huckState.selectionPending);
+	huckStateWriteStream->writeSint16BE(_huckState.selected1Slot);
+	huckStateWriteStream->writeSint16BE(_huckState.selected2Slot);
+	huckStateWriteStream->writeSint16BE(_huckState.selected1SpriteId);
+	huckStateWriteStream->writeSint16BE(_huckState.selected2SpriteId);
 
 	// Sanity check: should be exactly 0x86
 	assert(huckStateWriteStream->pos() == 0x86);
@@ -893,8 +893,8 @@ void BoltEngine::cleanUpHuck() {
 }
 
 int16 BoltEngine::huckGame(int16 prevBooth) {
-	if (!openBOLTLib(&g_huckBoltLib, &g_huckBoltCallbacks, assetPath("huck.blt")))
-		return g_huckReturnBooth;
+	if (!openBOLTLib(&_huckBoltLib, &_huckBoltCallbacks, assetPath("huck.blt")))
+		return _huckReturnBooth;
 
 	int16 savedTimer = _xp->setInactivityTimer(30);
 
@@ -904,23 +904,23 @@ int16 BoltEngine::huckGame(int16 prevBooth) {
 	cleanUpHuck();
 
 	_xp->setInactivityTimer(savedTimer);
-	closeBOLTLib(&g_huckBoltLib);
+	closeBOLTLib(&_huckBoltLib);
 
-	return g_huckReturnBooth;
+	return _huckReturnBooth;
 }
 
 void BoltEngine::swapHuckWordArray() {
-	byte *ptr = g_boltCurrentMemberEntry->dataPtr;
+	byte *ptr = _boltCurrentMemberEntry->dataPtr;
 	if (!ptr)
 		return;
 
-	int16 count = (int16)(g_boltCurrentMemberEntry->decompSize / 2);
+	int16 count = (int16)(_boltCurrentMemberEntry->decompSize / 2);
 	for (int16 i = 0; i < count; i++, ptr += 2)
 		WRITE_UINT16(ptr, READ_BE_UINT16(ptr));
 }
 
 void BoltEngine::swapHuckWords() {
-	byte *ptr = g_boltCurrentMemberEntry->dataPtr;
+	byte *ptr = _boltCurrentMemberEntry->dataPtr;
 	if (!ptr)
 		return;
 
diff --git a/engines/bolt/booths/scooby.cpp b/engines/bolt/booths/scooby.cpp
index 0908001c6c6..a6512a7dffc 100644
--- a/engines/bolt/booths/scooby.cpp
+++ b/engines/bolt/booths/scooby.cpp
@@ -26,15 +26,15 @@
 namespace Bolt {
 
 bool BoltEngine::loadScoobyBaseAssets() {
-	if (!getBOLTGroup(g_scoobyBoltLib, 0, 1))
+	if (!getBOLTGroup(_scoobyBoltLib, 0, 1))
 		return false;
 
-	g_scoobyBaseData = memberAddr(g_scoobyBoltLib, 0);
+	_scoobyBaseData = memberAddr(_scoobyBoltLib, 0);
 	return true;
 }
 
 void BoltEngine::cleanUpScoobyBaseAssets() {
-	freeBOLTGroup(g_scoobyBoltLib, 0, 1);
+	freeBOLTGroup(_scoobyBoltLib, 0, 1);
 }
 
 void BoltEngine::displayPicClipHack(byte *pic, int16 offsetX, int16 offsetY, int16 *clipRect, int16 displayMode) {
@@ -44,21 +44,21 @@ void BoltEngine::displayPicClipHack(byte *pic, int16 offsetX, int16 offsetY, int
 	int16 picWidth = READ_UINT16(pic + 0x0A);
 	byte *pixelData = getResolvedPtr(pic, 0x12);
 
-	int32 srcOffset = (clipRect[0] - g_displayX) + picWidth * (clipRect[1] - g_displayY);
+	int32 srcOffset = (clipRect[0] - _displayX) + picWidth * (clipRect[1] - _displayY);
 
-	_xp->blit(pixelData + srcOffset, picWidth, g_scoobyTempPic.pixelData, clipW, clipW, clipH);
+	_xp->blit(pixelData + srcOffset, picWidth, _scoobyTempPic.pixelData, clipW, clipW, clipH);
 
-	g_scoobyTempPic.width = clipW;
-	g_scoobyTempPic.height = clipH;
-	g_scoobyTempPic.palette = nullptr;
-	g_scoobyTempPic.paletteStart = 0;
-	g_scoobyTempPic.paletteCount = 0;
-	g_scoobyTempPic.flags = 0;
+	_scoobyTempPic.width = clipW;
+	_scoobyTempPic.height = clipH;
+	_scoobyTempPic.palette = nullptr;
+	_scoobyTempPic.paletteStart = 0;
+	_scoobyTempPic.paletteCount = 0;
+	_scoobyTempPic.flags = 0;
 
 	if (*pic & 2)
-		g_scoobyTempPic.flags |= 2;
+		_scoobyTempPic.flags |= 2;
 
-	_xp->displayPic(&g_scoobyTempPic,
+	_xp->displayPic(&_scoobyTempPic,
 					READ_UINT16(pic + 6) + offsetX + clipRect[0],
 					READ_UINT16(pic + 8) + offsetY + clipRect[1],
 					displayMode);
@@ -78,24 +78,24 @@ void BoltEngine::drawMovingWalls(int16 cellIdx, int16 direction, int16 picFrame,
 	if (bgFrame < 0)
 		bgFrame = 0;
 
-	int16 wallMarginX = READ_UINT16(g_scoobyLevelData + 0x0E);
-	int16 wallMarginY = READ_UINT16(g_scoobyLevelData + 0x10);
+	int16 wallMarginX = READ_UINT16(_scoobyLevelData + 0x0E);
+	int16 wallMarginY = READ_UINT16(_scoobyLevelData + 0x10);
 
 	int16 posX = 0, posY = 0;
 
 	// Compute position along wall's primary axis
 	switch (direction) {
 	case 0: // up wall
-		posY = g_scoobyCellBounds[cellIdx].top - 1 - wallMarginY;
+		posY = _scoobyCellBounds[cellIdx].top - 1 - wallMarginY;
 		break;
 	case 1: // right wall
-		posX = g_scoobyCellBounds[cellIdx].right - wallMarginX;
+		posX = _scoobyCellBounds[cellIdx].right - wallMarginX;
 		break;
 	case 2: // down wall
-		posY = g_scoobyCellBounds[cellIdx].bottom - wallMarginY;
+		posY = _scoobyCellBounds[cellIdx].bottom - wallMarginY;
 		break;
 	case 3: // left wall
-		posX = g_scoobyCellBounds[cellIdx].left - 1 - wallMarginX;
+		posX = _scoobyCellBounds[cellIdx].left - 1 - wallMarginX;
 		break;
 	default:
 		break;
@@ -106,9 +106,9 @@ void BoltEngine::drawMovingWalls(int16 cellIdx, int16 direction, int16 picFrame,
 	case 1: // right wall (vertical)
 	case 3: // left wall (vertical)
 	{
-		posY = g_scoobyCellBounds[cellIdx].top + wallMarginY + 1;
+		posY = _scoobyCellBounds[cellIdx].top + wallMarginY + 1;
 
-		byte *basePic = g_scoobyWallPicsA[3];
+		byte *basePic = _scoobyWallPicsA[3];
 		int16 picW = READ_UINT16(basePic + 0x0A);
 		int16 picH = READ_UINT16(basePic + 0x0C);
 
@@ -118,10 +118,10 @@ void BoltEngine::drawMovingWalls(int16 cellIdx, int16 direction, int16 picFrame,
 		clipRect[2] = picW;
 		clipRect[3] = picH;
 
-		displayPicClipHack(g_scoobyBgPic, 0, 0, clipRect, 1);
+		displayPicClipHack(_scoobyBgPic, 0, 0, clipRect, 1);
 
 		if (bgFrame != 0) {
-			displayPic(g_scoobyWallPicsA[bgFrame - 1], posX, posY, 1);
+			displayPic(_scoobyWallPicsA[bgFrame - 1], posX, posY, 1);
 		}
 
 		break;
@@ -130,9 +130,9 @@ void BoltEngine::drawMovingWalls(int16 cellIdx, int16 direction, int16 picFrame,
 	case 0: // up wall (horizontal)
 	case 2: // down wall (horizontal)
 	{
-		posX = g_scoobyCellBounds[cellIdx].left + wallMarginX + 1;
+		posX = _scoobyCellBounds[cellIdx].left + wallMarginX + 1;
 
-		byte *basePic = g_scoobyWallPicsB[4];
+		byte *basePic = _scoobyWallPicsB[4];
 		int16 picW = READ_UINT16(basePic + 0x0A);
 		int16 picH = READ_UINT16(basePic + 0x0C);
 
@@ -142,10 +142,10 @@ void BoltEngine::drawMovingWalls(int16 cellIdx, int16 direction, int16 picFrame,
 		clipRect[2] = picW;
 		clipRect[3] = picH;
 
-		displayPicClipHack(g_scoobyBgPic, 0, 0, clipRect, 1);
+		displayPicClipHack(_scoobyBgPic, 0, 0, clipRect, 1);
 
 		if (picFrame != 0) {
-			displayPic(g_scoobyWallPicsB[picFrame - 1], posX, posY, 1);
+			displayPic(_scoobyWallPicsB[picFrame - 1], posX, posY, 1);
 		}
 
 		break;
@@ -157,7 +157,7 @@ void BoltEngine::drawMovingWalls(int16 cellIdx, int16 direction, int16 picFrame,
 }
 
 void BoltEngine::drawAllMovingWalls() {
-	ScoobyState *state = &g_scoobyGameState;
+	ScoobyState *state = &_scoobyGameState;
 
 	// Phase 1: Draw open passages between adjacent cells (both sides state 2)
 	for (int16 i = 0; i < 25; i++) {
@@ -236,14 +236,14 @@ void BoltEngine::drawAllMovingWalls() {
 }
 
 void BoltEngine::animateTransition(int16 memberIdx) {
-	g_scoobyGameState.spriteFrameCount = 1;
+	_scoobyGameState.spriteFrameCount = 1;
 
-	g_scoobyGameState.frameData[0] = memberAddr(g_scoobyBoltLib, memberIdx - 1);
+	_scoobyGameState.frameData[0] = memberAddr(_scoobyBoltLib, memberIdx - 1);
 
-	setSSpriteFrames(&g_scoobySprite, g_scoobyGameState.spriteFrameCount, g_scoobyGameState.frameData, 1);
+	setSSpriteFrames(&_scoobySprite, _scoobyGameState.spriteFrameCount, _scoobyGameState.frameData, 1);
 
-	g_scoobyGameState.velocityX = 0;
-	g_scoobyGameState.velocityY = 0;
+	_scoobyGameState.velocityX = 0;
+	_scoobyGameState.velocityY = 0;
 }
 
 void BoltEngine::clearPictMSb(byte *pic) {
@@ -261,18 +261,18 @@ void BoltEngine::clearPictMSb(byte *pic) {
 
 void BoltEngine::initScoobyLevelGraphics() {
 	// Get palette from level data
-	byte *palette = memberAddr(g_scoobyBoltLib, READ_UINT16(g_scoobyLevelData));
+	byte *palette = memberAddr(_scoobyBoltLib, READ_UINT16(_scoobyLevelData));
 
 	// Get background pic based on display mode
 	int16 picMember;
-	if (g_displayMode != 0) {
-		picMember = READ_UINT16(g_scoobyLevelData + 4);
+	if (_displayMode != 0) {
+		picMember = READ_UINT16(_scoobyLevelData + 4);
 	} else {
-		picMember = READ_UINT16(g_scoobyLevelData + 2);
+		picMember = READ_UINT16(_scoobyLevelData + 2);
 	}
 
-	g_scoobyBgPic = memberAddr(g_scoobyBoltLib, picMember);
-	clearPictMSb(g_scoobyBgPic);
+	_scoobyBgPic = memberAddr(_scoobyBoltLib, picMember);
+	clearPictMSb(_scoobyBgPic);
 
 	// Drain timer events
 	uint32 dummy;
@@ -285,7 +285,7 @@ void BoltEngine::initScoobyLevelGraphics() {
 	displayColors(palette, stBack, 0);
 
 	// Display background to front surface
-	displayPic(g_scoobyBgPic, g_displayX, g_displayY, stFront);
+	displayPic(_scoobyBgPic, _displayX, _displayY, stFront);
 	_xp->updateDisplay();
 
 	_xp->setTransparency(true);
@@ -295,31 +295,31 @@ void BoltEngine::initScoobyLevelGraphics() {
 	displayColors(palette, stBack, 1);
 
 	// Display background to back surface
-	displayPic(g_scoobyBgPic, g_displayX, g_displayY, stBack);
+	displayPic(_scoobyBgPic, _displayX, _displayY, stBack);
 
 	drawAllMovingWalls();
 
 	// Setup Scooby sprite
-	g_scoobyGameState.spriteFrameCount = 1;
+	_scoobyGameState.spriteFrameCount = 1;
 
 	int16 spriteMember;
-	if (g_scoobyGameState.direction == 6) {
+	if (_scoobyGameState.direction == 6) {
 		spriteMember = 0x23;
 	} else {
 		spriteMember = 0x22;
 	}
 
-	g_scoobyGameState.frameData[0] = memberAddr(g_scoobyBoltLib, spriteMember);
+	_scoobyGameState.frameData[0] = memberAddr(_scoobyBoltLib, spriteMember);
 
-	setUpSSprite(&g_scoobySprite, g_scoobyGameState.spriteFrameCount, g_scoobyGameState.frameData, 1, 0, 0);
-	setSSpriteFrames(&g_scoobySprite, g_scoobyGameState.spriteFrameCount, g_scoobyGameState.frameData, 1);
-	setSSpriteVelocity(&g_scoobySprite, g_scoobyGameState.velocityX, g_scoobyGameState.velocityY);
-	displaySSprite(&g_scoobySprite, g_scoobyGameState.scoobyX, g_scoobyGameState.scoobyY);
-	animateSSprite(&g_scoobySprite, 0);
+	setUpSSprite(&_scoobySprite, _scoobyGameState.spriteFrameCount, _scoobyGameState.frameData, 1, 0, 0);
+	setSSpriteFrames(&_scoobySprite, _scoobyGameState.spriteFrameCount, _scoobyGameState.frameData, 1);
+	setSSpriteVelocity(&_scoobySprite, _scoobyGameState.velocityX, _scoobyGameState.velocityY);
+	displaySSprite(&_scoobySprite, _scoobyGameState.scoobyX, _scoobyGameState.scoobyY);
+	animateSSprite(&_scoobySprite, 0);
 
 	// Setup palette cycle
 	XPCycleState cycleData[4];
-	byte *cycleMember = memberAddr(g_scoobyBoltLib, READ_UINT16(g_scoobyLevelData + 0x0A));
+	byte *cycleMember = memberAddr(_scoobyBoltLib, READ_UINT16(_scoobyLevelData + 0x0A));
 	boltCycleToXPCycle(cycleMember, cycleData);
 	_xp->startCycle(cycleData);
 
@@ -330,59 +330,59 @@ bool BoltEngine::initScoobyLevelAssets() {
 	uint32 maxPicSize = 0;
 
 	// Compute graphics group index from level number
-	g_scoobySelectedGraphicsGroup = (g_scoobyGameState.levelNumber - 1) * 0x100 + 0x100;
+	_scoobySelectedGraphicsGroup = (_scoobyGameState.levelNumber - 1) * 0x100 + 0x100;
 
-	if (!getBOLTGroup(g_scoobyBoltLib, g_scoobySelectedGraphicsGroup, 1))
+	if (!getBOLTGroup(_scoobyBoltLib, _scoobySelectedGraphicsGroup, 1))
 		return false;
 
-	g_scoobyLevelData = memberAddr(g_scoobyBoltLib, g_scoobySelectedGraphicsGroup);
+	_scoobyLevelData = memberAddr(_scoobyBoltLib, _scoobySelectedGraphicsGroup);
 
 	// Load wall pic set A (4 pics at level data offset +6)
 	for (int16 i = 0; i < 4; i++) {
-		int16 memberIdx = READ_UINT16(g_scoobyLevelData + 6) + i;
-		g_scoobyWallPicsA[i] = memberAddr(g_scoobyBoltLib, memberIdx);
-		clearPictMSb(g_scoobyWallPicsA[i]);
+		int16 memberIdx = READ_UINT16(_scoobyLevelData + 6) + i;
+		_scoobyWallPicsA[i] = memberAddr(_scoobyBoltLib, memberIdx);
+		clearPictMSb(_scoobyWallPicsA[i]);
 
-		uint32 picSize = (uint32)READ_UINT16(g_scoobyWallPicsA[i] + 0x0A) *
-						 (uint32)READ_UINT16(g_scoobyWallPicsA[i] + 0x0C);
+		uint32 picSize = (uint32)READ_UINT16(_scoobyWallPicsA[i] + 0x0A) *
+						 (uint32)READ_UINT16(_scoobyWallPicsA[i] + 0x0C);
 		if (picSize > maxPicSize)
 			maxPicSize = picSize;
 	}
 
 	// Load wall pic set B (5 pics at level data offset +8)
 	for (int16 i = 0; i < 5; i++) {
-		int16 memberIdx = READ_UINT16(g_scoobyLevelData + 8) + i;
-		g_scoobyWallPicsB[i] = memberAddr(g_scoobyBoltLib, memberIdx);
-		clearPictMSb(g_scoobyWallPicsB[i]);
+		int16 memberIdx = READ_UINT16(_scoobyLevelData + 8) + i;
+		_scoobyWallPicsB[i] = memberAddr(_scoobyBoltLib, memberIdx);
+		clearPictMSb(_scoobyWallPicsB[i]);
 
-		uint32 picSize = (uint32)READ_UINT16(g_scoobyWallPicsB[i] + 0x0A) *
-						 (uint32)READ_UINT16(g_scoobyWallPicsB[i] + 0x0C);
+		uint32 picSize = (uint32)READ_UINT16(_scoobyWallPicsB[i] + 0x0A) *
+						 (uint32)READ_UINT16(_scoobyWallPicsB[i] + 0x0C);
 		if (picSize > maxPicSize)
 			maxPicSize = picSize;
 	}
 
 	// Allocate temp buffer for largest pic
-	g_scoobyTempPic.pixelData = (byte *)_xp->allocMem(maxPicSize);
-	if (!g_scoobyTempPic.pixelData)
+	_scoobyTempPic.pixelData = (byte *)_xp->allocMem(maxPicSize);
+	if (!_scoobyTempPic.pixelData)
 		return false;
 
 	// Determine effective level count (capped at 10)
-	if (g_scoobyGameState.levelNumber > 10) {
-		g_scoobyLevelCount = 10;
+	if (_scoobyGameState.levelNumber > 10) {
+		_scoobyLevelCount = 10;
 	} else {
-		g_scoobyLevelCount = g_scoobyGameState.levelNumber;
+		_scoobyLevelCount = _scoobyGameState.levelNumber;
 	}
 
 	// Look up difficulty from global save data
-	int16 levelIdx = g_scoobyLevelCount - 1;
+	int16 levelIdx = _scoobyLevelCount - 1;
 	int16 globalOffset = levelIdx * 6;
-	int16 saveSlot = g_scoobyGameState.slotIndex[levelIdx];
-	g_scoobyDifficulty = g_scoobyGlobalSaveData[globalOffset + saveSlot * 2];
+	int16 saveSlot = _scoobyGameState.slotIndex[levelIdx];
+	_scoobyDifficulty = _scoobyGlobalSaveData[globalOffset + saveSlot * 2];
 
 	// Load wall states for all 25 cells based on difficulty
 	for (int16 i = 0; i < 25; i++) {
 		int16 srcOffset;
-		switch (g_scoobyDifficulty) {
+		switch (_scoobyDifficulty) {
 		case 0:
 			srcOffset = 0x1C;
 			break;
@@ -397,29 +397,29 @@ bool BoltEngine::initScoobyLevelAssets() {
 		}
 
 		for (int16 j = 0; j < 4; j++) {
-			g_scoobyGameState.wallStates[i][j] =
-				READ_UINT16(g_scoobyLevelData + i * 8 + j * 2 + srcOffset);
+			_scoobyGameState.wallStates[i][j] =
+				READ_UINT16(_scoobyLevelData + i * 8 + j * 2 + srcOffset);
 		}
 	}
 
 	// Compute cell bounds and start positions for 5x5 grid
-	int16 cellW = READ_UINT16(g_scoobyLevelData + 0x16);
-	int16 cellH = READ_UINT16(g_scoobyLevelData + 0x18);
+	int16 cellW = READ_UINT16(_scoobyLevelData + 0x16);
+	int16 cellH = READ_UINT16(_scoobyLevelData + 0x18);
 
-	int16 yPos = READ_UINT16(g_scoobyLevelData + 0x14);
+	int16 yPos = READ_UINT16(_scoobyLevelData + 0x14);
 	for (int16 row = 0; row < 5; row++) {
-		int16 xPos = READ_UINT16(g_scoobyLevelData + 0x12);
+		int16 xPos = READ_UINT16(_scoobyLevelData + 0x12);
 
 		for (int16 col = 0; col < 5; col++) {
 			int16 cellIdx = row * 5 + col;
 
-			g_scoobyCellBounds[cellIdx].left = xPos;
-			g_scoobyCellBounds[cellIdx].right = xPos + cellW - 1;
-			g_scoobyCellBounds[cellIdx].top = yPos;
-			g_scoobyCellBounds[cellIdx].bottom = yPos + cellH - 1;
+			_scoobyCellBounds[cellIdx].left = xPos;
+			_scoobyCellBounds[cellIdx].right = xPos + cellW - 1;
+			_scoobyCellBounds[cellIdx].top = yPos;
+			_scoobyCellBounds[cellIdx].bottom = yPos + cellH - 1;
 
-			g_scoobyLevelStartXY[cellIdx].x = xPos + ((uint16)cellW >> 1);
-			g_scoobyLevelStartXY[cellIdx].y = yPos + ((uint16)cellH >> 1);
+			_scoobyLevelStartXY[cellIdx].x = xPos + ((uint16)cellW >> 1);
+			_scoobyLevelStartXY[cellIdx].y = yPos + ((uint16)cellH >> 1);
 
 			xPos += cellW;
 		}
@@ -432,23 +432,23 @@ bool BoltEngine::initScoobyLevelAssets() {
 
 void BoltEngine::cleanUpScoobyLevelGraphics() {
 	_xp->stopCycle();
-	freeBOLTGroup(g_scoobyBoltLib, g_scoobySelectedGraphicsGroup, 1);
+	freeBOLTGroup(_scoobyBoltLib, _scoobySelectedGraphicsGroup, 1);
 }
 
 void BoltEngine::setScoobySpriteDirection(int16 startMember) {
-	g_scoobyGameState.spriteFrameCount = 6;
+	_scoobyGameState.spriteFrameCount = 6;
 
 	int16 memberIdx = startMember;
 	for (int16 i = 0; i < 6; i++) {
-		g_scoobyGameState.frameData[i] = memberAddr(g_scoobyBoltLib, memberIdx + i);
+		_scoobyGameState.frameData[i] = memberAddr(_scoobyBoltLib, memberIdx + i);
 	}
 
-	setSSpriteFrames(&g_scoobySprite, g_scoobyGameState.spriteFrameCount, g_scoobyGameState.frameData, 1);
+	setSSpriteFrames(&_scoobySprite, _scoobyGameState.spriteFrameCount, _scoobyGameState.frameData, 1);
 }
 
 void BoltEngine::playSoundMapScooby(int16 memberIdx) {
-	byte *soundData = getBOLTMember(g_scoobyBoltLib, memberIdx);
-	uint32 soundSize = memberSize(g_scoobyBoltLib, memberIdx);
+	byte *soundData = getBOLTMember(_scoobyBoltLib, memberIdx);
+	uint32 soundSize = memberSize(_scoobyBoltLib, memberIdx);
 
 	if (soundData) {
 		_xp->playSound(soundData, soundSize, 22050);
@@ -456,42 +456,42 @@ void BoltEngine::playSoundMapScooby(int16 memberIdx) {
 }
 
 void BoltEngine::playWallSound() {
-	if (g_scoobySoundPlaying == 0)
+	if (_scoobySoundPlaying == 0)
 		return;
 
 	_xp->stopSound();
 
 	int16 soundMember;
-	switch (g_scoobySoundPlaying) {
+	switch (_scoobySoundPlaying) {
 	case 1:
-		soundMember = READ_UINT16(g_scoobyLevelData + 0x27C);
+		soundMember = READ_UINT16(_scoobyLevelData + 0x27C);
 		break;
 	case 2:
-		soundMember = READ_UINT16(g_scoobyLevelData + 0x27E);
+		soundMember = READ_UINT16(_scoobyLevelData + 0x27E);
 		break;
 	case 3:
-		soundMember = READ_UINT16(g_scoobyLevelData + 0x278);
+		soundMember = READ_UINT16(_scoobyLevelData + 0x278);
 		break;
 	case 4:
-		soundMember = READ_UINT16(g_scoobyLevelData + 0x27A);
+		soundMember = READ_UINT16(_scoobyLevelData + 0x27A);
 		break;
 	case 5:
-		soundMember = READ_UINT16(g_scoobyLevelData + 0x284);
+		soundMember = READ_UINT16(_scoobyLevelData + 0x284);
 		break;
 	case 6:
-		soundMember = READ_UINT16(g_scoobyLevelData + 0x286);
+		soundMember = READ_UINT16(_scoobyLevelData + 0x286);
 		break;
 	case 7:
-		soundMember = READ_UINT16(g_scoobyLevelData + 0x280);
+		soundMember = READ_UINT16(_scoobyLevelData + 0x280);
 		break;
 	case 8:
-		soundMember = READ_UINT16(g_scoobyLevelData + 0x282);
+		soundMember = READ_UINT16(_scoobyLevelData + 0x282);
 		break;
 	case 9:
-		soundMember = READ_UINT16(g_scoobyLevelData + 0x288);
+		soundMember = READ_UINT16(_scoobyLevelData + 0x288);
 		break;
 	case 10:
-		soundMember = READ_UINT16(g_scoobyLevelData + 0x28A);
+		soundMember = READ_UINT16(_scoobyLevelData + 0x28A);
 		break;
 	default:
 		return;
@@ -502,143 +502,143 @@ void BoltEngine::playWallSound() {
 
 void BoltEngine::animateWalls() {
 	// Phase 1: Animate current cell's walls
-	int16 curCell = g_scoobyGameState.scoobyCell;
+	int16 curCell = _scoobyGameState.scoobyCell;
 
 	for (int16 dir = 0; dir < 4; dir++) {
-		if (g_scoobyGameState.wallStates[curCell][dir] == 1) {
+		if (_scoobyGameState.wallStates[curCell][dir] == 1) {
 			// Closed wall, opening animation
 			bool shouldDraw = true;
 
 			switch (dir) {
 			case 0: // up neighbor
-				if (curCell - 5 == g_scoobyGameState.scoobySavedCell) {
-					if (g_scoobyGameState.wallStates[g_scoobyGameState.scoobySavedCell][2] == 1)
+				if (curCell - 5 == _scoobyGameState.scoobySavedCell) {
+					if (_scoobyGameState.wallStates[_scoobyGameState.scoobySavedCell][2] == 1)
 						shouldDraw = false;
 				}
 
 				if (shouldDraw)
-					g_scoobySoundPlaying = 5;
+					_scoobySoundPlaying = 5;
 
 				break;
 			case 1: // right neighbor
-				if (curCell + 1 == g_scoobyGameState.scoobySavedCell) {
-					if (g_scoobyGameState.wallStates[g_scoobyGameState.scoobySavedCell][3] == 1)
+				if (curCell + 1 == _scoobyGameState.scoobySavedCell) {
+					if (_scoobyGameState.wallStates[_scoobyGameState.scoobySavedCell][3] == 1)
 						shouldDraw = false;
 				}
 
 				if (shouldDraw)
-					g_scoobySoundPlaying = 1;
+					_scoobySoundPlaying = 1;
 
 				break;
 			case 2: // down neighbor
-				if (curCell + 5 == g_scoobyGameState.scoobySavedCell) {
-					if (g_scoobyGameState.wallStates[g_scoobyGameState.scoobySavedCell][0] == 1)
+				if (curCell + 5 == _scoobyGameState.scoobySavedCell) {
+					if (_scoobyGameState.wallStates[_scoobyGameState.scoobySavedCell][0] == 1)
 						shouldDraw = false;
 				}
 
 				if (shouldDraw)
-					g_scoobySoundPlaying = 5;
+					_scoobySoundPlaying = 5;
 
 				break;
 			case 3: // left neighbor
-				if (curCell - 1 == g_scoobyGameState.scoobySavedCell) {
-					if (g_scoobyGameState.wallStates[g_scoobyGameState.scoobySavedCell][1] == 1)
+				if (curCell - 1 == _scoobyGameState.scoobySavedCell) {
+					if (_scoobyGameState.wallStates[_scoobyGameState.scoobySavedCell][1] == 1)
 						shouldDraw = false;
 				}
 
 				if (shouldDraw)
-					g_scoobySoundPlaying = 1;
+					_scoobySoundPlaying = 1;
 
 				break;
 			}
 
 			if (shouldDraw) {
-				drawMovingWalls(curCell, dir, 5 - g_scoobyWallAnimStep, 4 - g_scoobyWallAnimStep);
+				drawMovingWalls(curCell, dir, 5 - _scoobyWallAnimStep, 4 - _scoobyWallAnimStep);
 			}
-		} else if (g_scoobyGameState.wallStates[curCell][dir] == 3) {
+		} else if (_scoobyGameState.wallStates[curCell][dir] == 3) {
 			// Dynamic wall, closing animation
 			if (dir == 3 || dir == 1) {
-				g_scoobySoundPlaying = 4;
+				_scoobySoundPlaying = 4;
 			} else {
-				g_scoobySoundPlaying = 8;
+				_scoobySoundPlaying = 8;
 			}
 
-			drawMovingWalls(curCell, dir, g_scoobyWallAnimStep, g_scoobyWallAnimStep);
+			drawMovingWalls(curCell, dir, _scoobyWallAnimStep, _scoobyWallAnimStep);
 		}
 	}
 
 	// Phase 2: Animate saved cell's walls
-	int16 savedCell = g_scoobyGameState.scoobySavedCell;
+	int16 savedCell = _scoobyGameState.scoobySavedCell;
 
 	for (int16 dir = 0; dir < 4; dir++) {
-		if (g_scoobyGameState.wallStates[savedCell][dir] == 1) {
+		if (_scoobyGameState.wallStates[savedCell][dir] == 1) {
 			// Closed wall in saved cell, closing animation
 			bool shouldDraw = true;
 
 			switch (dir) {
 			case 0: // up neighbor
-				if (savedCell - 5 == g_scoobyGameState.scoobyCell) {
-					if (g_scoobyGameState.wallStates[g_scoobyGameState.scoobyCell][2] == 1)
+				if (savedCell - 5 == _scoobyGameState.scoobyCell) {
+					if (_scoobyGameState.wallStates[_scoobyGameState.scoobyCell][2] == 1)
 						shouldDraw = false;
 				}
 
 				if (shouldDraw)
-					g_scoobySoundPlaying = 6;
+					_scoobySoundPlaying = 6;
 
 				break;
 			case 1: // right neighbor
-				if (savedCell + 1 == g_scoobyGameState.scoobyCell) {
-					if (g_scoobyGameState.wallStates[g_scoobyGameState.scoobyCell][3] == 1)
+				if (savedCell + 1 == _scoobyGameState.scoobyCell) {
+					if (_scoobyGameState.wallStates[_scoobyGameState.scoobyCell][3] == 1)
 						shouldDraw = false;
 				}
 
 				if (shouldDraw)
-					g_scoobySoundPlaying = 2;
+					_scoobySoundPlaying = 2;
 
 				break;
 			case 2: // down neighbor
-				if (savedCell + 5 == g_scoobyGameState.scoobyCell) {
-					if (g_scoobyGameState.wallStates[g_scoobyGameState.scoobyCell][0] == 1)
+				if (savedCell + 5 == _scoobyGameState.scoobyCell) {
+					if (_scoobyGameState.wallStates[_scoobyGameState.scoobyCell][0] == 1)
 						shouldDraw = false;
 				}
 
 				if (shouldDraw)
-					g_scoobySoundPlaying = 6;
+					_scoobySoundPlaying = 6;
 
 				break;
 			case 3: // left neighbor
-				if (savedCell - 1 == g_scoobyGameState.scoobyCell) {
-					if (g_scoobyGameState.wallStates[g_scoobyGameState.scoobyCell][1] == 1)
+				if (savedCell - 1 == _scoobyGameState.scoobyCell) {
+					if (_scoobyGameState.wallStates[_scoobyGameState.scoobyCell][1] == 1)
 						shouldDraw = false;
 				}
 
 				if (shouldDraw)
-					g_scoobySoundPlaying = 2;
+					_scoobySoundPlaying = 2;
 
 				break;
 			}
 
 			if (shouldDraw) {
-				drawMovingWalls(savedCell, dir, g_scoobyWallAnimStep, g_scoobyWallAnimStep);
+				drawMovingWalls(savedCell, dir, _scoobyWallAnimStep, _scoobyWallAnimStep);
 			}
-		} else if (g_scoobyGameState.wallStates[savedCell][dir] == 3) {
+		} else if (_scoobyGameState.wallStates[savedCell][dir] == 3) {
 			// Dynamic wall in saved cell, opening animation
 			if (dir == 3 || dir == 1) {
-				g_scoobySoundPlaying = 3;
+				_scoobySoundPlaying = 3;
 			} else {
-				g_scoobySoundPlaying = 7;
+				_scoobySoundPlaying = 7;
 			}
 
-			drawMovingWalls(savedCell, dir, 5 - g_scoobyWallAnimStep, 4 - g_scoobyWallAnimStep);
+			drawMovingWalls(savedCell, dir, 5 - _scoobyWallAnimStep, 4 - _scoobyWallAnimStep);
 		}
 	}
 
 	// Determine combined sound on first step
-	if (g_scoobyWallAnimStep == 1) {
-		if (g_scoobyWallsToClose == 0 && g_scoobyWallsToOpen > 1) {
-			g_scoobySoundPlaying = 9;
-		} else if (g_scoobyWallsToClose > 1 || (g_scoobyWallsToClose == 1 && g_scoobyWallsToOpen >= 1)) {
-			g_scoobySoundPlaying = 10;
+	if (_scoobyWallAnimStep == 1) {
+		if (_scoobyWallsToClose == 0 && _scoobyWallsToOpen > 1) {
+			_scoobySoundPlaying = 9;
+		} else if (_scoobyWallsToClose > 1 || (_scoobyWallsToClose == 1 && _scoobyWallsToOpen >= 1)) {
+			_scoobySoundPlaying = 10;
 		}
 
 		playWallSound();
@@ -646,8 +646,8 @@ void BoltEngine::animateWalls() {
 }
 
 void BoltEngine::decideDirection() {
-	int16 cell = g_scoobyGameState.scoobyCell;
-	int16 targetDir = g_scoobyGameState.transitionTarget;
+	int16 cell = _scoobyGameState.scoobyCell;
+	int16 targetDir = _scoobyGameState.transitionTarget;
 
 	int16 primaryPos, secondaryPos, primaryCenter, secondaryCenter;
 	int16 wallDir;
@@ -655,19 +655,19 @@ void BoltEngine::decideDirection() {
 	switch (targetDir) {
 	case 2: // right
 	case 6: // left
-		primaryPos = g_scoobyGameState.scoobyX;
-		secondaryPos = g_scoobyGameState.scoobyY;
-		primaryCenter = g_scoobyLevelStartXY[cell].x;
-		secondaryCenter = g_scoobyLevelStartXY[cell].y;
+		primaryPos = _scoobyGameState.scoobyX;
+		secondaryPos = _scoobyGameState.scoobyY;
+		primaryCenter = _scoobyLevelStartXY[cell].x;
+		secondaryCenter = _scoobyLevelStartXY[cell].y;
 		wallDir = (targetDir == 6) ? 3 : 1;
 
 		break;
 	case 0: // up
 	case 4: // down
-		primaryPos = g_scoobyGameState.scoobyY;
-		secondaryPos = g_scoobyGameState.scoobyX;
-		primaryCenter = g_scoobyLevelStartXY[cell].y;
-		secondaryCenter = g_scoobyLevelStartXY[cell].x;
+		primaryPos = _scoobyGameState.scoobyY;
+		secondaryPos = _scoobyGameState.scoobyX;
+		primaryCenter = _scoobyLevelStartXY[cell].y;
+		secondaryCenter = _scoobyLevelStartXY[cell].x;
 		wallDir = (targetDir == 0) ? 0 : 2;
 
 		break;
@@ -677,15 +677,15 @@ void BoltEngine::decideDirection() {
 
 	if (secondaryPos == secondaryCenter) {
 		// Aligned on secondary axis, can move in target direction
-		g_scoobyMoveRequested = 1;
+		_scoobyMoveRequested = 1;
 
 		if (primaryPos == primaryCenter) {
 			// At cell center, check wall
-			if (g_scoobyGameState.wallStates[cell][wallDir] == 2 ||
-				g_scoobyGameState.wallStates[cell][wallDir] == 3) {
+			if (_scoobyGameState.wallStates[cell][wallDir] == 2 ||
+				_scoobyGameState.wallStates[cell][wallDir] == 3) {
 				// Open passage or dynamic wall, stop
-				g_scoobyGameState.targetVelocityX = 0;
-				g_scoobyGameState.targetVelocityY = 0;
+				_scoobyGameState.targetVelocityX = 0;
+				_scoobyGameState.targetVelocityY = 0;
 				return;
 			}
 		}
@@ -693,56 +693,56 @@ void BoltEngine::decideDirection() {
 		// Wall closed, set velocity in target direction
 		switch (targetDir) {
 		case 0: // up
-			g_scoobyGameState.targetVelocityX = 0;
-			g_scoobyGameState.targetVelocityY = -3;
+			_scoobyGameState.targetVelocityX = 0;
+			_scoobyGameState.targetVelocityY = -3;
 			return;
 		case 2: // right
-			g_scoobyGameState.targetVelocityX = 3;
-			g_scoobyGameState.targetVelocityY = 0;
+			_scoobyGameState.targetVelocityX = 3;
+			_scoobyGameState.targetVelocityY = 0;
 			return;
 		case 4: // down
-			g_scoobyGameState.targetVelocityX = 0;
-			g_scoobyGameState.targetVelocityY = 3;
+			_scoobyGameState.targetVelocityX = 0;
+			_scoobyGameState.targetVelocityY = 3;
 			return;
 		case 6: // left
-			g_scoobyGameState.targetVelocityX = -3;
-			g_scoobyGameState.targetVelocityY = 0;
+			_scoobyGameState.targetVelocityX = -3;
+			_scoobyGameState.targetVelocityY = 0;
 			return;
 		default:
 			return;
 		}
 	} else {
 		// Not aligned on secondary axis, slide toward center
-		g_scoobyMoveRequested = 1;
+		_scoobyMoveRequested = 1;
 
-		if (g_scoobyGameState.wallStates[cell][wallDir] == 2) {
-			g_scoobyGameState.targetVelocityX = 0;
-			g_scoobyGameState.targetVelocityY = 0;
+		if (_scoobyGameState.wallStates[cell][wallDir] == 2) {
+			_scoobyGameState.targetVelocityX = 0;
+			_scoobyGameState.targetVelocityY = 0;
 			return;
 		}
 
 		switch (targetDir) {
 		case 2: // right, slide vertically
 		case 6: // left
-			g_scoobyGameState.targetVelocityX = 0;
-			if (g_scoobyLevelStartXY[cell].y < g_scoobyGameState.scoobyY) {
-				g_scoobyGameState.transitionTarget = 0;
-				g_scoobyGameState.targetVelocityY = -3;
+			_scoobyGameState.targetVelocityX = 0;
+			if (_scoobyLevelStartXY[cell].y < _scoobyGameState.scoobyY) {
+				_scoobyGameState.transitionTarget = 0;
+				_scoobyGameState.targetVelocityY = -3;
 			} else {
-				g_scoobyGameState.transitionTarget = 4;
-				g_scoobyGameState.targetVelocityY = 3;
+				_scoobyGameState.transitionTarget = 4;
+				_scoobyGameState.targetVelocityY = 3;
 			}
 
 			return;
 		case 0: // up, slide horizontally
 		case 4: // down
-			g_scoobyGameState.targetVelocityY = 0;
-			if (g_scoobyLevelStartXY[cell].x < g_scoobyGameState.scoobyX) {
-				g_scoobyGameState.transitionTarget = 6;
-				g_scoobyGameState.targetVelocityX = -3;
+			_scoobyGameState.targetVelocityY = 0;
+			if (_scoobyLevelStartXY[cell].x < _scoobyGameState.scoobyX) {
+				_scoobyGameState.transitionTarget = 6;
+				_scoobyGameState.targetVelocityX = -3;
 			} else {
-				g_scoobyGameState.transitionTarget = 2;
-				g_scoobyGameState.targetVelocityX = 3;
+				_scoobyGameState.transitionTarget = 2;
+				_scoobyGameState.targetVelocityX = 3;
 			}
 
 			return;
@@ -753,9 +753,9 @@ void BoltEngine::decideDirection() {
 }
 
 void BoltEngine::updateScoobySound() {
-	switch (g_scoobySoundMode) {
+	switch (_scoobySoundMode) {
 	case 0:
-		if (g_scoobySoundPlaying == 0) {
+		if (_scoobySoundPlaying == 0) {
 			_xp->stopSound();
 		}
 
@@ -772,149 +772,149 @@ void BoltEngine::updateScoobySound() {
 }
 
 void BoltEngine::setScoobySound(int16 mode) {
-	if (mode != g_scoobySoundMode) {
-		g_scoobySoundMode = mode;
+	if (mode != _scoobySoundMode) {
+		_scoobySoundMode = mode;
 		updateScoobySound();
 	}
 }
 
 void BoltEngine::updateScoobyLocation() {
 	Common::Point loc;
-	getSSpriteLoc(&g_scoobySprite, &loc);
-	g_scoobyGameState.scoobyX = loc.x;
-	g_scoobyGameState.scoobyY = loc.y;
+	getSSpriteLoc(&_scoobySprite, &loc);
+	_scoobyGameState.scoobyX = loc.x;
+	_scoobyGameState.scoobyY = loc.y;
 
 	// Find which cell Scooby is in
-	g_scoobyGameState.scoobyCell = -1;
+	_scoobyGameState.scoobyCell = -1;
 	for (int16 i = 0; i < 25; i++) {
-		if (g_scoobyCellBounds[i].left > g_scoobyGameState.scoobyX ||
-			g_scoobyCellBounds[i].right < g_scoobyGameState.scoobyX ||
-			g_scoobyCellBounds[i].top > g_scoobyGameState.scoobyY ||
-			g_scoobyCellBounds[i].bottom < g_scoobyGameState.scoobyY) {
+		if (_scoobyCellBounds[i].left > _scoobyGameState.scoobyX ||
+			_scoobyCellBounds[i].right < _scoobyGameState.scoobyX ||
+			_scoobyCellBounds[i].top > _scoobyGameState.scoobyY ||
+			_scoobyCellBounds[i].bottom < _scoobyGameState.scoobyY) {
 			continue;
 		}
 
 		// Found cell
-		g_scoobyGameState.scoobyCell = i;
+		_scoobyGameState.scoobyCell = i;
 
 		int16 col = i % 5;
-		g_scoobyGameState.leftNeighbor = (col == 0) ? -1 : i - 1;
-		g_scoobyGameState.rightNeighbor = (col == 4) ? -1 : i + 1;
-		g_scoobyGameState.upNeighbor = (i < 5) ? -1 : i - 5;
-		g_scoobyGameState.downNeighbor = (i > 19) ? -1 : i + 5;
+		_scoobyGameState.leftNeighbor = (col == 0) ? -1 : i - 1;
+		_scoobyGameState.rightNeighbor = (col == 4) ? -1 : i + 1;
+		_scoobyGameState.upNeighbor = (i < 5) ? -1 : i - 5;
+		_scoobyGameState.downNeighbor = (i > 19) ? -1 : i + 5;
 		break;
 	}
 
-	if (g_scoobyGameState.scoobyCell != -1)
+	if (_scoobyGameState.scoobyCell != -1)
 		return;
 
 	// Scooby is outside all cells, force movement back toward grid
-	int16 gridStartX = READ_UINT16(g_scoobyLevelData + 0x12);
-	int16 cellW = READ_UINT16(g_scoobyLevelData + 0x16);
-	int16 gridStartY = READ_UINT16(g_scoobyLevelData + 0x14);
-	int16 cellH = READ_UINT16(g_scoobyLevelData + 0x18);
-
-	if (gridStartX > g_scoobyGameState.scoobyX) {
-		g_scoobyGameState.velocityX = -3;
-		g_scoobyGameState.velocityY = 0;
+	int16 gridStartX = READ_UINT16(_scoobyLevelData + 0x12);
+	int16 cellW = READ_UINT16(_scoobyLevelData + 0x16);
+	int16 gridStartY = READ_UINT16(_scoobyLevelData + 0x14);
+	int16 cellH = READ_UINT16(_scoobyLevelData + 0x18);
+
+	if (gridStartX > _scoobyGameState.scoobyX) {
+		_scoobyGameState.velocityX = -3;
+		_scoobyGameState.velocityY = 0;
 		setScoobySpriteDirection(6);
-	} else if (gridStartX + cellW * 5 - 1 < g_scoobyGameState.scoobyX) {
-		g_scoobyGameState.velocityX = 3;
-		g_scoobyGameState.velocityY = 0;
+	} else if (gridStartX + cellW * 5 - 1 < _scoobyGameState.scoobyX) {
+		_scoobyGameState.velocityX = 3;
+		_scoobyGameState.velocityY = 0;
 		setScoobySpriteDirection(0);
-	} else if (gridStartY > g_scoobyGameState.scoobyY) {
-		g_scoobyGameState.velocityX = 0;
-		g_scoobyGameState.velocityY = -3;
+	} else if (gridStartY > _scoobyGameState.scoobyY) {
+		_scoobyGameState.velocityX = 0;
+		_scoobyGameState.velocityY = -3;
 		setScoobySpriteDirection(0x0C);
-	} else if (gridStartY + (cellH - 1) * 5 - 1 < g_scoobyGameState.scoobyY) {
-		g_scoobyGameState.velocityX = 0;
-		g_scoobyGameState.velocityY = 3;
+	} else if (gridStartY + (cellH - 1) * 5 - 1 < _scoobyGameState.scoobyY) {
+		_scoobyGameState.velocityX = 0;
+		_scoobyGameState.velocityY = 3;
 		setScoobySpriteDirection(0x12);
 	}
 
 	// Scroll Scooby off-screen
-	int16 halfW = READ_UINT16(g_scoobyBaseData + 0x0A) / 2;
+	int16 halfW = READ_UINT16(_scoobyBaseData + 0x0A) / 2;
 
 	while (!shouldQuit()) {
-		if (g_displayX - halfW >= g_scoobyGameState.scoobyX)
+		if (_displayX - halfW >= _scoobyGameState.scoobyX)
 			break;
 
-		if (g_displayX + g_displayWidth + halfW <= g_scoobyGameState.scoobyX)
+		if (_displayX + _displayWidth + halfW <= _scoobyGameState.scoobyX)
 			break;
 
-		int16 halfH = READ_UINT16(g_scoobyBaseData + 0x0C) / 2;
-		if (g_displayY - halfH >= g_scoobyGameState.scoobyY)
+		int16 halfH = READ_UINT16(_scoobyBaseData + 0x0C) / 2;
+		if (_displayY - halfH >= _scoobyGameState.scoobyY)
 			break;
 
-		if (g_displayY + g_displayHeight + halfH <= g_scoobyGameState.scoobyY)
+		if (_displayY + _displayHeight + halfH <= _scoobyGameState.scoobyY)
 			break;
 
 		// Still visible, animate one frame
-		setSSpriteVelocity(&g_scoobySprite, g_scoobyGameState.velocityX, g_scoobyGameState.velocityY);
-		animateSSprite(&g_scoobySprite, 0);
+		setSSpriteVelocity(&_scoobySprite, _scoobyGameState.velocityX, _scoobyGameState.velocityY);
+		animateSSprite(&_scoobySprite, 0);
 		_xp->updateDisplay();
 
 		uint32 dummy;
 		if (_xp->getEvent(etSound, &dummy) == etSound)
 			updateScoobySound();
 
-		getSSpriteLoc(&g_scoobySprite, &loc);
-		g_scoobyGameState.scoobyX = loc.x;
-		g_scoobyGameState.scoobyY = loc.y;
+		getSSpriteLoc(&_scoobySprite, &loc);
+		_scoobyGameState.scoobyX = loc.x;
+		_scoobyGameState.scoobyY = loc.y;
 	}
 
 	// Scooby left the screen, level complete
 	_xp->stopSound();
-	g_scoobyGameState.levelComplete = 1;
+	_scoobyGameState.levelComplete = 1;
 
-	g_scoobyGameState.levelNumber++;
-	if (g_scoobyGameState.levelNumber > 12)
-		g_scoobyGameState.levelNumber = 10;
+	_scoobyGameState.levelNumber++;
+	if (_scoobyGameState.levelNumber > 12)
+		_scoobyGameState.levelNumber = 10;
 
 	// Cycle slot for current level
-	int16 slotIdx = g_scoobyLevelCount - 1;
-	g_scoobyGameState.slotIndex[slotIdx]++;
-	if (g_scoobyGameState.slotIndex[slotIdx] == 3)
-		g_scoobyGameState.slotIndex[slotIdx] = 0;
+	int16 slotIdx = _scoobyLevelCount - 1;
+	_scoobyGameState.slotIndex[slotIdx]++;
+	if (_scoobyGameState.slotIndex[slotIdx] == 3)
+		_scoobyGameState.slotIndex[slotIdx] = 0;
 }
 
 void BoltEngine::updateScoobyWalls() {
-	if (g_scoobyWallAnimating != 0) {
-		g_scoobyWallAnimStep++;
-		if (g_scoobyWallAnimStep > 5) {
-			g_scoobyWallAnimating = 0;
-			g_scoobyWallAnimStep = 5;
+	if (_scoobyWallAnimating != 0) {
+		_scoobyWallAnimStep++;
+		if (_scoobyWallAnimStep > 5) {
+			_scoobyWallAnimating = 0;
+			_scoobyWallAnimStep = 5;
 		}
 
 		animateWalls();
 
-		if (g_scoobyWallAnimating == 0) {
-			g_scoobyGameState.scoobySavedCell = g_scoobyGameState.scoobyCell;
+		if (_scoobyWallAnimating == 0) {
+			_scoobyGameState.scoobySavedCell = _scoobyGameState.scoobyCell;
 		}
 
 		return;
 	}
 
 	// Not animating, check if Scooby is at cell center
-	int16 curCell = g_scoobyGameState.scoobyCell;
-	if (g_scoobyLevelStartXY[curCell].x != g_scoobyGameState.scoobyX)
+	int16 curCell = _scoobyGameState.scoobyCell;
+	if (_scoobyLevelStartXY[curCell].x != _scoobyGameState.scoobyX)
 		return;
 
-	if (g_scoobyLevelStartXY[curCell].y != g_scoobyGameState.scoobyY)
+	if (_scoobyLevelStartXY[curCell].y != _scoobyGameState.scoobyY)
 		return;
 
 	// At cell center, update active level
-	g_scoobyGameState.activeLevel = g_scoobyGameState.scoobyCell;
+	_scoobyGameState.activeLevel = _scoobyGameState.scoobyCell;
 
 	// If same cell as saved, no wall changes needed
-	if (g_scoobyGameState.scoobyCell == g_scoobyGameState.scoobySavedCell)
+	if (_scoobyGameState.scoobyCell == _scoobyGameState.scoobySavedCell)
 		return;
 
 	// Detect wall changes between current and saved cells
-	g_scoobyWallsToOpen = 0;
-	g_scoobyWallsToClose = 0;
+	_scoobyWallsToOpen = 0;
+	_scoobyWallsToClose = 0;
 
-	int16 savedCell = g_scoobyGameState.scoobySavedCell;
+	int16 savedCell = _scoobyGameState.scoobySavedCell;
 
 	int16 curUp = curCell - 5;
 	int16 curLeft = curCell - 1;
@@ -928,38 +928,38 @@ void BoltEngine::updateScoobyWalls() {
 
 	for (int16 dir = 0; dir <= 3; dir++) {
 		// Check current cell wall
-		if (g_scoobyGameState.wallStates[curCell][dir] == 3) {
-			g_scoobyWallAnimating = 1;
-			g_scoobyWallAnimStep = 1;
-			g_scoobyWallsToClose++;
-		} else if (g_scoobyGameState.wallStates[curCell][dir] == 1) {
-			g_scoobyWallAnimating = 1;
-			g_scoobyWallAnimStep = 1;
-			g_scoobyWallsToOpen++;
+		if (_scoobyGameState.wallStates[curCell][dir] == 3) {
+			_scoobyWallAnimating = 1;
+			_scoobyWallAnimStep = 1;
+			_scoobyWallsToClose++;
+		} else if (_scoobyGameState.wallStates[curCell][dir] == 1) {
+			_scoobyWallAnimating = 1;
+			_scoobyWallAnimStep = 1;
+			_scoobyWallsToOpen++;
 
 			bool cancelOpen = false;
 			switch (dir) {
 			case 0:
 				if (curUp == savedCell &&
-					g_scoobyGameState.wallStates[savedCell][2] == 1)
+					_scoobyGameState.wallStates[savedCell][2] == 1)
 					cancelOpen = true;
 
 				break;
 			case 1:
 				if (curRight == savedCell &&
-					g_scoobyGameState.wallStates[savedCell][3] == 1)
+					_scoobyGameState.wallStates[savedCell][3] == 1)
 					cancelOpen = true;
 
 				break;
 			case 2:
 				if (curDown == savedCell &&
-					g_scoobyGameState.wallStates[savedCell][0] == 1)
+					_scoobyGameState.wallStates[savedCell][0] == 1)
 					cancelOpen = true;
 
 				break;
 			case 3:
 				if (curLeft == savedCell &&
-					g_scoobyGameState.wallStates[savedCell][1] == 1)
+					_scoobyGameState.wallStates[savedCell][1] == 1)
 					cancelOpen = true;
 
 				break;
@@ -968,42 +968,42 @@ void BoltEngine::updateScoobyWalls() {
 			}
 
 			if (cancelOpen)
-				g_scoobyWallsToOpen--;
+				_scoobyWallsToOpen--;
 		}
 
 		// Check saved cell wall
-		if (g_scoobyGameState.wallStates[savedCell][dir] == 3) {
-			g_scoobyWallAnimating = 1;
-			g_scoobyWallAnimStep = 1;
-			g_scoobyWallsToOpen++;
-		} else if (g_scoobyGameState.wallStates[savedCell][dir] == 1) {
-			g_scoobyWallAnimating = 1;
-			g_scoobyWallAnimStep = 1;
-			g_scoobyWallsToClose++;
+		if (_scoobyGameState.wallStates[savedCell][dir] == 3) {
+			_scoobyWallAnimating = 1;
+			_scoobyWallAnimStep = 1;
+			_scoobyWallsToOpen++;
+		} else if (_scoobyGameState.wallStates[savedCell][dir] == 1) {
+			_scoobyWallAnimating = 1;
+			_scoobyWallAnimStep = 1;
+			_scoobyWallsToClose++;
 
 			bool cancelClose = false;
 			switch (dir) {
 			case 0:
 				if (savedUp == curCell &&
-					g_scoobyGameState.wallStates[curCell][2] == 1)
+					_scoobyGameState.wallStates[curCell][2] == 1)
 					cancelClose = true;
 
 				break;
 			case 1:
 				if (savedRight == curCell &&
-					g_scoobyGameState.wallStates[curCell][3] == 1)
+					_scoobyGameState.wallStates[curCell][3] == 1)
 					cancelClose = true;
 
 				break;
 			case 2:
 				if (savedDown == curCell &&
-					g_scoobyGameState.wallStates[curCell][0] == 1)
+					_scoobyGameState.wallStates[curCell][0] == 1)
 					cancelClose = true;
 
 				break;
 			case 3:
 				if (savedLeft == curCell &&
-					g_scoobyGameState.wallStates[curCell][1] == 1)
+					_scoobyGameState.wallStates[curCell][1] == 1)
 					cancelClose = true;
 
 				break;
@@ -1012,79 +1012,79 @@ void BoltEngine::updateScoobyWalls() {
 			}
 
 			if (cancelClose)
-				g_scoobyWallsToClose--;
+				_scoobyWallsToClose--;
 		}
 	}
 
-	if (g_scoobyWallAnimating != 0) {
+	if (_scoobyWallAnimating != 0) {
 		animateWalls();
 	}
 }
 
 void BoltEngine::updateScoobyDirection(int16 inputDir) {
-	int16 cell = g_scoobyGameState.scoobyCell;
+	int16 cell = _scoobyGameState.scoobyCell;
 
 	// If at cell center, wall passable, and no transition active then accept immediately
-	if (g_scoobyLevelStartXY[cell].x == g_scoobyGameState.scoobyX &&
-		g_scoobyLevelStartXY[cell].y == g_scoobyGameState.scoobyY &&
-		g_scoobyGameState.wallStates[cell][inputDir] != 2 &&
-		g_scoobyTransitioning == 0) {
-		g_scoobyDesiredDir = inputDir;
-		g_scoobyInputHoldCount = 3;
-		g_scoobyLastInputDir = inputDir; 
+	if (_scoobyLevelStartXY[cell].x == _scoobyGameState.scoobyX &&
+		_scoobyLevelStartXY[cell].y == _scoobyGameState.scoobyY &&
+		_scoobyGameState.wallStates[cell][inputDir] != 2 &&
+		_scoobyTransitioning == 0) {
+		_scoobyDesiredDir = inputDir;
+		_scoobyInputHoldCount = 3;
+		_scoobyLastInputDir = inputDir; 
 	} else {
-		if (g_scoobyLastInputDir == inputDir) {
-			g_scoobyInputHoldCount++;
-			if (g_scoobyInputHoldCount >= 3 && g_scoobyTransitioning == 0) {
-				g_scoobyDesiredDir = g_scoobyLastInputDir;
+		if (_scoobyLastInputDir == inputDir) {
+			_scoobyInputHoldCount++;
+			if (_scoobyInputHoldCount >= 3 && _scoobyTransitioning == 0) {
+				_scoobyDesiredDir = _scoobyLastInputDir;
 			}
 		} else {
-			g_scoobyInputHoldCount = 0;
-			g_scoobyLastInputDir = inputDir;
+			_scoobyInputHoldCount = 0;
+			_scoobyLastInputDir = inputDir;
 		}
 	}
 
 	// Resolve diagonal inputs into cardinal directions
 	int16 resolvedDir = 0;
 
-#define WALL_OPEN(c, d) (g_scoobyGameState.wallStates[c][d] == 2 || g_scoobyGameState.wallStates[c][d] == 3)
+#define WALL_OPEN(c, d) (_scoobyGameState.wallStates[c][d] == 2 || _scoobyGameState.wallStates[c][d] == 3)
 
-	switch (g_scoobyDesiredDir) {
+	switch (_scoobyDesiredDir) {
 	case 0:
 	case 2:
 	case 4:
 	case 6:
-		resolvedDir = g_scoobyDesiredDir;
+		resolvedDir = _scoobyDesiredDir;
 		break;
 	case 1: // up-right
 		if (WALL_OPEN(cell, 0)) {
-			resolvedDir = WALL_OPEN(cell, 1) ? (g_scoobyDesiredDir & 0xFE) : 2;
+			resolvedDir = WALL_OPEN(cell, 1) ? (_scoobyDesiredDir & 0xFE) : 2;
 		} else {
-			resolvedDir = WALL_OPEN(cell, 1) ? 0 : (g_scoobyDesiredDir & 0xFE);
+			resolvedDir = WALL_OPEN(cell, 1) ? 0 : (_scoobyDesiredDir & 0xFE);
 		}
 
 		break;
 	case 3: // down-right
 		if (WALL_OPEN(cell, 2)) {
-			resolvedDir = WALL_OPEN(cell, 1) ? (g_scoobyDesiredDir & 0xFE) : 2;
+			resolvedDir = WALL_OPEN(cell, 1) ? (_scoobyDesiredDir & 0xFE) : 2;
 		} else {
-			resolvedDir = WALL_OPEN(cell, 1) ? 4 : (g_scoobyDesiredDir & 0xFE);
+			resolvedDir = WALL_OPEN(cell, 1) ? 4 : (_scoobyDesiredDir & 0xFE);
 		}
 
 		break;
 	case 5: // down-left
 		if (WALL_OPEN(cell, 2)) {
-			resolvedDir = WALL_OPEN(cell, 3) ? (g_scoobyDesiredDir & 0xFE) : 6;
+			resolvedDir = WALL_OPEN(cell, 3) ? (_scoobyDesiredDir & 0xFE) : 6;
 		} else {
-			resolvedDir = WALL_OPEN(cell, 3) ? 4 : (g_scoobyDesiredDir & 0xFE);
+			resolvedDir = WALL_OPEN(cell, 3) ? 4 : (_scoobyDesiredDir & 0xFE);
 		}
 
 		break;
 	case 7: // up-left
 		if (WALL_OPEN(cell, 0)) {
-			resolvedDir = WALL_OPEN(cell, 3) ? (g_scoobyDesiredDir & 0xFE) : 6;
+			resolvedDir = WALL_OPEN(cell, 3) ? (_scoobyDesiredDir & 0xFE) : 6;
 		} else {
-			resolvedDir = WALL_OPEN(cell, 3) ? 0 : (g_scoobyDesiredDir & 0xFE);
+			resolvedDir = WALL_OPEN(cell, 3) ? 0 : (_scoobyDesiredDir & 0xFE);
 		}
 
 		break;
@@ -1094,117 +1094,117 @@ void BoltEngine::updateScoobyDirection(int16 inputDir) {
 
 #undef WALL_OPEN
 
-	g_scoobyMoveRequested = 0;
+	_scoobyMoveRequested = 0;
 
-	if (g_scoobyDesiredDir == -1) {
-		g_scoobyMoveRequested = 1;
+	if (_scoobyDesiredDir == -1) {
+		_scoobyMoveRequested = 1;
 
-		if (g_scoobyGameState.direction == 6) {
-			g_scoobyGameState.transitionTarget = -3;
+		if (_scoobyGameState.direction == 6) {
+			_scoobyGameState.transitionTarget = -3;
 		} else {
-			g_scoobyGameState.transitionTarget = -2;
+			_scoobyGameState.transitionTarget = -2;
 		}
 
-		g_scoobyGameState.targetVelocityX = 0;
-		g_scoobyGameState.targetVelocityY = 0;
+		_scoobyGameState.targetVelocityX = 0;
+		_scoobyGameState.targetVelocityY = 0;
 	} else {
-		g_scoobyGameState.transitionTarget = resolvedDir;
+		_scoobyGameState.transitionTarget = resolvedDir;
 		decideDirection();
 	}
 }
 
 void BoltEngine::updateScoobyTransition() {
-	if (g_scoobyTransitioning != 0) {
+	if (_scoobyTransitioning != 0) {
 		// Active transition in progress
-		switch (g_scoobyTransitionTarget) {
+		switch (_scoobyTransitionTarget) {
 		case -3:
 			animateTransition(0x24);
-			g_scoobyTransitioning = 0;
+			_scoobyTransitioning = 0;
 			break;
 		case -2:
 			animateTransition(0x23);
-			g_scoobyTransitioning = 0;
+			_scoobyTransitioning = 0;
 			break;
 		case 0:
 			setScoobySpriteDirection(0x0C);
-			g_scoobyTransitioning = 0;
+			_scoobyTransitioning = 0;
 			break;
 		case 2:
-			if (g_scoobyTransitionFrom == 4) {
+			if (_scoobyTransitionFrom == 4) {
 				animateTransition(0x1C);
-				g_scoobyTransitionFrom = 0;
+				_scoobyTransitionFrom = 0;
 			} else {
 				setScoobySpriteDirection(0x0);
-				g_scoobyTransitioning = 0;
+				_scoobyTransitioning = 0;
 			}
 			break;
 		case 4:
-			if (g_scoobyTransitionFrom == 0) {
+			if (_scoobyTransitionFrom == 0) {
 				animateTransition(0x19);
-				g_scoobyTransitionFrom = 4;
+				_scoobyTransitionFrom = 4;
 			} else {
 				setScoobySpriteDirection(0x12);
-				g_scoobyTransitioning = 0;
+				_scoobyTransitioning = 0;
 			}
 			break;
 		case 6:
 			setScoobySpriteDirection(6);
-			g_scoobyTransitioning = 0;
+			_scoobyTransitioning = 0;
 			break;
 		default:
 			break;
 		}
 
-		if (g_scoobyTransitioning == 0) {
+		if (_scoobyTransitioning == 0) {
 			// Transition complete, commit state
-			switch (g_scoobyTransitionTarget) {
+			switch (_scoobyTransitionTarget) {
 			case 0:
 			case 2:
 			case 4:
 			case 6:
-				g_scoobyGameState.velocityX = g_scoobyTransitionVelX;
-				g_scoobyGameState.velocityY = g_scoobyTransitionVelY;
+				_scoobyGameState.velocityX = _scoobyTransitionVelX;
+				_scoobyGameState.velocityY = _scoobyTransitionVelY;
 				break;
 			default:
 				break;
 			}
 
-			g_scoobyGameState.currentAnim = g_scoobyTransitionTarget;
+			_scoobyGameState.currentAnim = _scoobyTransitionTarget;
 
-			if (g_scoobyTransitionTarget == 6) {
-				g_scoobyGameState.direction = 6;
-			} else if (g_scoobyGameState.currentAnim == 2) {
-				g_scoobyGameState.direction = 2;
+			if (_scoobyTransitionTarget == 6) {
+				_scoobyGameState.direction = 6;
+			} else if (_scoobyGameState.currentAnim == 2) {
+				_scoobyGameState.direction = 2;
 			}
 		}
 	} else {
 		// No transition active, check if one should start
-		if (g_scoobyMoveRequested == 0)
+		if (_scoobyMoveRequested == 0)
 			goto epilogue;
 
-		g_scoobyMoveRequested = 0;
+		_scoobyMoveRequested = 0;
 
-		if (g_scoobyGameState.transitionTarget == g_scoobyGameState.currentAnim) {
+		if (_scoobyGameState.transitionTarget == _scoobyGameState.currentAnim) {
 			// Same direction, just update velocity
-			g_scoobyGameState.velocityX = g_scoobyGameState.targetVelocityX;
-			g_scoobyGameState.velocityY = g_scoobyGameState.targetVelocityY;
+			_scoobyGameState.velocityX = _scoobyGameState.targetVelocityX;
+			_scoobyGameState.velocityY = _scoobyGameState.targetVelocityY;
 			goto epilogue;
 		}
 
 		// Start new transition
-		g_scoobyTransitioning = 1;
-		g_scoobyTransitionTarget = g_scoobyGameState.transitionTarget;
-		g_scoobyTransitionFrom = g_scoobyGameState.currentAnim;
-		g_scoobyTransitionVelX = g_scoobyGameState.targetVelocityX;
-		g_scoobyTransitionVelY = g_scoobyGameState.targetVelocityY;
+		_scoobyTransitioning = 1;
+		_scoobyTransitionTarget = _scoobyGameState.transitionTarget;
+		_scoobyTransitionFrom = _scoobyGameState.currentAnim;
+		_scoobyTransitionVelX = _scoobyGameState.targetVelocityX;
+		_scoobyTransitionVelY = _scoobyGameState.targetVelocityY;
 
 		// Pick transition animation based on current -> target direction.
-		switch (g_scoobyGameState.currentAnim) {
+		switch (_scoobyGameState.currentAnim) {
 		case -3:
 		case -2:
-			switch (g_scoobyGameState.transitionTarget) {
+			switch (_scoobyGameState.transitionTarget) {
 			case 0:
-				if (g_scoobyGameState.currentAnim == -2)
+				if (_scoobyGameState.currentAnim == -2)
 					animateTransition(0x1B);
 				else
 					animateTransition(0x1C);
@@ -1213,7 +1213,7 @@ void BoltEngine::updateScoobyTransition() {
 				animateTransition(0x1D);
 				break;
 			case 4:
-				if (g_scoobyGameState.currentAnim == -2)
+				if (_scoobyGameState.currentAnim == -2)
 					animateTransition(0x19);
 				else
 					animateTransition(0x1A);
@@ -1227,7 +1227,7 @@ void BoltEngine::updateScoobyTransition() {
 			}
 			break;
 		case 0:
-			switch (g_scoobyGameState.transitionTarget) {
+			switch (_scoobyGameState.transitionTarget) {
 			case -3:
 				animateTransition(0x1C);
 				break;
@@ -1249,7 +1249,7 @@ void BoltEngine::updateScoobyTransition() {
 			break;
 
 		case 2:
-			switch (g_scoobyGameState.transitionTarget) {
+			switch (_scoobyGameState.transitionTarget) {
 			case -3:
 				animateTransition(0x1D);
 				break;
@@ -1271,7 +1271,7 @@ void BoltEngine::updateScoobyTransition() {
 			break;
 
 		case 4:
-			switch (g_scoobyGameState.transitionTarget) {
+			switch (_scoobyGameState.transitionTarget) {
 			case -3:
 				animateTransition(0x1A);
 				break;
@@ -1293,7 +1293,7 @@ void BoltEngine::updateScoobyTransition() {
 			break;
 
 		case 6:
-			switch (g_scoobyGameState.transitionTarget) {
+			switch (_scoobyGameState.transitionTarget) {
 			case -3:
 				animateTransition(0x1E);
 				break;
@@ -1322,13 +1322,13 @@ void BoltEngine::updateScoobyTransition() {
 	}
 
 epilogue:
-	setSSpriteVelocity(&g_scoobySprite, g_scoobyGameState.velocityX, g_scoobyGameState.velocityY);
-	animateSSprite(&g_scoobySprite, 0);
+	setSSpriteVelocity(&_scoobySprite, _scoobyGameState.velocityX, _scoobyGameState.velocityY);
+	animateSSprite(&_scoobySprite, 0);
 
 	int16 soundMode;
-	if (g_scoobyGameState.currentAnim == -2 || g_scoobyGameState.currentAnim == -3) {
+	if (_scoobyGameState.currentAnim == -2 || _scoobyGameState.currentAnim == -3) {
 		soundMode = 0;
-	} else if (g_scoobyGameState.velocityX == 0 && g_scoobyGameState.velocityY == 0 && g_scoobyTransitioning == 0) {
+	} else if (_scoobyGameState.velocityX == 0 && _scoobyGameState.velocityY == 0 && _scoobyTransitioning == 0) {
 		soundMode = 2;
 	} else {
 		soundMode = 1;
@@ -1343,47 +1343,47 @@ bool BoltEngine::initScoobyLevel() {
 
 	// Set current level based on difficulty
 	int16 level;
-	switch (g_scoobyDifficulty) {
+	switch (_scoobyDifficulty) {
 	case 0:
-		level = READ_UINT16(g_scoobyLevelData + 0x1A);
+		level = READ_UINT16(_scoobyLevelData + 0x1A);
 		break;
 	case 1:
-		level = READ_UINT16(g_scoobyLevelData + 0xE4);
+		level = READ_UINT16(_scoobyLevelData + 0xE4);
 		break;
 	case 2:
-		level = READ_UINT16(g_scoobyLevelData + 0x1AE);
+		level = READ_UINT16(_scoobyLevelData + 0x1AE);
 		break;
 	default:
 		goto skipLevelSet;
 	}
 
-	g_scoobyGameState.scoobyCell = level;
-	g_scoobyGameState.scoobySavedCell = level;
+	_scoobyGameState.scoobyCell = level;
+	_scoobyGameState.scoobySavedCell = level;
 
 skipLevelSet:
-	g_scoobyGameState.activeLevel = g_scoobyGameState.scoobyCell;
+	_scoobyGameState.activeLevel = _scoobyGameState.scoobyCell;
 
 	// Compute neighbor levels in a 5-wide grid
-	int16 col = g_scoobyGameState.scoobyCell % 5;
+	int16 col = _scoobyGameState.scoobyCell % 5;
 
-	g_scoobyGameState.leftNeighbor = (col == 0) ? -1 : g_scoobyGameState.scoobyCell - 1;
-	g_scoobyGameState.rightNeighbor = (col == 4) ? -1 : g_scoobyGameState.scoobyCell + 1;
-	g_scoobyGameState.upNeighbor = (g_scoobyGameState.scoobyCell < 5) ? -1 : g_scoobyGameState.scoobyCell - 5;
-	g_scoobyGameState.downNeighbor = (g_scoobyGameState.scoobyCell > 19) ? -1 : g_scoobyGameState.scoobyCell + 5;
+	_scoobyGameState.leftNeighbor = (col == 0) ? -1 : _scoobyGameState.scoobyCell - 1;
+	_scoobyGameState.rightNeighbor = (col == 4) ? -1 : _scoobyGameState.scoobyCell + 1;
+	_scoobyGameState.upNeighbor = (_scoobyGameState.scoobyCell < 5) ? -1 : _scoobyGameState.scoobyCell - 5;
+	_scoobyGameState.downNeighbor = (_scoobyGameState.scoobyCell > 19) ? -1 : _scoobyGameState.scoobyCell + 5;
 
-	g_scoobyGameState.levelComplete = 0;
+	_scoobyGameState.levelComplete = 0;
 
 	// Starting position from level table
-	g_scoobyGameState.scoobyX = g_scoobyLevelStartXY[g_scoobyGameState.activeLevel].x;
-	g_scoobyGameState.scoobyY = g_scoobyLevelStartXY[g_scoobyGameState.activeLevel].y;
+	_scoobyGameState.scoobyX = _scoobyLevelStartXY[_scoobyGameState.activeLevel].x;
+	_scoobyGameState.scoobyY = _scoobyLevelStartXY[_scoobyGameState.activeLevel].y;
 
-	g_scoobyGameState.velocityX = 0;
-	g_scoobyGameState.velocityY = 0;
-	g_scoobyGameState.targetVelocityX = 0;
-	g_scoobyGameState.targetVelocityY = 0;
-	g_scoobyGameState.currentAnim = -2;
-	g_scoobyGameState.transitionTarget = -2;
-	g_scoobyGameState.direction = 2;
+	_scoobyGameState.velocityX = 0;
+	_scoobyGameState.velocityY = 0;
+	_scoobyGameState.targetVelocityX = 0;
+	_scoobyGameState.targetVelocityY = 0;
+	_scoobyGameState.currentAnim = -2;
+	_scoobyGameState.transitionTarget = -2;
+	_scoobyGameState.direction = 2;
 
 	initScoobyLevelGraphics();
 	return true;
@@ -1394,19 +1394,19 @@ bool BoltEngine::resumeScoobyLevel() {
 		return false;
 
 	// Restore level from saved level
-	g_scoobyGameState.scoobyCell = g_scoobyGameState.scoobySavedCell;
+	_scoobyGameState.scoobyCell = _scoobyGameState.scoobySavedCell;
 
 	// Compute neighbor levels
-	int16 col = g_scoobyGameState.scoobyCell % 5;
+	int16 col = _scoobyGameState.scoobyCell % 5;
 
-	g_scoobyGameState.leftNeighbor = (col == 0) ? -1 : g_scoobyGameState.scoobyCell - 1;
-	g_scoobyGameState.rightNeighbor = (col == 4) ? -1 : g_scoobyGameState.scoobyCell + 1;
-	g_scoobyGameState.upNeighbor = (g_scoobyGameState.scoobyCell < 5) ? -1 : g_scoobyGameState.scoobyCell - 5;
-	g_scoobyGameState.downNeighbor = (g_scoobyGameState.scoobyCell > 19) ? -1 : g_scoobyGameState.scoobyCell + 5;
+	_scoobyGameState.leftNeighbor = (col == 0) ? -1 : _scoobyGameState.scoobyCell - 1;
+	_scoobyGameState.rightNeighbor = (col == 4) ? -1 : _scoobyGameState.scoobyCell + 1;
+	_scoobyGameState.upNeighbor = (_scoobyGameState.scoobyCell < 5) ? -1 : _scoobyGameState.scoobyCell - 5;
+	_scoobyGameState.downNeighbor = (_scoobyGameState.scoobyCell > 19) ? -1 : _scoobyGameState.scoobyCell + 5;
 
 	// Starting position from level table
-	g_scoobyGameState.scoobyX = g_scoobyLevelStartXY[g_scoobyGameState.activeLevel].x;
-	g_scoobyGameState.scoobyY = g_scoobyLevelStartXY[g_scoobyGameState.activeLevel].y;
+	_scoobyGameState.scoobyX = _scoobyLevelStartXY[_scoobyGameState.activeLevel].x;
+	_scoobyGameState.scoobyY = _scoobyLevelStartXY[_scoobyGameState.activeLevel].y;
 
 	initScoobyLevelGraphics();
 	return true;
@@ -1415,87 +1415,87 @@ bool BoltEngine::resumeScoobyLevel() {
 bool BoltEngine::initScooby() {
 	_xp->randomize();
 
-	g_scoobyMoveRequested = 0;
-	g_scoobyTransitioning = 0;
-	g_scoobyWallAnimating = 0;
-	g_scoobyDesiredDir = -1;
-	g_scoobyInputHoldCount = 0;
+	_scoobyMoveRequested = 0;
+	_scoobyTransitioning = 0;
+	_scoobyWallAnimating = 0;
+	_scoobyDesiredDir = -1;
+	_scoobyInputHoldCount = 0;
 
 	if (!loadScoobyBaseAssets())
 		return false;
 
-	if (!vLoad(&g_scoobyGlobalSaveData, "ScoobyGlobal")) {
+	if (!vLoad(&_scoobyGlobalSaveData, "ScoobyGlobal")) {
 		// Initialize global save data: 10 levels, 3 slots each
 		for (int16 level = 0; level < 10; level++) {
-			g_scoobyGameState.slotIndex[level] = 0;
+			_scoobyGameState.slotIndex[level] = 0;
 
 			int16 baseOff = level * 6;
 			for (int16 j = 0; j < 3; j++) {
-				g_scoobyGlobalSaveData[baseOff + j * 2] = 3;
+				_scoobyGlobalSaveData[baseOff + j * 2] = 3;
 			}
 
 			// Randomly assign difficulty values 0, 1, 2 to the 3 slots
 			for (int16 j = 0; j < 3; j++) {
 				int16 slot = _xp->getRandom(3);
 
-				while (g_scoobyGlobalSaveData[baseOff + slot * 2] != 3) {
+				while (_scoobyGlobalSaveData[baseOff + slot * 2] != 3) {
 					slot++;
 					if (slot >= 3)
 						slot = 0;
 				}
 
-				g_scoobyGlobalSaveData[baseOff + slot * 2] = j;
+				_scoobyGlobalSaveData[baseOff + slot * 2] = j;
 			}
 		}
 	}
 
 	byte scoobyStateBuf[0x11A];
 	if (!vLoad(scoobyStateBuf, "Scooby")) {
-		g_scoobyGameState.levelNumber = 1;
+		_scoobyGameState.levelNumber = 1;
 
 		if (!initScoobyLevel())
 			return false;
 	} else {
 		Common::SeekableReadStream *scoobyStateReadStream = new Common::MemoryReadStream(scoobyStateBuf, sizeof(scoobyStateBuf), DisposeAfterUse::NO);
 
-		g_scoobyGameState.levelNumber = scoobyStateReadStream->readSint16BE(); // +0x00
+		_scoobyGameState.levelNumber = scoobyStateReadStream->readSint16BE(); // +0x00
 
 		for (int i = 0; i < 10; i++) // +0x02
-			g_scoobyGameState.slotIndex[i] = scoobyStateReadStream->readSint16BE();
+			_scoobyGameState.slotIndex[i] = scoobyStateReadStream->readSint16BE();
 
-		g_scoobyGameState.levelComplete = scoobyStateReadStream->readSint16BE(); // +0x16
+		_scoobyGameState.levelComplete = scoobyStateReadStream->readSint16BE(); // +0x16
 
 		for (int i = 0; i < 25; i++) // +0x18
 			for (int j = 0; j < 4; j++)
-				g_scoobyGameState.wallStates[i][j] = scoobyStateReadStream->readSint16BE();
-
-		g_scoobyGameState.scoobyCell = scoobyStateReadStream->readSint16BE();       // +0xE0
-		g_scoobyGameState.scoobySavedCell = scoobyStateReadStream->readSint16BE();  // +0xE2
-		g_scoobyGameState.leftNeighbor = scoobyStateReadStream->readSint16BE();     // +0xE4
-		g_scoobyGameState.rightNeighbor = scoobyStateReadStream->readSint16BE();    // +0xE6
-		g_scoobyGameState.upNeighbor = scoobyStateReadStream->readSint16BE();       // +0xE8
-		g_scoobyGameState.downNeighbor = scoobyStateReadStream->readSint16BE();     // +0xEA
-		g_scoobyGameState.activeLevel = scoobyStateReadStream->readSint16BE();      // +0xEC
-		g_scoobyGameState.scoobyX = scoobyStateReadStream->readSint16BE();          // +0xEE
-		g_scoobyGameState.scoobyY = scoobyStateReadStream->readSint16BE();          // +0xF0
-		g_scoobyGameState.velocityX = scoobyStateReadStream->readSint16BE();        // +0xF2
-		g_scoobyGameState.velocityY = scoobyStateReadStream->readSint16BE();        // +0xF4
-		g_scoobyGameState.targetVelocityX = scoobyStateReadStream->readSint16BE();  // +0xF6
-		g_scoobyGameState.targetVelocityY = scoobyStateReadStream->readSint16BE();  // +0xF8
-		g_scoobyGameState.transitionTarget = scoobyStateReadStream->readSint16BE(); // +0xFA
-		g_scoobyGameState.currentAnim = scoobyStateReadStream->readSint16BE();      // +0xFC
-		g_scoobyGameState.direction = scoobyStateReadStream->readSint16BE();        // +0xFE
-		g_scoobyGameState.spriteFrameCount = scoobyStateReadStream->readSint16BE(); // +0x100
+				_scoobyGameState.wallStates[i][j] = scoobyStateReadStream->readSint16BE();
+
+		_scoobyGameState.scoobyCell = scoobyStateReadStream->readSint16BE();       // +0xE0
+		_scoobyGameState.scoobySavedCell = scoobyStateReadStream->readSint16BE();  // +0xE2
+		_scoobyGameState.leftNeighbor = scoobyStateReadStream->readSint16BE();     // +0xE4
+		_scoobyGameState.rightNeighbor = scoobyStateReadStream->readSint16BE();    // +0xE6
+		_scoobyGameState.upNeighbor = scoobyStateReadStream->readSint16BE();       // +0xE8
+		_scoobyGameState.downNeighbor = scoobyStateReadStream->readSint16BE();     // +0xEA
+		_scoobyGameState.activeLevel = scoobyStateReadStream->readSint16BE();      // +0xEC
+		_scoobyGameState.scoobyX = scoobyStateReadStream->readSint16BE();          // +0xEE
+		_scoobyGameState.scoobyY = scoobyStateReadStream->readSint16BE();          // +0xF0
+		_scoobyGameState.velocityX = scoobyStateReadStream->readSint16BE();        // +0xF2
+		_scoobyGameState.velocityY = scoobyStateReadStream->readSint16BE();        // +0xF4
+		_scoobyGameState.targetVelocityX = scoobyStateReadStream->readSint16BE();  // +0xF6
+		_scoobyGameState.targetVelocityY = scoobyStateReadStream->readSint16BE();  // +0xF8
+		_scoobyGameState.transitionTarget = scoobyStateReadStream->readSint16BE(); // +0xFA
+		_scoobyGameState.currentAnim = scoobyStateReadStream->readSint16BE();      // +0xFC
+		_scoobyGameState.direction = scoobyStateReadStream->readSint16BE();        // +0xFE
+		_scoobyGameState.spriteFrameCount = scoobyStateReadStream->readSint16BE(); // +0x100
 
 		for (int i = 0; i < 6; i++) { // +0x102
 			scoobyStateReadStream->readUint32BE(); // dummy values
-			g_scoobyGameState.frameData[i] = nullptr;
+			_scoobyGameState.frameData[i] = nullptr;
 		}
 
 		assert(scoobyStateReadStream->pos() == 0x11A);
 		delete scoobyStateReadStream;
 
-		if (g_scoobyGameState.levelComplete != 0) {
+		if (_scoobyGameState.levelComplete != 0) {
 			if (!initScoobyLevel())
 				return false;
 		} else {
@@ -1509,40 +1509,40 @@ bool BoltEngine::initScooby() {
 }
 
 void BoltEngine::cleanUpScooby() {
-	vSave(&g_scoobyGlobalSaveData, 0x3C, "ScoobyGlobal");
+	vSave(&_scoobyGlobalSaveData, 0x3C, "ScoobyGlobal");
 
 	// Serialize the game state into a flat 0x11A-byte BE buffer
 	byte scoobyStateBuf[0x11A] = {0};
 	Common::MemoryWriteStream *scoobyStateWriteStream = new Common::MemoryWriteStream(scoobyStateBuf, sizeof(scoobyStateBuf));
 
-	scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.levelNumber); // +0x00
+	scoobyStateWriteStream->writeSint16BE(_scoobyGameState.levelNumber); // +0x00
 
 	for (int i = 0; i < 10; i++) // +0x02
-		scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.slotIndex[i]);
+		scoobyStateWriteStream->writeSint16BE(_scoobyGameState.slotIndex[i]);
 
-	scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.levelComplete); // +0x16
+	scoobyStateWriteStream->writeSint16BE(_scoobyGameState.levelComplete); // +0x16
 
 	for (int i = 0; i < 25; i++) // +0x18
 		for (int j = 0; j < 4; j++)
-			scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.wallStates[i][j]);
-
-	scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.scoobyCell);       // +0xE0
-	scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.scoobySavedCell);  // +0xE2
-	scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.leftNeighbor);     // +0xE4
-	scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.rightNeighbor);    // +0xE6
-	scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.upNeighbor);       // +0xE8
-	scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.downNeighbor);     // +0xEA
-	scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.activeLevel);      // +0xEC
-	scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.scoobyX);          // +0xEE
-	scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.scoobyY);          // +0xF0
-	scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.velocityX);        // +0xF2
-	scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.velocityY);        // +0xF4
-	scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.targetVelocityX);  // +0xF6
-	scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.targetVelocityY);  // +0xF8
-	scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.transitionTarget); // +0xFA
-	scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.currentAnim);      // +0xFC
-	scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.direction);        // +0xFE
-	scoobyStateWriteStream->writeSint16BE(g_scoobyGameState.spriteFrameCount); // +0x100
+			scoobyStateWriteStream->writeSint16BE(_scoobyGameState.wallStates[i][j]);
+
+	scoobyStateWriteStream->writeSint16BE(_scoobyGameState.scoobyCell);       // +0xE0
+	scoobyStateWriteStream->writeSint16BE(_scoobyGameState.scoobySavedCell);  // +0xE2
+	scoobyStateWriteStream->writeSint16BE(_scoobyGameState.leftNeighbor);     // +0xE4
+	scoobyStateWriteStream->writeSint16BE(_scoobyGameState.rightNeighbor);    // +0xE6
+	scoobyStateWriteStream->writeSint16BE(_scoobyGameState.upNeighbor);       // +0xE8
+	scoobyStateWriteStream->writeSint16BE(_scoobyGameState.downNeighbor);     // +0xEA
+	scoobyStateWriteStream->writeSint16BE(_scoobyGameState.activeLevel);      // +0xEC
+	scoobyStateWriteStream->writeSint16BE(_scoobyGameState.scoobyX);          // +0xEE
+	scoobyStateWriteStream->writeSint16BE(_scoobyGameState.scoobyY);          // +0xF0
+	scoobyStateWriteStream->writeSint16BE(_scoobyGameState.velocityX);        // +0xF2
+	scoobyStateWriteStream->writeSint16BE(_scoobyGameState.velocityY);        // +0xF4
+	scoobyStateWriteStream->writeSint16BE(_scoobyGameState.targetVelocityX);  // +0xF6
+	scoobyStateWriteStream->writeSint16BE(_scoobyGameState.targetVelocityY);  // +0xF8
+	scoobyStateWriteStream->writeSint16BE(_scoobyGameState.transitionTarget); // +0xFA
+	scoobyStateWriteStream->writeSint16BE(_scoobyGameState.currentAnim);      // +0xFC
+	scoobyStateWriteStream->writeSint16BE(_scoobyGameState.direction);        // +0xFE
+	scoobyStateWriteStream->writeSint16BE(_scoobyGameState.spriteFrameCount); // +0x100
 
 	for (int i = 0; i < 6; i++) // +0x102
 		scoobyStateWriteStream->writeUint32BE(0); // pointers saved as zero
@@ -1555,11 +1555,11 @@ void BoltEngine::cleanUpScooby() {
 	cleanUpScoobyLevelGraphics();
 	cleanUpScoobyBaseAssets();
 
-	if (g_scoobyTempPic.pixelData) {
-		_xp->freeMem(g_scoobyTempPic.pixelData);
-		g_scoobyTempPic.pixelData = nullptr;
-		g_scoobyTempPic.width = 0;
-		g_scoobyTempPic.height = 0;
+	if (_scoobyTempPic.pixelData) {
+		_xp->freeMem(_scoobyTempPic.pixelData);
+		_scoobyTempPic.pixelData = nullptr;
+		_scoobyTempPic.width = 0;
+		_scoobyTempPic.height = 0;
 	}
 
 	_xp->setFrameRate(0);
@@ -1582,24 +1582,24 @@ int16 BoltEngine::helpScooby() {
 	byte *helpPics[3];
 
 	// If Scooby is mid-movement, stop and face idle direction
-	if (g_scoobyGameState.currentAnim != -2 && g_scoobyGameState.currentAnim != -3) {
-		g_scoobyGameState.velocityX = 0;
-		g_scoobyGameState.velocityY = 0;
+	if (_scoobyGameState.currentAnim != -2 && _scoobyGameState.currentAnim != -3) {
+		_scoobyGameState.velocityX = 0;
+		_scoobyGameState.velocityY = 0;
 
-		if (g_scoobyGameState.direction == 2)
-			g_scoobyGameState.transitionTarget = -2;
+		if (_scoobyGameState.direction == 2)
+			_scoobyGameState.transitionTarget = -2;
 		else
-			g_scoobyGameState.transitionTarget = -3;
+			_scoobyGameState.transitionTarget = -3;
 
-		g_scoobyDesiredDir = -1;
-		g_scoobyInputHoldCount = 0;
-		g_scoobyMoveRequested = 1;
+		_scoobyDesiredDir = -1;
+		_scoobyInputHoldCount = 0;
+		_scoobyMoveRequested = 1;
 
 		do {
 			updateScoobyTransition();
 			updateScoobyWalls();
 			_xp->updateDisplay();
-		} while (g_scoobyTransitioning != 0);
+		} while (_scoobyTransitioning != 0);
 
 		updateScoobyTransition();
 		updateScoobyWalls();
@@ -1612,12 +1612,12 @@ int16 BoltEngine::helpScooby() {
 
 	_xp->setFrameRate(0);
 	_xp->setInactivityTimer(0);
-	animateSSprite(&g_scoobySprite, 0);
+	animateSSprite(&_scoobySprite, 0);
 
 	// Load 3 help screen entries
 	for (int16 i = 0; i < 3; i++) {
-		int16 memberIdx = READ_UINT16(g_scoobyLevelData + 0x0C) + i;
-		helpEntries[i] = memberAddr(g_scoobyBoltLib, memberIdx);
+		int16 memberIdx = READ_UINT16(_scoobyLevelData + 0x0C) + i;
+		helpEntries[i] = memberAddr(_scoobyBoltLib, memberIdx);
 
 		byte *entry = helpEntries[i];
 		if (READ_UINT32(entry) == 2)
@@ -1630,7 +1630,7 @@ int16 BoltEngine::helpScooby() {
 
 		// Load and display pic
 		int16 picMember = READ_UINT16(entry + 4);
-		helpPics[i] = memberAddr(g_scoobyBoltLib, picMember);
+		helpPics[i] = memberAddr(_scoobyBoltLib, picMember);
 		displayPic(helpPics[i], 0, 0, 0);
 	}
 
@@ -1762,7 +1762,7 @@ int16 BoltEngine::helpScooby() {
 			if (wasPlaying != 0)
 				break;
 
-			if (startAnimation(g_rtfHandle, 0x1C)) {
+			if (startAnimation(_rtfHandle, 0x1C)) {
 				animFrameIdx = 0;
 				isPlaying = 1;
 			}
@@ -1822,7 +1822,7 @@ int16 BoltEngine::helpScooby() {
 		hiliteScoobyHelpObject(helpEntries[i], 0);
 	}
 
-	animateSSprite(&g_scoobySprite, 0);
+	animateSSprite(&_scoobySprite, 0);
 	_xp->updateDisplay();
 	_xp->setFrameRate(12);
 
@@ -1876,13 +1876,13 @@ int16 BoltEngine::playScooby() {
 	int16 inputDir = -1;
 
 	// Check if help screen should show on startup
-	if (g_scoobyShowHelp != 0) {
+	if (_scoobyShowHelp != 0) {
 		int16 helpResult = helpScooby();
 		if (helpResult == 0)
 			return 5; // exit/quit
 	}
 
-	g_scoobyShowHelp = 0;
+	_scoobyShowHelp = 0;
 	_xp->enableController();
 
 	// Main game loop
@@ -1890,7 +1890,7 @@ int16 BoltEngine::playScooby() {
 		updateScoobyLocation();
 
 		// Level complete?
-		if (g_scoobyGameState.levelComplete != 0)
+		if (_scoobyGameState.levelComplete != 0)
 			return 16;
 
 		// Poll for event
@@ -1909,11 +1909,11 @@ int16 BoltEngine::playScooby() {
 		case etInactivity:
 			if (helpScooby() == 0)
 				return 5;
-			g_scoobyMoveRequested = 0;
+			_scoobyMoveRequested = 0;
 			break;
 
 		case etSound:
-			g_scoobySoundPlaying = 0;
+			_scoobySoundPlaying = 0;
 			updateScoobySound();
 			break;
 
@@ -1933,7 +1933,7 @@ int16 BoltEngine::playScooby() {
 int16 BoltEngine::scoobyGame(int16 prevBooth) {
 	int16 result = 5;
 
-	if (!openBOLTLib(&g_scoobyBoltLib, &g_scoobyBoltCallbacks, assetPath("scooby.blt")))
+	if (!openBOLTLib(&_scoobyBoltLib, &_scoobyBoltCallbacks, assetPath("scooby.blt")))
 		return result;
 
 	int16 prevInactivity = _xp->setInactivityTimer(30);
@@ -1944,13 +1944,13 @@ int16 BoltEngine::scoobyGame(int16 prevBooth) {
 
 	cleanUpScooby();
 	_xp->setInactivityTimer(prevInactivity);
-	closeBOLTLib(&g_scoobyBoltLib);
+	closeBOLTLib(&_scoobyBoltLib);
 
 	return result;
 }
 
 void BoltEngine::swapScoobyHelpEntry() {
-	byte *data = g_boltCurrentMemberEntry->dataPtr;
+	byte *data = _boltCurrentMemberEntry->dataPtr;
 	if (data == nullptr)
 		return;
 
@@ -1963,11 +1963,11 @@ void BoltEngine::swapScoobyHelpEntry() {
 }
 
 void BoltEngine::swapScoobyWordArray() {
-	byte *data = g_boltCurrentMemberEntry->dataPtr;
+	byte *data = _boltCurrentMemberEntry->dataPtr;
 	if (data == nullptr)
 		return;
 
-	int16 count = g_boltCurrentMemberEntry->decompSize >> 1;
+	int16 count = _boltCurrentMemberEntry->decompSize >> 1;
 
 	for (int16 i = 0; i < count; i++) {
 		WRITE_UINT16(data, READ_BE_UINT16(data));
diff --git a/engines/bolt/booths/topcat.cpp b/engines/bolt/booths/topcat.cpp
index bd6233d20ae..d83e4bd707c 100644
--- a/engines/bolt/booths/topcat.cpp
+++ b/engines/bolt/booths/topcat.cpp
@@ -39,105 +39,105 @@ int16 Bolt::BoltEngine::topCatGame(int16 prevBooth) {
 }
 
 bool BoltEngine::initTopCat() {
-	g_topCatRtfHandle = openRTF(assetPath(g_displayMode != 0 ? "topcatcr.av" : "topcatog.av"));
-	if (!g_topCatRtfHandle)
+	_topCatRtfHandle = openRTF(assetPath(_displayMode != 0 ? "topcatcr.av" : "topcatog.av"));
+	if (!_topCatRtfHandle)
 		return false;
 
-	g_topCatAvHandle = openRTF(assetPath("topcatau.av"));
-	if (!g_topCatAvHandle)
+	_topCatAvHandle = openRTF(assetPath("topcatau.av"));
+	if (!_topCatAvHandle)
 		return false;
 
-	if (!openBOLTLib(&g_topCatBoltLib, &g_topCatBoltCallbacks, assetPath("topcat.blt")))
+	if (!openBOLTLib(&_topCatBoltLib, &_topCatBoltCallbacks, assetPath("topcat.blt")))
 		return false;
 
-	if (!getBOLTGroup(g_topCatBoltLib, 0, 1))
+	if (!getBOLTGroup(_topCatBoltLib, 0, 1))
 		return false;
 
-	g_topCatBackgroundPalette = memberAddr(g_topCatBoltLib, 0x20);
-	g_topCatBackground = memberAddr(g_topCatBoltLib, g_displayMode != 0 ? 0x22 : 0x21);
-	g_topCatBackgroundAnimationPalette = memberAddr(g_topCatBoltLib, 0x00);
-	g_topCatGraphicsAssets = memberAddr(g_topCatBoltLib, g_displayMode != 0 ? 0x1E : 0x1D);
+	_topCatBackgroundPalette = memberAddr(_topCatBoltLib, 0x20);
+	_topCatBackground = memberAddr(_topCatBoltLib, _displayMode != 0 ? 0x22 : 0x21);
+	_topCatBackgroundAnimationPalette = memberAddr(_topCatBoltLib, 0x00);
+	_topCatGraphicsAssets = memberAddr(_topCatBoltLib, _displayMode != 0 ? 0x1E : 0x1D);
 
-	g_topCatBackgroundAnimFrame = 0;
+	_topCatBackgroundAnimFrame = 0;
 
 	// Count background anim frames...
-	g_topCatMaxBackgroundAnimFrames = 0;
-	while (getResolvedPtr(g_topCatGraphicsAssets, g_topCatMaxBackgroundAnimFrames * 4) != 0) {
-		g_topCatMaxBackgroundAnimFrames++;
+	_topCatMaxBackgroundAnimFrames = 0;
+	while (getResolvedPtr(_topCatGraphicsAssets, _topCatMaxBackgroundAnimFrames * 4) != 0) {
+		_topCatMaxBackgroundAnimFrames++;
 	}
 
-	g_topCatCurrentAnimType = 0;
-	g_topCatAnimStateMachineStep = 0;
-	g_topCatQueuedSoundFrames = 0;
-	g_topCatAnimQueueSize = 0;
+	_topCatCurrentAnimType = 0;
+	_topCatAnimStateMachineStep = 0;
+	_topCatQueuedSoundFrames = 0;
+	_topCatAnimQueueSize = 0;
 
-	g_topCatButtonsPalette = memberAddr(g_topCatBoltLib, 0x3D);
-	g_topCatBlinkEntries = memberAddr(g_topCatBoltLib, 0x3E);
+	_topCatButtonsPalette = memberAddr(_topCatBoltLib, 0x3D);
+	_topCatBlinkEntries = memberAddr(_topCatBoltLib, 0x3E);
 
-	g_topCatLightsPalette = memberAddr(g_topCatBoltLib, 0x31);
-	g_topCatBlinkSeqPtr = nullptr;
+	_topCatLightsPalette = memberAddr(_topCatBoltLib, 0x31);
+	_topCatBlinkSeqPtr = nullptr;
 
-	g_topCatSelectedChoiceOverlayGfx = memberAddr(g_topCatBoltLib, 0x26);
-	g_topCatCycleData = memberAddr(g_topCatBoltLib, 0x27);
-	boltCycleToXPCycle(g_topCatCycleData, g_topCatCycleSpecs);
+	_topCatSelectedChoiceOverlayGfx = memberAddr(_topCatBoltLib, 0x26);
+	_topCatCycleData = memberAddr(_topCatBoltLib, 0x27);
+	boltCycleToXPCycle(_topCatCycleData, _topCatCycleSpecs);
 
-	g_topCatChoiceCycleState->startIndex = 0;
-	g_topCatChoiceCycleState->endIndex = 0;
-	g_topCatChoiceCycleState->delay = 0;
-	g_topCatChoiceCycleState->nextFire = 0;
-	g_topCatChoiceCycleState->active = false;
+	_topCatChoiceCycleState->startIndex = 0;
+	_topCatChoiceCycleState->endIndex = 0;
+	_topCatChoiceCycleState->delay = 0;
+	_topCatChoiceCycleState->nextFire = 0;
+	_topCatChoiceCycleState->active = false;
 
-	g_topCatShuffledQuestions = memberAddr(g_topCatBoltLib, 0x32); // [60]
-	g_topCatShuffledAnswers = memberAddr(g_topCatBoltLib, 0x33); // [60]
-	g_topCatAnswersPermutations = memberAddr(g_topCatBoltLib, 0x34); // [60 * 3]
-	g_topCatAnswers = memberAddr(g_topCatBoltLib, 0x35);
-	g_topCatAnswersScreenPositions = memberAddr(g_topCatBoltLib, 0x36);
+	_topCatShuffledQuestions = memberAddr(_topCatBoltLib, 0x32); // [60]
+	_topCatShuffledAnswers = memberAddr(_topCatBoltLib, 0x33); // [60]
+	_topCatAnswersPermutations = memberAddr(_topCatBoltLib, 0x34); // [60 * 3]
+	_topCatAnswers = memberAddr(_topCatBoltLib, 0x35);
+	_topCatAnswersScreenPositions = memberAddr(_topCatBoltLib, 0x36);
 
-	if (vLoad(&g_topCatSavedScore, g_topCatSaveFileName)) {
-		g_topCatScore = g_topCatSavedScore;
+	if (vLoad(&_topCatSavedScore, "TopCatBF")) {
+		_topCatScore = _topCatSavedScore;
 	} else {
-		g_topCatScore = 0;
+		_topCatScore = 0;
 	}
 
-	if (vLoad(g_topCatSaveBuffer, g_topCatStaticSaveFileName)) {
-		g_topCatShuffledQuestionsArrayIdx = READ_BE_INT16(g_topCatSaveBuffer);
+	if (vLoad(_topCatSaveBuffer, "TopCatBFStatic")) {
+		_topCatShuffledQuestionsArrayIdx = READ_BE_INT16(_topCatSaveBuffer);
 
 		int offset;
-		for (int i = 0; i < ARRAYSIZE(g_topCatSavedShuffledQuestions); i++) {
+		for (int i = 0; i < ARRAYSIZE(_topCatSavedShuffledQuestions); i++) {
 			offset = sizeof(int16) + i;
-			g_topCatSavedShuffledQuestions[i] = g_topCatSaveBuffer[offset];
+			_topCatSavedShuffledQuestions[i] = _topCatSaveBuffer[offset];
 		}
 
-		for (int i = 0; i < ARRAYSIZE(g_topCatSavedShuffledAnswers); i++) {
-			offset = sizeof(g_topCatSavedShuffledQuestions) + sizeof(int16) + i;
-			g_topCatSavedShuffledAnswers[i] = g_topCatSaveBuffer[offset];
+		for (int i = 0; i < ARRAYSIZE(_topCatSavedShuffledAnswers); i++) {
+			offset = sizeof(_topCatSavedShuffledQuestions) + sizeof(int16) + i;
+			_topCatSavedShuffledAnswers[i] = _topCatSaveBuffer[offset];
 		}
 
-		for (int i = 0; i < ARRAYSIZE(g_topCatSavedAnswersPermutations); i++) {
-			offset = sizeof(g_topCatSavedShuffledAnswers) + sizeof(g_topCatSavedShuffledQuestions) + sizeof(int16) + i;
-			g_topCatSavedAnswersPermutations[i] = g_topCatSaveBuffer[offset];
+		for (int i = 0; i < ARRAYSIZE(_topCatSavedAnswersPermutations); i++) {
+			offset = sizeof(_topCatSavedShuffledAnswers) + sizeof(_topCatSavedShuffledQuestions) + sizeof(int16) + i;
+			_topCatSavedAnswersPermutations[i] = _topCatSaveBuffer[offset];
 		}
 
 		int16 answerOff = 0;
 		for (int16 i = 0; i < 60; i++) {
-			g_topCatShuffledQuestions[i] = g_topCatSavedShuffledQuestions[i];
-			g_topCatShuffledAnswers[i] = g_topCatSavedShuffledAnswers[i];
+			_topCatShuffledQuestions[i] = _topCatSavedShuffledQuestions[i];
+			_topCatShuffledAnswers[i] = _topCatSavedShuffledAnswers[i];
 
 			for (int16 j = 0; j < 3; j++) {
-				g_topCatAnswersPermutations[answerOff + j] = g_topCatSavedAnswersPermutations[answerOff + j];
+				_topCatAnswersPermutations[answerOff + j] = _topCatSavedAnswersPermutations[answerOff + j];
 			}
 
 			answerOff += 3;
 		}
 	} else {
-		g_topCatShuffledQuestionsArrayIdx = -1;
+		_topCatShuffledQuestionsArrayIdx = -1;
 		shuffleTopCatQuestions();
 		shuffleTopCatPermutations();
 	}
 
-	g_topCatBlinkTimer = 0;
+	_topCatBlinkTimer = 0;
 
-	getTopCatSoundInfo(g_topCatBoltLib, 0x1F, &g_topCatSoundInfo);
+	getTopCatSoundInfo(_topCatBoltLib, 0x1F, &_topCatSoundInfo);
 
 	uint32 dummy;
 	while (_xp->getEvent(etTimer, &dummy) != etTimer);
@@ -145,39 +145,39 @@ bool BoltEngine::initTopCat() {
 	_xp->stopCycle();
 	_xp->setTransparency(false);
 
-	displayColors(g_topCatBackgroundPalette, stFront, 0);
-	displayPic(g_topCatBackground, g_displayX, g_displayY, stFront);
+	displayColors(_topCatBackgroundPalette, stFront, 0);
+	displayPic(_topCatBackground, _displayX, _displayY, stFront);
 	_xp->updateDisplay();
 
 	_xp->setTransparency(true);
-	displayColors(g_topCatBackgroundAnimationPalette, stFront, 0);
-	displayPic(g_topCatBackground, g_displayX, g_displayY, stBack);
+	displayColors(_topCatBackgroundAnimationPalette, stFront, 0);
+	displayPic(_topCatBackground, _displayX, _displayY, stBack);
 
-	byte *firstBackgroundAnimFrame = getResolvedPtr(g_topCatGraphicsAssets, 0);
-	displayPic(firstBackgroundAnimFrame, g_displayX, g_displayY, stFront);
+	byte *firstBackgroundAnimFrame = getResolvedPtr(_topCatGraphicsAssets, 0);
+	displayPic(firstBackgroundAnimFrame, _displayX, _displayY, stFront);
 
-	displayColors(g_topCatBackgroundPalette, stBack, 0);
+	displayColors(_topCatBackgroundPalette, stBack, 0);
 
 	if (!setupNextQuestion())
 		return false;
 
 	_xp->updateDisplay();
 
-	g_topCatHoveredEntry = nullptr;
+	_topCatHoveredEntry = nullptr;
 
 	// Init button palettes...
 	int16 off = 0;
 	byte *entry;
 	while (true) {
-		entry = getResolvedPtr(g_topCatButtonsPalette, off);
+		entry = getResolvedPtr(_topCatButtonsPalette, off);
 		if (!entry)
 			break;
 
 		if (READ_UINT32(entry) == 1)
-			g_topCatHelpButton = entry;
+			_topCatHelpButton = entry;
 
 		if (READ_UINT32(entry) == 2)
-			g_topCatBackButton = entry;
+			_topCatBackButton = entry;
 
 		_xp->getPalette(READ_UINT16(entry + 0x0E), READ_UINT16(entry + 0x10), entry + 0x2E);
 		unpackColors(READ_UINT16(entry + 0x10), entry + 0x12);
@@ -188,7 +188,7 @@ bool BoltEngine::initTopCat() {
 	// Init light palelttes...
 	int16 lightOff = 0;
 	for (int16 i = 0; i < 6; i++) {
-		byte *lightEntry = getResolvedPtr(g_topCatLightsPalette, lightOff);
+		byte *lightEntry = getResolvedPtr(_topCatLightsPalette, lightOff);
 
 		int16 colorCount = READ_UINT16(lightEntry + 4);
 		int16 palStart = READ_UINT16(lightEntry);
@@ -204,7 +204,7 @@ bool BoltEngine::initTopCat() {
 	}
 
 	// Set lights based on saved score...
-	int16 litMask = (1 << g_topCatScore) - 1;
+	int16 litMask = (1 << _topCatScore) - 1;
 	setScoreLights(litMask);
 
 	return true;
@@ -214,63 +214,63 @@ void BoltEngine::cleanUpTopCat() {
 	_xp->stopSound();
 	_xp->stopCycle();
 
-	if (g_topCatScore < 6) {
-		byte questionIdx = g_topCatShuffledQuestions[g_topCatShuffledQuestionsArrayIdx];
-		g_topCatShuffledAnswers[questionIdx]--;
-		g_topCatShuffledQuestionsArrayIdx--;
+	if (_topCatScore < 6) {
+		byte questionIdx = _topCatShuffledQuestions[_topCatShuffledQuestionsArrayIdx];
+		_topCatShuffledAnswers[questionIdx]--;
+		_topCatShuffledQuestionsArrayIdx--;
 	} else {
-		g_topCatScore = 0;
+		_topCatScore = 0;
 	}
 
-	g_topCatSavedScore = g_topCatScore;
-	g_topCatSaveHistory = g_topCatShuffledQuestionsArrayIdx;
+	_topCatSavedScore = _topCatScore;
+	_topCatSaveHistory = _topCatShuffledQuestionsArrayIdx;
 
 	int16 answerOff = 0;
 	for (int16 i = 0; i < 60; i++) {
-		g_topCatSavedShuffledQuestions[i] = g_topCatShuffledQuestions[i];
-		g_topCatSavedShuffledAnswers[i] = g_topCatShuffledAnswers[i];
+		_topCatSavedShuffledQuestions[i] = _topCatShuffledQuestions[i];
+		_topCatSavedShuffledAnswers[i] = _topCatShuffledAnswers[i];
 
 		for (int16 j = 0; j < 3; j++) {
-			g_topCatSavedAnswersPermutations[answerOff + j] = g_topCatAnswersPermutations[answerOff + j];
+			_topCatSavedAnswersPermutations[answerOff + j] = _topCatAnswersPermutations[answerOff + j];
 		}
 
 		answerOff += 3;
 	}
 
-	memset(g_topCatSaveBuffer, 0, sizeof(g_topCatSaveBuffer));
+	memset(_topCatSaveBuffer, 0, sizeof(_topCatSaveBuffer));
 
-	WRITE_BE_INT16(g_topCatSaveBuffer, g_topCatSaveHistory);
+	WRITE_BE_INT16(_topCatSaveBuffer, _topCatSaveHistory);
 
 	int offset;
-	for (int i = 0; i < ARRAYSIZE(g_topCatSavedShuffledQuestions); i++) {
+	for (int i = 0; i < ARRAYSIZE(_topCatSavedShuffledQuestions); i++) {
 		offset = sizeof(int16) + i;
-		g_topCatSaveBuffer[offset] = g_topCatSavedShuffledQuestions[i];
+		_topCatSaveBuffer[offset] = _topCatSavedShuffledQuestions[i];
 	}
 
-	for (int i = 0; i < ARRAYSIZE(g_topCatSavedShuffledAnswers); i++) {
-		offset = sizeof(g_topCatSavedShuffledQuestions) + sizeof(int16) + i;
-		g_topCatSaveBuffer[offset] = g_topCatSavedShuffledAnswers[i];
+	for (int i = 0; i < ARRAYSIZE(_topCatSavedShuffledAnswers); i++) {
+		offset = sizeof(_topCatSavedShuffledQuestions) + sizeof(int16) + i;
+		_topCatSaveBuffer[offset] = _topCatSavedShuffledAnswers[i];
 	}
 
-	for (int i = 0; i < ARRAYSIZE(g_topCatSavedAnswersPermutations); i++) {
-		offset = sizeof(g_topCatSavedShuffledAnswers) + sizeof(g_topCatSavedShuffledQuestions) + sizeof(int16) + i;
-		g_topCatSaveBuffer[offset] = g_topCatSavedAnswersPermutations[i];
+	for (int i = 0; i < ARRAYSIZE(_topCatSavedAnswersPermutations); i++) {
+		offset = sizeof(_topCatSavedShuffledAnswers) + sizeof(_topCatSavedShuffledQuestions) + sizeof(int16) + i;
+		_topCatSaveBuffer[offset] = _topCatSavedAnswersPermutations[i];
 	}
 
-	vSave(&g_topCatSavedScore, sizeof(g_topCatSavedScore), g_topCatSaveFileName);
-	vSave(g_topCatSaveBuffer, sizeof(g_topCatSaveBuffer), g_topCatStaticSaveFileName);
+	vSave(&_topCatSavedScore, sizeof(_topCatSavedScore), "TopCatBF");
+	vSave(_topCatSaveBuffer, sizeof(_topCatSaveBuffer), "TopCatBFStatic");
 
-	freeBOLTGroup(g_topCatBoltLib, 0, 1);
-	closeBOLTLib(&g_topCatBoltLib);
+	freeBOLTGroup(_topCatBoltLib, 0, 1);
+	closeBOLTLib(&_topCatBoltLib);
 
-	if (g_topCatRtfHandle) {
-		closeRTF(g_topCatRtfHandle);
-		g_topCatRtfHandle = nullptr;
+	if (_topCatRtfHandle) {
+		closeRTF(_topCatRtfHandle);
+		_topCatRtfHandle = nullptr;
 	}
 
-	if (g_topCatAvHandle) {
-		closeRTF(g_topCatAvHandle);
-		g_topCatAvHandle = nullptr;
+	if (_topCatAvHandle) {
+		closeRTF(_topCatAvHandle);
+		_topCatAvHandle = nullptr;
 	}
 
 	// These currently erroneously clear some graphics while on the "win a letter"
@@ -309,23 +309,23 @@ int16 BoltEngine::playTopCat() {
 		}
 
 		case etTimer: { // Timer expired
-			if (eventData != g_topCatBlinkTimer)
+			if (eventData != _topCatBlinkTimer)
 				break;
 
-			if (!g_topCatBlinkEntry)
+			if (!_topCatBlinkEntry)
 				break;
 
-			g_topCatBlinkTimer = _xp->startTimer(500);
+			_topCatBlinkTimer = _xp->startTimer(500);
 
 			// Toggle highlight...
 			int16 highlight;
-			if (READ_UINT16(g_topCatBlinkEntry + 0x0C) & 1) {
+			if (READ_UINT16(_topCatBlinkEntry + 0x0C) & 1) {
 				highlight = 0;
 			} else {
 				highlight = 1;
 			}
 
-			highlightObject(g_topCatBlinkEntry, highlight);
+			highlightObject(_topCatBlinkEntry, highlight);
 			break;
 		}
 
@@ -339,7 +339,7 @@ int16 BoltEngine::playTopCat() {
 			int16 off = 0;
 
 			while (true) {
-				entry = getResolvedPtr(g_topCatButtonsPalette, off);
+				entry = getResolvedPtr(_topCatButtonsPalette, off);
 				if (!entry)
 					break;
 
@@ -359,37 +359,37 @@ int16 BoltEngine::playTopCat() {
 			}
 
 			// Unhighlight previous entry if needed...
-			if (g_topCatHoveredEntry != entry &&
-				g_topCatHoveredEntry != nullptr &&
-				g_topCatHoveredEntry != g_topCatBlinkEntry &&
-				g_topCatHoveredEntry != g_topCatBackButton) {
-
-				if (g_topCatHoveredEntry == g_topCatHelpButton) {
-					if (g_topCatCurrentAnimType != 2)
-						highlightObject(g_topCatHoveredEntry, 0);
+			if (_topCatHoveredEntry != entry &&
+				_topCatHoveredEntry != nullptr &&
+				_topCatHoveredEntry != _topCatBlinkEntry &&
+				_topCatHoveredEntry != _topCatBackButton) {
+
+				if (_topCatHoveredEntry == _topCatHelpButton) {
+					if (_topCatCurrentAnimType != 2)
+						highlightObject(_topCatHoveredEntry, 0);
 				} else {
-					byte *hovered = g_topCatHoveredEntry;
+					byte *hovered = _topCatHoveredEntry;
 					if (READ_UINT32(hovered) < 3 || READ_UINT32(hovered) >= 6)
-						highlightObject(g_topCatHoveredEntry, 0);
+						highlightObject(_topCatHoveredEntry, 0);
 				}
 			}
 
-			g_topCatHoveredEntry = entry;
+			_topCatHoveredEntry = entry;
 
 			// Highlight new entry if applicable...
 			if (!entry)
 				break;
 
-			if (entry == g_topCatBlinkEntry)
+			if (entry == _topCatBlinkEntry)
 				break;
 
-			if (entry == g_topCatBackButton)
+			if (entry == _topCatBackButton)
 				break;
 
 			if (READ_UINT32(entry) >= 3 && READ_UINT32(entry) < 6)
 				break;
 
-			highlightObject(g_topCatHoveredEntry, 1);
+			highlightObject(_topCatHoveredEntry, 1);
 			break;
 		}
 
@@ -403,7 +403,7 @@ int16 BoltEngine::playTopCat() {
 
 		case etSound: {
 			int16 soundType;
-			if (soundDataPtr >= g_topCatSoundInfo.data && soundDataPtr < g_topCatSoundInfo.data + g_topCatSoundInfo.size) {
+			if (soundDataPtr >= _topCatSoundInfo.data && soundDataPtr < _topCatSoundInfo.data + _topCatSoundInfo.size) {
 				soundType = 1;
 			} else {
 				soundType = 2;
@@ -419,9 +419,9 @@ int16 BoltEngine::playTopCat() {
 		case etInactivity: {
 			// Toggle blinking highlight...
 			int16 highlight;
-			if (g_topCatBlinkEntry == g_topCatHoveredEntry &&
-				g_topCatBlinkEntry != 0) {
-				if (READ_UINT32(g_topCatBlinkEntry) < 3 || READ_UINT32(g_topCatBlinkEntry) >= 6) {
+			if (_topCatBlinkEntry == _topCatHoveredEntry &&
+				_topCatBlinkEntry != 0) {
+				if (READ_UINT32(_topCatBlinkEntry) < 3 || READ_UINT32(_topCatBlinkEntry) >= 6) {
 					highlight = 1;
 				} else {
 					highlight = 0;
@@ -430,32 +430,32 @@ int16 BoltEngine::playTopCat() {
 				highlight = 0;
 			}
 
-			highlightObject(g_topCatBlinkEntry, highlight);
+			highlightObject(_topCatBlinkEntry, highlight);
 
 			// Set help button as new blinking entry...
-			g_topCatBlinkEntry = g_topCatHelpButton;
+			_topCatBlinkEntry = _topCatHelpButton;
 
-			if (g_topCatHelpButton != 0) {
-				if (READ_UINT32(g_topCatBlinkEntry) < 3 || READ_UINT32(g_topCatBlinkEntry) >= 6) {
-					g_topCatBlinkTimer = _xp->startTimer(500);
+			if (_topCatHelpButton != 0) {
+				if (READ_UINT32(_topCatBlinkEntry) < 3 || READ_UINT32(_topCatBlinkEntry) >= 6) {
+					_topCatBlinkTimer = _xp->startTimer(500);
 				} else {
-					g_topCatBlinkTimer = 0;
+					_topCatBlinkTimer = 0;
 				}
 			} else {
-				g_topCatBlinkTimer = 0;
+				_topCatBlinkTimer = 0;
 			}
 
-			highlightObject(g_topCatBlinkEntry, 1);
+			highlightObject(_topCatBlinkEntry, 1);
 			break;
 		}
 
 		case etTrigger: { // Palette cycle event
-			if (g_topCatCurrentAnimType == 2) {
+			if (_topCatCurrentAnimType == 2) {
 				// Toggle blinking highlight...
 				int16 highlight;
-				if (g_topCatBlinkEntry == g_topCatHoveredEntry &&
-					g_topCatBlinkEntry != 0) {
-					if (READ_UINT32(g_topCatBlinkEntry) < 3 || READ_UINT32(g_topCatBlinkEntry) >= 6) {
+				if (_topCatBlinkEntry == _topCatHoveredEntry &&
+					_topCatBlinkEntry != 0) {
+					if (READ_UINT32(_topCatBlinkEntry) < 3 || READ_UINT32(_topCatBlinkEntry) >= 6) {
 						highlight = 1;
 					} else {
 						highlight = 0;
@@ -464,45 +464,45 @@ int16 BoltEngine::playTopCat() {
 					highlight = 0;
 				}
 
-				highlightObject(g_topCatBlinkEntry, highlight);
+				highlightObject(_topCatBlinkEntry, highlight);
 
 				// Advance to next blinking entry...
-				if (g_topCatCycleStep & 1) {
-					g_topCatBlinkEntry = nullptr;
+				if (_topCatCycleStep & 1) {
+					_topCatBlinkEntry = nullptr;
 				} else {
-					g_topCatBlinkEntry = getResolvedPtr(g_topCatBlinkEntries, (g_topCatCycleStep >> 1) * 4);
+					_topCatBlinkEntry = getResolvedPtr(_topCatBlinkEntries, (_topCatCycleStep >> 1) * 4);
 				}
 
-				if (g_topCatBlinkEntry != 0) {
-					if (READ_UINT32(g_topCatBlinkEntry) < 3 || READ_UINT32(g_topCatBlinkEntry) >= 6) {
-						g_topCatBlinkTimer = _xp->startTimer(500);
+				if (_topCatBlinkEntry != 0) {
+					if (READ_UINT32(_topCatBlinkEntry) < 3 || READ_UINT32(_topCatBlinkEntry) >= 6) {
+						_topCatBlinkTimer = _xp->startTimer(500);
 					} else {
-						g_topCatBlinkTimer = 0;
+						_topCatBlinkTimer = 0;
 					}
 				} else {
-					g_topCatBlinkTimer = 0;
+					_topCatBlinkTimer = 0;
 				}
 
-				highlightObject(g_topCatBlinkEntry, 1);
-				g_topCatCycleStep++;
+				highlightObject(_topCatBlinkEntry, 1);
+				_topCatCycleStep++;
 
-			} else if (g_topCatCurrentAnimType == 3) {
+			} else if (_topCatCurrentAnimType == 3) {
 				// Unhighlight previous...
-				if (g_topCatCycleStep > 3) {
-					byte *prevEntry = getResolvedPtr(g_topCatButtonsPalette, (g_topCatCycleStep - 1) * 4);
+				if (_topCatCycleStep > 3) {
+					byte *prevEntry = getResolvedPtr(_topCatButtonsPalette, (_topCatCycleStep - 1) * 4);
 					highlightObject(prevEntry, 0);
 				} else {
-					highlightObject(g_topCatBackButton, 0);
+					highlightObject(_topCatBackButton, 0);
 				}
 
 				// Highlight next...
-				byte *nextEntry = getResolvedPtr(g_topCatButtonsPalette, g_topCatCycleStep * 4);
+				byte *nextEntry = getResolvedPtr(_topCatButtonsPalette, _topCatCycleStep * 4);
 				highlightObject(nextEntry, 1);
-				g_topCatCycleStep++;
-			} else if (g_topCatCurrentAnimType == 6) {
+				_topCatCycleStep++;
+			} else if (_topCatCurrentAnimType == 6) {
 				// Light blinking animation...
-				int16 mask = READ_UINT16(g_topCatBlinkSeqPtr);
-				g_topCatBlinkSeqPtr++;
+				int16 mask = READ_UINT16(_topCatBlinkSeqPtr);
+				_topCatBlinkSeqPtr++;
 
 				setScoreLights(mask);
 			}
@@ -514,8 +514,8 @@ int16 BoltEngine::playTopCat() {
 			break;
 		}
 
-		if (exitFlag && g_topCatAnimQueueSize == 0 && g_topCatCurrentAnimType == 1) {
-			if (g_topCatScore != 6)
+		if (exitFlag && _topCatAnimQueueSize == 0 && _topCatCurrentAnimType == 1) {
+			if (_topCatScore != 6)
 				_xp->hideCursor();
 
 			return result;
@@ -524,14 +524,14 @@ int16 BoltEngine::playTopCat() {
 }
 
 int16 BoltEngine::handleActionButton(int16 *result) {
-	int16 prevState = g_topCatCurrentAnimType;
+	int16 prevState = _topCatCurrentAnimType;
 
-	switch (g_topCatCurrentAnimType) {
+	switch (_topCatCurrentAnimType) {
 	case 2:
 	case 3:
 		stopAnimation();
 		_xp->stopSound();
-		g_topCatQueuedSoundFrames = 0;
+		_topCatQueuedSoundFrames = 0;
 		setAnimType(1);
 		break;
 
@@ -543,22 +543,22 @@ int16 BoltEngine::handleActionButton(int16 *result) {
 		break;
 	}
 
-	if (g_topCatAnimQueueSize != 0 && g_topCatHoveredEntry != 0) {
-		if (READ_UINT32(g_topCatHoveredEntry) != 0)
+	if (_topCatAnimQueueSize != 0 && _topCatHoveredEntry != 0) {
+		if (READ_UINT32(_topCatHoveredEntry) != 0)
 			return 0;
 	}
 
-	if (!g_topCatHoveredEntry)
+	if (!_topCatHoveredEntry)
 		return 0;
 
 	_xp->setInactivityTimer(30);
 
 	// If blinking entry is the help button, stop blinking...
-	if (g_topCatBlinkEntry == g_topCatHelpButton) {
+	if (_topCatBlinkEntry == _topCatHelpButton) {
 		int16 highlight;
-		if (g_topCatBlinkEntry == g_topCatHoveredEntry &&
-			g_topCatBlinkEntry != 0) {
-			if (READ_UINT32(g_topCatBlinkEntry) < 3 || READ_UINT32(g_topCatBlinkEntry) >= 6) {
+		if (_topCatBlinkEntry == _topCatHoveredEntry &&
+			_topCatBlinkEntry != 0) {
+			if (READ_UINT32(_topCatBlinkEntry) < 3 || READ_UINT32(_topCatBlinkEntry) >= 6) {
 				highlight = 1;
 			} else {
 				highlight = 0;
@@ -567,13 +567,13 @@ int16 BoltEngine::handleActionButton(int16 *result) {
 			highlight = 0;
 		}
 
-		highlightObject(g_topCatBlinkEntry, highlight);
-		g_topCatBlinkEntry = 0;
-		g_topCatBlinkTimer = 0;
-		highlightObject(g_topCatBlinkEntry, 1);
+		highlightObject(_topCatBlinkEntry, highlight);
+		_topCatBlinkEntry = 0;
+		_topCatBlinkTimer = 0;
+		highlightObject(_topCatBlinkEntry, 1);
 	}
 
-	int32 entryType = READ_UINT32(g_topCatHoveredEntry);
+	int32 entryType = READ_UINT32(_topCatHoveredEntry);
 
 	if (entryType == 0) {
 		*result = 8;
@@ -603,34 +603,34 @@ int16 BoltEngine::handleActionButton(int16 *result) {
 	int16 answerSlot = (int16)entryType - 3;
 
 	// Display overlay pic on the answer slot...
-	int16 selectedChoiceOverlayX = READ_UINT16(g_topCatAnswersScreenPositions + answerSlot * 4);
-	int16 selectedChoiceOverlayY = READ_UINT16(g_topCatAnswersScreenPositions + answerSlot * 4 + 2);
-	byte *selectedChoiceOverlayPic = getResolvedPtr(g_topCatSelectedChoiceOverlayGfx, answerSlot * 4);
+	int16 selectedChoiceOverlayX = READ_UINT16(_topCatAnswersScreenPositions + answerSlot * 4);
+	int16 selectedChoiceOverlayY = READ_UINT16(_topCatAnswersScreenPositions + answerSlot * 4 + 2);
+	byte *selectedChoiceOverlayPic = getResolvedPtr(_topCatSelectedChoiceOverlayGfx, answerSlot * 4);
 	displayPic(selectedChoiceOverlayPic, selectedChoiceOverlayX, selectedChoiceOverlayY, stBack);
 
 	_xp->updateDisplay();
 
 	// Start palette cycle on the answer slot overlay...
-	g_topCatChoiceCycleState[0].startIndex = g_topCatCycleSpecs[answerSlot].startIndex;
-	g_topCatChoiceCycleState[0].endIndex = g_topCatCycleSpecs[answerSlot].endIndex;
-	g_topCatChoiceCycleState[0].delay = g_topCatCycleSpecs[answerSlot].delay;
-	g_topCatChoiceCycleState[0].nextFire = g_topCatCycleSpecs[answerSlot].nextFire;
-	g_topCatChoiceCycleState[0].active = g_topCatCycleSpecs[answerSlot].active;
+	_topCatChoiceCycleState[0].startIndex = _topCatCycleSpecs[answerSlot].startIndex;
+	_topCatChoiceCycleState[0].endIndex = _topCatCycleSpecs[answerSlot].endIndex;
+	_topCatChoiceCycleState[0].delay = _topCatCycleSpecs[answerSlot].delay;
+	_topCatChoiceCycleState[0].nextFire = _topCatCycleSpecs[answerSlot].nextFire;
+	_topCatChoiceCycleState[0].active = _topCatCycleSpecs[answerSlot].active;
 
-	_xp->startCycle(g_topCatChoiceCycleState);
+	_xp->startCycle(_topCatChoiceCycleState);
 
 	// Check answer...
-	int8 correctAnswer = (int8)g_topCatAnswers[g_topCatCurrentAnswerIdx * 3];
+	int8 correctAnswer = (int8)_topCatAnswers[_topCatCurrentAnswerIdx * 3];
 
 	if (correctAnswer == answerSlot) {
 		// Correct! :-)
 		queueAnim(4, 0); // Correct answer animation
 		queueAnim(6, 0); // Blink the lights
 
-		g_topCatShuffledQuestions[g_topCatShuffledQuestionsArrayIdx] |= 0x80; // Answered!
-		g_topCatScore++;
+		_topCatShuffledQuestions[_topCatShuffledQuestionsArrayIdx] |= 0x80; // Answered!
+		_topCatScore++;
 
-		if (g_topCatScore == 6) {
+		if (_topCatScore == 6) {
 			queueAnim(6, 0); // Blink the lights
 			*result = 0x10;
 			return 1;
@@ -647,13 +647,13 @@ int16 BoltEngine::handleActionButton(int16 *result) {
 }
 
 void BoltEngine::queueAnim(int16 animType, int16 param) {
-	int16 slot = g_topCatAnimQueueSize;
-	g_topCatAnimQueueSize++;
+	int16 slot = _topCatAnimQueueSize;
+	_topCatAnimQueueSize++;
 
-	TopCatAnim *entry = &g_topCatAnimQueue[slot];
+	TopCatAnim *entry = &_topCatAnimQueue[slot];
 
-	if (g_topCatAnimQueueSize > 3) {
-		g_topCatAnimQueueSize--;
+	if (_topCatAnimQueueSize > 3) {
+		_topCatAnimQueueSize--;
 		return;
 	}
 
@@ -669,25 +669,25 @@ void BoltEngine::queueAnim(int16 animType, int16 param) {
 		break;
 
 	case 4: // Correct answer
-		entry->animIndex = g_topCatCorrectAnimIdx;
-		g_topCatCorrectAnimIdx++;
-		g_topCatCorrectAnimIdx %= 3;
+		entry->animIndex = _topCatCorrectAnimIdx;
+		_topCatCorrectAnimIdx++;
+		_topCatCorrectAnimIdx %= 3;
 		break;
 
 	case 5: // Wrong answer
-		entry->animIndex = g_topCatWrongAnimIdx + 3;
-		g_topCatWrongAnimIdx++;
-		g_topCatWrongAnimIdx %= 3;
+		entry->animIndex = _topCatWrongAnimIdx + 3;
+		_topCatWrongAnimIdx++;
+		_topCatWrongAnimIdx %= 3;
 		break;
 
 	case 6: // Score light
-		if (g_topCatScore < 6)
-			entry->animIndex = g_topCatScore + 1;
+		if (_topCatScore < 6)
+			entry->animIndex = _topCatScore + 1;
 		else
 			entry->animIndex = 0;
 
 		// Store blink sequence pointer from table
-		entry->seqPtr = g_topCatBlinkSeqTable[g_topCatScore];
+		entry->seqPtr = _topCatBlinkSeqTable[_topCatScore];
 		break;
 	}
 }
@@ -695,15 +695,15 @@ void BoltEngine::queueAnim(int16 animType, int16 param) {
 bool BoltEngine::maintainAnim(int16 soundEvent) {
 	int16 animDone = 0;
 
-	switch (g_topCatAnimStateMachineStep) {
+	switch (_topCatAnimStateMachineStep) {
 	case 0: // Waiting to start next anim
-		if (g_topCatAnimQueueSize == 0)
+		if (_topCatAnimQueueSize == 0)
 			break;
 
-		if (!startNextAnim(&g_topCatShouldPlayAnswerAnim))
+		if (!startNextAnim(&_topCatShouldPlayAnswerAnim))
 			return false;
 
-		g_topCatAnimStateMachineStep = 1;
+		_topCatAnimStateMachineStep = 1;
 		break;
 	case 1: // Filling RTF buffer
 		if (fillRTFBuffer()) {
@@ -711,51 +711,51 @@ bool BoltEngine::maintainAnim(int16 soundEvent) {
 				break;
 		}
 
-		g_topCatAnimStateMachineStep = 2;
+		_topCatAnimStateMachineStep = 2;
 		break;
 	case 2: // Waiting for sound sync
 		if (!soundEvent)
 			break;
 
-		if (g_topCatMaxBackgroundAnimFrames - g_topCatQueuedSoundFrames != g_topCatBackgroundAnimFrame)
+		if (_topCatMaxBackgroundAnimFrames - _topCatQueuedSoundFrames != _topCatBackgroundAnimFrame)
 			break;
 
 		flushRTFSoundQueue();
-		g_topCatAnimStateMachineStep = 3;
+		_topCatAnimStateMachineStep = 3;
 		break;
 	case 3: // Waiting for AV end
 	case 4: // Playing AV
-		if (g_topCatAnimStateMachineStep == 3) {
+		if (_topCatAnimStateMachineStep == 3) {
 			if (soundEvent != 2)
 				break;
 
-			g_topCatAnimStateMachineStep = 4;
-			g_topCatMaintainSoundFlag = 1;
+			_topCatAnimStateMachineStep = 4;
+			_topCatMaintainSoundFlag = 1;
 		}
 
 		int16 avResult;
-		if (g_topCatShouldPlayAnswerAnim != 0)
+		if (_topCatShouldPlayAnswerAnim != 0)
 			avResult = maintainAV(soundEvent == 2 ? 1 : 0);
 		else
 			avResult = maintainAudioPlay(soundEvent == 2 ? 1 : 0);
 
 		if (!avResult) { // AV finished
-			if (g_topCatShouldPlayAnswerAnim != 0) {
+			if (_topCatShouldPlayAnswerAnim != 0) {
 				// Display background anim frame...
-				byte *animFrame = getResolvedPtr(g_topCatGraphicsAssets, g_topCatBackgroundAnimFrame * 4);
-				displayPic(animFrame, g_displayX, g_displayY, stFront);
+				byte *animFrame = getResolvedPtr(_topCatGraphicsAssets, _topCatBackgroundAnimFrame * 4);
+				displayPic(animFrame, _displayX, _displayY, stFront);
 			}
 
 			animDone = 1;
 		} else {
 			// Check if idle sound needs maintaining...
-			if (g_topCatMaintainSoundFlag != 0) {
+			if (_topCatMaintainSoundFlag != 0) {
 				if (!isRTFPlaying()) {
-					int16 savedFrame = g_topCatBackgroundAnimFrame;
-					g_topCatBackgroundAnimFrame = 0;
+					int16 savedFrame = _topCatBackgroundAnimFrame;
+					_topCatBackgroundAnimFrame = 0;
 					maintainIdleSound(0);
-					g_topCatBackgroundAnimFrame = savedFrame;
-					g_topCatMaintainSoundFlag = 0;
+					_topCatBackgroundAnimFrame = savedFrame;
+					_topCatMaintainSoundFlag = 0;
 				}
 			}
 		}
@@ -764,14 +764,14 @@ bool BoltEngine::maintainAnim(int16 soundEvent) {
 	}
 
 	// Advance audio and the background animation together...
-	if (soundEvent == 1 || (soundEvent == 2 && g_topCatShouldPlayAnswerAnim == 0)) {
-		g_topCatBackgroundAnimFrame++;
-		if (g_topCatBackgroundAnimFrame >= g_topCatMaxBackgroundAnimFrames)
-			g_topCatBackgroundAnimFrame = 0;
+	if (soundEvent == 1 || (soundEvent == 2 && _topCatShouldPlayAnswerAnim == 0)) {
+		_topCatBackgroundAnimFrame++;
+		if (_topCatBackgroundAnimFrame >= _topCatMaxBackgroundAnimFrames)
+			_topCatBackgroundAnimFrame = 0;
 
 		if (soundEvent == 1) {
-			if (g_topCatAnimStateMachineStep == 3 || g_topCatAnimStateMachineStep == 4) {
-				g_topCatQueuedSoundFrames--;
+			if (_topCatAnimStateMachineStep == 3 || _topCatAnimStateMachineStep == 4) {
+				_topCatQueuedSoundFrames--;
 			} else {
 				maintainIdleSound(1);
 			}
@@ -780,8 +780,8 @@ bool BoltEngine::maintainAnim(int16 soundEvent) {
 		// If no pending sound event, display the next background anim frame...
 		uint32 peekData;
 		if (_xp->peekEvent(etSound, &peekData) != etSound) {
-			byte *backgroundFrame = getResolvedPtr(g_topCatGraphicsAssets, g_topCatBackgroundAnimFrame * 4);
-			displayPic(backgroundFrame, g_displayX, g_displayY, stFront);
+			byte *backgroundFrame = getResolvedPtr(_topCatGraphicsAssets, _topCatBackgroundAnimFrame * 4);
+			displayPic(backgroundFrame, _displayX, _displayY, stFront);
 			_xp->updateDisplay();
 		}
 	}
@@ -793,19 +793,19 @@ bool BoltEngine::maintainAnim(int16 soundEvent) {
 }
 
 void BoltEngine::maintainIdleSound(int16 decrement) {
-	byte *soundData = g_topCatSoundInfo.data;
-	int16 chunkSize = (int16)(g_topCatSoundInfo.size / g_topCatMaxBackgroundAnimFrames);
+	byte *soundData = _topCatSoundInfo.data;
+	int16 chunkSize = (int16)(_topCatSoundInfo.size / _topCatMaxBackgroundAnimFrames);
 
 	if (decrement)
-		g_topCatQueuedSoundFrames--;
+		_topCatQueuedSoundFrames--;
 
-	int16 idx = g_topCatBackgroundAnimFrame + g_topCatQueuedSoundFrames;
+	int16 idx = _topCatBackgroundAnimFrame + _topCatQueuedSoundFrames;
 
-	while (g_topCatQueuedSoundFrames < 10) {
-		g_topCatQueuedSoundFrames++;
+	while (_topCatQueuedSoundFrames < 10) {
+		_topCatQueuedSoundFrames++;
 
-		if (idx >= g_topCatMaxBackgroundAnimFrames)
-			idx -= g_topCatMaxBackgroundAnimFrames;
+		if (idx >= _topCatMaxBackgroundAnimFrames)
+			idx -= _topCatMaxBackgroundAnimFrames;
 
 		_xp->playSound(soundData + chunkSize * idx, chunkSize, 22050);
 		idx++;
@@ -813,10 +813,10 @@ void BoltEngine::maintainIdleSound(int16 decrement) {
 }
 
 bool BoltEngine::startNextAnim(int16 *playAnswerAnim) {
-	TopCatAnim *entry = &g_topCatAnimQueue[0];
+	TopCatAnim *entry = &_topCatAnimQueue[0];
 	bool startResult = false;
 
-	if (g_topCatAnimQueueSize == 0)
+	if (_topCatAnimQueueSize == 0)
 		return false;
 
 	// Setup next question, if requested...
@@ -828,68 +828,68 @@ bool BoltEngine::startNextAnim(int16 *playAnswerAnim) {
 			_xp->updateDisplay();
 		}
 
-		entry->animIndex = g_topCatCurrentAnswerIdx * 60 + g_topCatDrawnQuestionId + 7;
+		entry->animIndex = _topCatCurrentAnswerIdx * 60 + _topCatDrawnQuestionId + 7;
 	}
 
 	int16 animType = entry->animType;
 
 	if (animType == 6) {
 		// Score light animation
-		g_topCatBlinkSeqPtr = entry->seqPtr;
+		_topCatBlinkSeqPtr = entry->seqPtr;
 
 		*playAnswerAnim = 0;
-		startResult = startAnimation(g_topCatAvHandle, entry->animIndex);
+		startResult = startAnimation(_topCatAvHandle, entry->animIndex);
 	} else if (animType == 3) {
 		// Question audio
-		g_topCatCycleStep = 3;
+		_topCatCycleStep = 3;
 
 		*playAnswerAnim = 0;
-		startResult = startAnimation(g_topCatAvHandle, entry->animIndex);
+		startResult = startAnimation(_topCatAvHandle, entry->animIndex);
 	} else if (animType == 2) {
 		// Help audio
-		g_topCatCycleStep = 0;
+		_topCatCycleStep = 0;
 
 		*playAnswerAnim = 0;
-		startResult = startAnimation(g_rtfHandle, entry->animIndex);
+		startResult = startAnimation(_rtfHandle, entry->animIndex);
 	} else {
 		// Correct/wrong answer animations
 		*playAnswerAnim = 1;
 		setAVBufferSize(0x4B000);
-		startResult = prepareAV(g_topCatRtfHandle, entry->animIndex,
-								g_displayWidth, g_displayHeight,
-								g_displayX, g_displayY);
+		startResult = prepareAV(_topCatRtfHandle, entry->animIndex,
+								_displayWidth, _displayHeight,
+								_displayX, _displayY);
 	}
 
 	if (startResult) {
 		setAnimType(entry->animType);
 	} else {
-		g_topCatCurrentAnimType = 1;
-		g_topCatAnimStateMachineStep = 0;
+		_topCatCurrentAnimType = 1;
+		_topCatAnimStateMachineStep = 0;
 		maintainIdleSound(0);
 	}
 
 	// Shift queue: copy entries 1..n down to 0..n-1
-	for (int16 i = 1; i < g_topCatAnimQueueSize; i++) {
-		g_topCatAnimQueue[i - 1] = g_topCatAnimQueue[i];
+	for (int16 i = 1; i < _topCatAnimQueueSize; i++) {
+		_topCatAnimQueue[i - 1] = _topCatAnimQueue[i];
 	}
 
-	g_topCatAnimQueueSize--;
+	_topCatAnimQueueSize--;
 
 	return true;
 }
 
 void BoltEngine::setAnimType(int16 newType) {
-	if (g_topCatCurrentAnimType == newType)
+	if (_topCatCurrentAnimType == newType)
 		return;
 
 	// Clean-up the previous state...
-	switch (g_topCatCurrentAnimType) {
+	switch (_topCatCurrentAnimType) {
 	case 2: // Was playing the help audio
 		// Stop blinking entry
 		int16 highlight;
-		if (g_topCatBlinkEntry == g_topCatHoveredEntry &&
-			g_topCatBlinkEntry != 0) {
-			if (READ_UINT32(g_topCatBlinkEntry) < 3 || READ_UINT32(g_topCatBlinkEntry) >= 6) {
+		if (_topCatBlinkEntry == _topCatHoveredEntry &&
+			_topCatBlinkEntry != 0) {
+			if (READ_UINT32(_topCatBlinkEntry) < 3 || READ_UINT32(_topCatBlinkEntry) >= 6) {
 				highlight = 1;
 			} else {
 				highlight = 0;
@@ -898,20 +898,20 @@ void BoltEngine::setAnimType(int16 newType) {
 			highlight = 0;
 		}
 
-		highlightObject(g_topCatBlinkEntry, highlight);
+		highlightObject(_topCatBlinkEntry, highlight);
 
-		g_topCatBlinkEntry = 0;
-		g_topCatBlinkTimer = 0;
-		highlightObject(g_topCatBlinkEntry, 1);
+		_topCatBlinkEntry = 0;
+		_topCatBlinkTimer = 0;
+		highlightObject(_topCatBlinkEntry, 1);
 		break;
 
 	case 3: // Was playing the question audio
-		if (g_topCatCycleStep >= 3) {
-			byte *prevEntry = getResolvedPtr(g_topCatButtonsPalette, (g_topCatCycleStep - 1) * 4);
+		if (_topCatCycleStep >= 3) {
+			byte *prevEntry = getResolvedPtr(_topCatButtonsPalette, (_topCatCycleStep - 1) * 4);
 			highlightObject(prevEntry, 0);
 		}
 
-		highlightObject(g_topCatBackButton, 0);
+		highlightObject(_topCatBackButton, 0);
 		break;
 
 	case 4: // Correct answer anim
@@ -919,7 +919,7 @@ void BoltEngine::setAnimType(int16 newType) {
 
 	case 5: // Wrong answer anim
 	case 6: // Score lights
-		if (g_topCatScore != 6)
+		if (_topCatScore != 6)
 			_xp->showCursor();
 
 		break;
@@ -938,7 +938,7 @@ void BoltEngine::setAnimType(int16 newType) {
 		break;
 
 	case 3: // Question audio
-		highlightObject(g_topCatBackButton, 1);
+		highlightObject(_topCatBackButton, 1);
 		break;
 
 	case 4: // Correct answer anim
@@ -950,8 +950,8 @@ void BoltEngine::setAnimType(int16 newType) {
 		break;
 	}
 
-	g_topCatCurrentAnimType = newType;
-	g_topCatAnimStateMachineStep = 0;
+	_topCatCurrentAnimType = newType;
+	_topCatAnimStateMachineStep = 0;
 }
 
 void BoltEngine::highlightObject(byte *entry, int16 highlight) {
@@ -974,43 +974,43 @@ bool BoltEngine::setupNextQuestion() {
 
 	int16 attempts = 0;
 	while (attempts < 60) {
-		g_topCatShuffledQuestionsArrayIdx++;
-		if (g_topCatShuffledQuestionsArrayIdx >= 60)
-			g_topCatShuffledQuestionsArrayIdx = 0;
+		_topCatShuffledQuestionsArrayIdx++;
+		if (_topCatShuffledQuestionsArrayIdx >= 60)
+			_topCatShuffledQuestionsArrayIdx = 0;
 
-		g_topCatDrawnQuestionId = (int8)g_topCatShuffledQuestions[g_topCatShuffledQuestionsArrayIdx];
+		_topCatDrawnQuestionId = (int8)_topCatShuffledQuestions[_topCatShuffledQuestionsArrayIdx];
 
-		if ((g_topCatDrawnQuestionId & 0x80) == 0)
+		if ((_topCatDrawnQuestionId & 0x80) == 0)
 			break; // Found a question which hasn't been answered yet...
 
 		attempts++;
 	}
 
 	if (attempts >= 60) {
-		g_topCatShuffledQuestionsArrayIdx = -1;
+		_topCatShuffledQuestionsArrayIdx = -1;
 		shuffleTopCatQuestions();
 		shuffleTopCatPermutations();
 
 		for (int16 i = 0; i < 60; i++) {
-			g_topCatShuffledQuestions[i] &= 0x7F; // Remove "already answered" flag from all questions...
+			_topCatShuffledQuestions[i] &= 0x7F; // Remove "already answered" flag from all questions...
 		}
 
 		return setupNextQuestion();
 	}
 
-	g_topCatShuffledAnswers[g_topCatDrawnQuestionId]++;
-	if (g_topCatShuffledAnswers[g_topCatDrawnQuestionId] >= 3)
-		g_topCatShuffledAnswers[g_topCatDrawnQuestionId] = 0;
+	_topCatShuffledAnswers[_topCatDrawnQuestionId]++;
+	if (_topCatShuffledAnswers[_topCatDrawnQuestionId] >= 3)
+		_topCatShuffledAnswers[_topCatDrawnQuestionId] = 0;
 
-	int16 qIdx = g_topCatDrawnQuestionId;
-	int16 permutation = (int8)g_topCatShuffledAnswers[g_topCatDrawnQuestionId];
-	g_topCatCurrentAnswerIdx = (int8)g_topCatAnswersPermutations[qIdx * 3 + permutation];
+	int16 qIdx = _topCatDrawnQuestionId;
+	int16 permutation = (int8)_topCatShuffledAnswers[_topCatDrawnQuestionId];
+	_topCatCurrentAnswerIdx = (int8)_topCatAnswersPermutations[qIdx * 3 + permutation];
 
-	int16 groupId = g_topCatDrawnQuestionId * 0x100 + 0x100;
-	if (!getBOLTGroup(g_topCatBoltLib, groupId, 1))
+	int16 groupId = _topCatDrawnQuestionId * 0x100 + 0x100;
+	if (!getBOLTGroup(_topCatBoltLib, groupId, 1))
 		return false;
 
-	byte *questionsAnswersGfxTable = memberAddr(g_topCatBoltLib, g_topCatDrawnQuestionId * 0x100 + 0x104);
+	byte *questionsAnswersGfxTable = memberAddr(_topCatBoltLib, _topCatDrawnQuestionId * 0x100 + 0x104);
 
 	// Display question pic...
 	byte *questionPic = getResolvedPtr(questionsAnswersGfxTable, 0);
@@ -1019,10 +1019,10 @@ bool BoltEngine::setupNextQuestion() {
 	// Display the three answer choices...
 	int16 tableOff = 0;
 	for (int16 i = 0; i < 3; i++) {
-		int8 answerIdx = (int8)g_topCatAnswers[g_topCatCurrentAnswerIdx * 3 + i];
+		int8 answerIdx = (int8)_topCatAnswers[_topCatCurrentAnswerIdx * 3 + i];
 
-		int16 x = READ_UINT16(g_topCatAnswersScreenPositions + answerIdx * 4);
-		int16 y = READ_UINT16(g_topCatAnswersScreenPositions + answerIdx * 4 + 2);
+		int16 x = READ_UINT16(_topCatAnswersScreenPositions + answerIdx * 4);
+		int16 y = READ_UINT16(_topCatAnswersScreenPositions + answerIdx * 4 + 2);
 
 		int16 colorShift = (answerIdx - i) * 3;
 		byte *answerPic = getResolvedPtr(questionsAnswersGfxTable, tableOff + 4);
@@ -1032,7 +1032,7 @@ bool BoltEngine::setupNextQuestion() {
 		tableOff += 4;
 	}
 
-	freeBOLTGroup(g_topCatBoltLib, groupId, 1);
+	freeBOLTGroup(_topCatBoltLib, groupId, 1);
 
 	return true;
 }
@@ -1064,16 +1064,16 @@ void BoltEngine::shuffleTopCatQuestions() {
 	for (int16 i = 0; i < 60; i++) {
 		int16 randIdx = _xp->getRandom(60);
 
-		byte tmp = g_topCatShuffledQuestions[i];
-		g_topCatShuffledQuestions[i] = g_topCatShuffledQuestions[randIdx];
-		g_topCatShuffledQuestions[randIdx] = tmp;
+		byte tmp = _topCatShuffledQuestions[i];
+		_topCatShuffledQuestions[i] = _topCatShuffledQuestions[randIdx];
+		_topCatShuffledQuestions[randIdx] = tmp;
 
 		for (int16 j = 0; j < 3; j++) {
 			int16 randJ = _xp->getRandom(3);
 
-			byte aTmp = g_topCatAnswersPermutations[answerOff + j];
-			g_topCatAnswersPermutations[answerOff + j] = g_topCatAnswersPermutations[answerOff + randJ];
-			g_topCatAnswersPermutations[answerOff + randJ] = aTmp;
+			byte aTmp = _topCatAnswersPermutations[answerOff + j];
+			_topCatAnswersPermutations[answerOff + j] = _topCatAnswersPermutations[answerOff + randJ];
+			_topCatAnswersPermutations[answerOff + randJ] = aTmp;
 		}
 
 		answerOff += 3;
@@ -1085,18 +1085,18 @@ void BoltEngine::shuffleTopCatPermutations() {
 	for (int16 i = 0; i < 3; i++) {
 		int16 randIdx = _xp->getRandom(3);
 
-		int16 tmp = g_topCatPermTableA[i];
-		g_topCatPermTableA[i] = g_topCatPermTableA[randIdx];
-		g_topCatPermTableA[randIdx] = tmp;
+		int16 tmp = _topCatPermTableA[i];
+		_topCatPermTableA[i] = _topCatPermTableA[randIdx];
+		_topCatPermTableA[randIdx] = tmp;
 	}
 
 	// Shuffle second permutation table...
 	for (int16 i = 0; i < 3; i++) {
 		int16 randIdx = _xp->getRandom(3);
 
-		int16 tmp = g_topCatPermTableB[i];
-		g_topCatPermTableB[i] = g_topCatPermTableB[randIdx];
-		g_topCatPermTableB[randIdx] = tmp;
+		int16 tmp = _topCatPermTableB[i];
+		_topCatPermTableB[i] = _topCatPermTableB[randIdx];
+		_topCatPermTableB[randIdx] = tmp;
 	}
 }
 
@@ -1110,7 +1110,7 @@ void BoltEngine::setScoreLights(int16 litMask) {
 	int16 tableOff = 0;
 
 	while (tableOff < 0x18) {
-		byte *lightEntry = getResolvedPtr(g_topCatLightsPalette, tableOff);
+		byte *lightEntry = getResolvedPtr(_topCatLightsPalette, tableOff);
 
 		int16 palStart = READ_UINT16(lightEntry) + 0x80;
 		int16 colorCount = READ_UINT16(lightEntry + 4);
@@ -1133,8 +1133,8 @@ void BoltEngine::setScoreLights(int16 litMask) {
 }
 
 void BoltEngine::swapTopCatHelpEntry() {
-	byte *data = g_boltCurrentMemberEntry->dataPtr;
-	uint32 decompSize = g_boltCurrentMemberEntry->decompSize;
+	byte *data = _boltCurrentMemberEntry->dataPtr;
+	uint32 decompSize = _boltCurrentMemberEntry->decompSize;
 	int16 offset = 0;
 
 	while (offset < (int32)decompSize) {
diff --git a/engines/bolt/booths/yogi.cpp b/engines/bolt/booths/yogi.cpp
index 26247be03a0..9bea7ab3faf 100644
--- a/engines/bolt/booths/yogi.cpp
+++ b/engines/bolt/booths/yogi.cpp
@@ -26,72 +26,72 @@
 namespace Bolt {
 
 void BoltEngine::playSoundMapYogi(int16 memberId) {
-	byte *soundData = getBOLTMember(g_yogiBoltLib, memberId);
-	int32 soundSize = memberSize(g_yogiBoltLib, memberId);
+	byte *soundData = getBOLTMember(_yogiBoltLib, memberId);
+	int32 soundSize = memberSize(_yogiBoltLib, memberId);
 	if (soundData) {
 		_xp->playSound(soundData, soundSize, 22050);
-		g_yogiSoundActive = 1;
-		g_yogiSoundPlaying++;
+		_yogiSoundActive = 1;
+		_yogiSoundPlaying++;
 	}
 }
 
 void BoltEngine::waitSoundMapYogi() {
-	if (!g_yogiSoundPlaying)
+	if (!_yogiSoundPlaying)
 		return;
 
 	uint32 dummy;
 	while (_xp->getEvent(etSound, &dummy) == etEmpty);
 
-	g_yogiSoundActive = 0;
-	g_yogiSoundPlaying--;
+	_yogiSoundActive = 0;
+	_yogiSoundPlaying--;
 }
 
 void BoltEngine::stopSoundYogi() {
-	if (g_yogiSoundActive) {
+	if (_yogiSoundActive) {
 		_xp->stopSound();
-		g_yogiSoundActive = 0;
+		_yogiSoundActive = 0;
 	}
 }
 
 void BoltEngine::setYogiColors(int16 which) {
 	if (which == 0)
-		_xp->setPalette(g_yogiPalRange[1] - g_yogiPalRange[0] + 1, g_yogiPalRange[0], g_yogiPalHighlight0);
+		_xp->setPalette(_yogiPalRange[1] - _yogiPalRange[0] + 1, _yogiPalRange[0], _yogiPalHighlight0);
 	else if (which == 1)
-		_xp->setPalette(g_yogiPalRange[5] - g_yogiPalRange[4] + 1, g_yogiPalRange[4], g_yogiPalHighlight1);
+		_xp->setPalette(_yogiPalRange[5] - _yogiPalRange[4] + 1, _yogiPalRange[4], _yogiPalHighlight1);
 }
 
 void BoltEngine::restoreYogiColors(int16 which) {
 	if (which == 0)
-		_xp->setPalette(g_yogiPalRange[1] - g_yogiPalRange[0] + 1, g_yogiPalRange[0], g_yogiPalSave0);
+		_xp->setPalette(_yogiPalRange[1] - _yogiPalRange[0] + 1, _yogiPalRange[0], _yogiPalSave0);
 	else if (which == 1)
-		_xp->setPalette(g_yogiPalRange[5] - g_yogiPalRange[4] + 1, g_yogiPalRange[4], g_yogiPalSave1);
+		_xp->setPalette(_yogiPalRange[5] - _yogiPalRange[4] + 1, _yogiPalRange[4], _yogiPalSave1);
 }
 
 void BoltEngine::drawBasket(int16 slot, byte *basketSprite) {
 	int16 sprStride = READ_UINT16(basketSprite + 0x0A);
 	int16 sprHeight = READ_UINT16(basketSprite + 0x0C);
 
-	memset(g_yogiScratchBuf.pixelData, 0, (int32)sprStride * sprHeight);
+	memset(_yogiScratchBuf.pixelData, 0, (int32)sprStride * sprHeight);
 
-	int16 slotY = READ_UINT16(g_yogiBasketPic + slot * 2 + 0x42);
-	int16 slotX = READ_UINT16(g_yogiBasketPic + slot * 2 + 0x12);
+	int16 slotY = READ_UINT16(_yogiBasketPic + slot * 2 + 0x42);
+	int16 slotX = READ_UINT16(_yogiBasketPic + slot * 2 + 0x12);
 	Common::Rect targetRect(slotY, slotX, slotY + sprHeight, slotX + sprStride);
 
-	int16 basketCount = READ_UINT16(g_yogiBasketPic);
+	int16 basketCount = READ_UINT16(_yogiBasketPic);
 	for (int16 i = 0; i < basketCount; i++) {
-		if (g_yogiState.basketState[i] != 0)
+		if (_yogiState.basketState[i] != 0)
 			continue;
 
 		byte *otherSprite;
-		if (i == g_yogiState.selected1Slot || i == g_yogiState.selected2Slot)
-			otherSprite = g_yogiHlSprite;
+		if (i == _yogiState.selected1Slot || i == _yogiState.selected2Slot)
+			otherSprite = _yogiHlSprite;
 		else
-			otherSprite = g_yogiNormalSprite;
+			otherSprite = _yogiNormalSprite;
 
 		int16 otherStride = READ_UINT16(otherSprite + 0x0A);
 		int16 otherHeight = READ_UINT16(otherSprite + 0x0C);
-		int16 otherY = READ_UINT16(g_yogiBasketPic + i * 2 + 0x42);
-		int16 otherX = READ_UINT16(g_yogiBasketPic + i * 2 + 0x12);
+		int16 otherY = READ_UINT16(_yogiBasketPic + i * 2 + 0x42);
+		int16 otherX = READ_UINT16(_yogiBasketPic + i * 2 + 0x12);
 		Common::Rect otherRect(otherY, otherX, otherY + otherHeight, otherX + otherStride);
 
 		Common::Rect isect;
@@ -100,57 +100,57 @@ void BoltEngine::drawBasket(int16 slot, byte *basketSprite) {
 
 		byte *srcPixels = getResolvedPtr(otherSprite, 0x12);
 		byte *src = srcPixels + (isect.left - otherY) * otherStride + (isect.top - otherX);
-		byte *dst = g_yogiScratchBuf.pixelData + (isect.left - slotY) * sprStride + (isect.top - slotX);
+		byte *dst = _yogiScratchBuf.pixelData + (isect.left - slotY) * sprStride + (isect.top - slotX);
 
 		int16 blitWidth = isect.bottom - isect.top;
 		int16 blitHeight = isect.right - isect.left;
 		_xp->maskBlit(src, otherStride, dst, sprStride, blitWidth, blitHeight);
 	}
 
-	g_yogiScratchBuf.width = sprStride;
-	g_yogiScratchBuf.height = sprHeight;
-	g_yogiScratchBuf.palette = nullptr;
-	g_yogiScratchBuf.flags = 0;
+	_yogiScratchBuf.width = sprStride;
+	_yogiScratchBuf.height = sprHeight;
+	_yogiScratchBuf.palette = nullptr;
+	_yogiScratchBuf.flags = 0;
 
-	_xp->displayPic(&g_yogiScratchBuf, slotX, slotY, stFront);
+	_xp->displayPic(&_yogiScratchBuf, slotX, slotY, stFront);
 }
 
 void BoltEngine::drawAllBaskets() {
 	_xp->fillDisplay(0, stFront);
 
-	int16 basketCount = READ_UINT16(g_yogiBasketPic);
+	int16 basketCount = READ_UINT16(_yogiBasketPic);
 	for (int16 i = 0; i < basketCount; i++) {
-		if (g_yogiState.basketState[i] != 0)
+		if (_yogiState.basketState[i] != 0)
 			continue;
 
-		byte *sprite = (i == g_yogiState.selected1Slot || i == g_yogiState.selected2Slot)
-						   ? g_yogiHlSprite
-						   : g_yogiNormalSprite;
+		byte *sprite = (i == _yogiState.selected1Slot || i == _yogiState.selected2Slot)
+						   ? _yogiHlSprite
+						   : _yogiNormalSprite;
 
-		int16 slotY = READ_UINT16(g_yogiBasketPic + i * 2 + 0x42);
-		int16 slotX = READ_UINT16(g_yogiBasketPic + i * 2 + 0x12);
+		int16 slotY = READ_UINT16(_yogiBasketPic + i * 2 + 0x42);
+		int16 slotX = READ_UINT16(_yogiBasketPic + i * 2 + 0x12);
 		displayPic(sprite, slotX, slotY, stFront);
 	}
 }
 
 void BoltEngine::handleYogiMatch() {
-	playSoundMapYogi((int16)READ_UINT16(g_yogiBasketPic + 0x9E));
-
-	if (g_yogiState.basketCount == 2) {
-		g_yogiExitFlag = 1;
-		g_yogiState.levelComplete = 1;
-		g_yogiState.levelNumber++;
-		if (g_yogiState.levelNumber > 10)
-			g_yogiState.levelNumber = 10;
+	playSoundMapYogi((int16)READ_UINT16(_yogiBasketPic + 0x9E));
+
+	if (_yogiState.basketCount == 2) {
+		_yogiExitFlag = 1;
+		_yogiState.levelComplete = 1;
+		_yogiState.levelNumber++;
+		if (_yogiState.levelNumber > 10)
+			_yogiState.levelNumber = 10;
 		_xp->fillDisplay(0, stFront);
 	} else {
-		drawBasket(g_yogiState.selected1Slot, g_yogiHlSprite);
-		drawBasket(g_yogiState.selected2Slot, g_yogiHlSprite);
+		drawBasket(_yogiState.selected1Slot, _yogiHlSprite);
+		drawBasket(_yogiState.selected2Slot, _yogiHlSprite);
 	}
 
-	g_yogiState.selected1Slot = -1;
-	g_yogiState.selected2Slot = -1;
-	g_yogiState.basketCount -= 2;
+	_yogiState.selected1Slot = -1;
+	_yogiState.selected2Slot = -1;
+	_yogiState.basketCount -= 2;
 	_xp->updateDisplay();
 	waitSoundMapYogi();
 }
@@ -158,33 +158,33 @@ void BoltEngine::handleYogiMatch() {
 bool BoltEngine::loadYogiBgPic() {
 	int16 groupId;
 
-	if (g_displayMode == 0) {
+	if (_displayMode == 0) {
 		groupId = 0x100;
-	} else if (g_displayMode == 1) {
+	} else if (_displayMode == 1) {
 		groupId = 0x200;
 	} else {
 		return false;
 	}
 
-	if (!getBOLTGroup(g_yogiBoltLib, groupId, 1))
+	if (!getBOLTGroup(_yogiBoltLib, groupId, 1))
 		return false;
 
-	g_yogiBgPic = memberAddr(g_yogiBoltLib, groupId + 1);
+	_yogiBgPic = memberAddr(_yogiBoltLib, groupId + 1);
 	return true;
 }
 
 void BoltEngine::unloadYogiBgPic() {
-	freeBOLTGroup(g_yogiBoltLib, 0x100, 1);
+	freeBOLTGroup(_yogiBoltLib, 0x100, 1);
 }
 
 void BoltEngine::drawYogiLevel() {
-	byte *palSprite = memberAddr(g_yogiBoltLib, READ_UINT16(g_yogiBasketPic + 0x72));
+	byte *palSprite = memberAddr(_yogiBoltLib, READ_UINT16(_yogiBasketPic + 0x72));
 
-	g_yogiSoundActive = 0;
-	g_yogiHotSpotCount = 0;
-	g_yogiBlinkTimer1 = 0;
-	g_yogiBlinkTimer2 = 0;
-	g_yogiExitFlag = 0;
+	_yogiSoundActive = 0;
+	_yogiHotSpotCount = 0;
+	_yogiBlinkTimer1 = 0;
+	_yogiBlinkTimer2 = 0;
+	_yogiExitFlag = 0;
 
 	uint32 dummy;
 	while (_xp->getEvent(etTimer, &dummy) != etTimer);
@@ -192,116 +192,116 @@ void BoltEngine::drawYogiLevel() {
 	_xp->stopCycle();
 	_xp->setTransparency(false);
 	displayColors(palSprite, stBack, 0);
-	displayPic(g_yogiBgPic, g_displayX, g_displayY, stFront);
+	displayPic(_yogiBgPic, _displayX, _displayY, stFront);
 	_xp->updateDisplay();
 
 	_xp->setTransparency(true);
 	displayColors(palSprite, stFront, 0);
 	displayColors(palSprite, stBack, 1);
-	displayPic(g_yogiBgPic, g_displayX, g_displayY, stBack);
+	displayPic(_yogiBgPic, _displayX, _displayY, stBack);
 
 	drawAllBaskets();
 	_xp->updateDisplay();
 
-	g_yogiCursorX = 0xC0;
-	g_yogiCursorY = 0x78;
+	_yogiCursorX = 0xC0;
+	_yogiCursorY = 0x78;
 	_xp->setCursorPos(0xC0, 0x78);
 	_xp->setCursorColor(0, 0, 0xFF);
 	_xp->showCursor();
 
-	g_yogiPalRange[0] = READ_UINT16(g_yogiBasketPic + 0x7C) + 0x80;
-	g_yogiPalRange[1] = READ_UINT16(g_yogiBasketPic + 0x7E) + 0x80;
-	g_yogiPalRange[2] = READ_UINT16(g_yogiBasketPic + 0x80) + 0x80;
-	g_yogiPalRange[3] = READ_UINT16(g_yogiBasketPic + 0x82) + 0x80;
-	g_yogiPalRange[4] = READ_UINT16(g_yogiBasketPic + 0x8E) + 0x80;
-	g_yogiPalRange[5] = READ_UINT16(g_yogiBasketPic + 0x90) + 0x80;
-	g_yogiPalRange[6] = READ_UINT16(g_yogiBasketPic + 0x92) + 0x80;
-	g_yogiPalRange[7] = READ_UINT16(g_yogiBasketPic + 0x94) + 0x80;
-
-	_xp->getPalette(g_yogiPalRange[0], g_yogiPalRange[1] - g_yogiPalRange[0] + 1, g_yogiPalSave0);
-	_xp->getPalette(g_yogiPalRange[2], g_yogiPalRange[3] - g_yogiPalRange[2] + 1, g_yogiPalHighlight0);
-	_xp->getPalette(g_yogiPalRange[4], g_yogiPalRange[5] - g_yogiPalRange[4] + 1, g_yogiPalSave1);
-	_xp->getPalette(g_yogiPalRange[6], g_yogiPalRange[7] - g_yogiPalRange[6] + 1, g_yogiPalHighlight1);
+	_yogiPalRange[0] = READ_UINT16(_yogiBasketPic + 0x7C) + 0x80;
+	_yogiPalRange[1] = READ_UINT16(_yogiBasketPic + 0x7E) + 0x80;
+	_yogiPalRange[2] = READ_UINT16(_yogiBasketPic + 0x80) + 0x80;
+	_yogiPalRange[3] = READ_UINT16(_yogiBasketPic + 0x82) + 0x80;
+	_yogiPalRange[4] = READ_UINT16(_yogiBasketPic + 0x8E) + 0x80;
+	_yogiPalRange[5] = READ_UINT16(_yogiBasketPic + 0x90) + 0x80;
+	_yogiPalRange[6] = READ_UINT16(_yogiBasketPic + 0x92) + 0x80;
+	_yogiPalRange[7] = READ_UINT16(_yogiBasketPic + 0x94) + 0x80;
+
+	_xp->getPalette(_yogiPalRange[0], _yogiPalRange[1] - _yogiPalRange[0] + 1, _yogiPalSave0);
+	_xp->getPalette(_yogiPalRange[2], _yogiPalRange[3] - _yogiPalRange[2] + 1, _yogiPalHighlight0);
+	_xp->getPalette(_yogiPalRange[4], _yogiPalRange[5] - _yogiPalRange[4] + 1, _yogiPalSave1);
+	_xp->getPalette(_yogiPalRange[6], _yogiPalRange[7] - _yogiPalRange[6] + 1, _yogiPalHighlight1);
 }
 
 bool BoltEngine::loadYogiLevel() {
 	int32 maxSize = 0;
 
-	g_yogiLevelGroupId = (g_yogiState.levelNumber - 1) * 0x100 + 0x300;
+	_yogiLevelGroupId = (_yogiState.levelNumber - 1) * 0x100 + 0x300;
 
-	if (!getBOLTGroup(g_yogiBoltLib, g_yogiLevelGroupId, 1))
+	if (!getBOLTGroup(_yogiBoltLib, _yogiLevelGroupId, 1))
 		return false;
 
-	g_yogiBasketPic = memberAddr(g_yogiBoltLib, g_yogiLevelGroupId);
-	g_yogiNormalSprite = memberAddr(g_yogiBoltLib, READ_UINT16(g_yogiBasketPic + 0x02));
-	g_yogiHlSprite = memberAddr(g_yogiBoltLib, READ_UINT16(g_yogiBasketPic + 0x04));
-	g_yogiAnimSprite = memberAddr(g_yogiBoltLib, READ_UINT16(g_yogiBasketPic + 0x06));
+	_yogiBasketPic = memberAddr(_yogiBoltLib, _yogiLevelGroupId);
+	_yogiNormalSprite = memberAddr(_yogiBoltLib, READ_UINT16(_yogiBasketPic + 0x02));
+	_yogiHlSprite = memberAddr(_yogiBoltLib, READ_UINT16(_yogiBasketPic + 0x04));
+	_yogiAnimSprite = memberAddr(_yogiBoltLib, READ_UINT16(_yogiBasketPic + 0x06));
 
 	int32 size;
 
-	size = (int32)READ_UINT16(g_yogiNormalSprite + 0x0A) * READ_UINT16(g_yogiNormalSprite + 0x0C);
+	size = (int32)READ_UINT16(_yogiNormalSprite + 0x0A) * READ_UINT16(_yogiNormalSprite + 0x0C);
 	if (size > maxSize)
 		maxSize = size;
 
-	size = (int32)READ_UINT16(g_yogiHlSprite + 0x0A) * READ_UINT16(g_yogiHlSprite + 0x0C);
+	size = (int32)READ_UINT16(_yogiHlSprite + 0x0A) * READ_UINT16(_yogiHlSprite + 0x0C);
 	if (size > maxSize)
 		maxSize = size;
 
-	size = (int32)READ_UINT16(g_yogiAnimSprite + 0x0A) * READ_UINT16(g_yogiAnimSprite + 0x0C);
+	size = (int32)READ_UINT16(_yogiAnimSprite + 0x0A) * READ_UINT16(_yogiAnimSprite + 0x0C);
 	if (size > maxSize)
 		maxSize = size;
 
-	g_yogiScratchBuf.pixelData = (byte *)_xp->allocMem(maxSize);
-	if (!g_yogiScratchBuf.pixelData)
+	_yogiScratchBuf.pixelData = (byte *)_xp->allocMem(maxSize);
+	if (!_yogiScratchBuf.pixelData)
 		return false;
 
-	g_yogiSpriteStride = READ_UINT16(g_yogiNormalSprite + 0x0A);
-	g_yogiSpriteHeight = READ_UINT16(g_yogiNormalSprite + 0x0C);
+	_yogiSpriteStride = READ_UINT16(_yogiNormalSprite + 0x0A);
+	_yogiSpriteHeight = READ_UINT16(_yogiNormalSprite + 0x0C);
 	return true;
 }
 
 void BoltEngine::unloadYogiResources() {
 	_xp->hideCursor();
-	int16 basketGroupId = (g_yogiLevelId << 8) + 0xD00;
-	freeBOLTGroup(g_yogiBoltLib, basketGroupId, 1);
-	freeBOLTGroup(g_yogiBoltLib, g_yogiLevelGroupId, 1);
+	int16 basketGroupId = (_yogiLevelId << 8) + 0xD00;
+	freeBOLTGroup(_yogiBoltLib, basketGroupId, 1);
+	freeBOLTGroup(_yogiBoltLib, _yogiLevelGroupId, 1);
 }
 
 bool BoltEngine::initYogiLevel() {
-	g_yogiState.currentSlot++;
-	if (g_yogiState.currentSlot >= 10)
-		g_yogiState.currentSlot = 0;
+	_yogiState.currentSlot++;
+	if (_yogiState.currentSlot >= 10)
+		_yogiState.currentSlot = 0;
 
-	g_yogiLevelId = g_yogiState.slotIndex[g_yogiState.currentSlot];
+	_yogiLevelId = _yogiState.slotIndex[_yogiState.currentSlot];
 
 	if (!loadYogiLevel())
 		return false;
 
-	int16 basketCount = READ_UINT16(g_yogiBasketPic);
-	g_yogiState.basketCount = basketCount;
-	g_yogiState.levelComplete = 0;
-	g_yogiState.matchCount = 0;
-	g_yogiState.selectionPending = 0;
-	g_yogiState.selected1Slot = -1;
-	g_yogiState.selected2Slot = -1;
+	int16 basketCount = READ_UINT16(_yogiBasketPic);
+	_yogiState.basketCount = basketCount;
+	_yogiState.levelComplete = 0;
+	_yogiState.matchCount = 0;
+	_yogiState.selectionPending = 0;
+	_yogiState.selected1Slot = -1;
+	_yogiState.selected2Slot = -1;
 
 	for (int16 i = 0; i < 0x18; i++) {
-		g_yogiState.basketSound[i] = 0x10;
-		g_yogiState.basketState[i] = 0;
+		_yogiState.basketSound[i] = 0x10;
+		_yogiState.basketState[i] = 0;
 	}
 
-	int16 idx = g_yogiState.levelIndex[g_yogiLevelId];
-	int16 snd = g_yogiGlobal[g_yogiLevelId * 0x10 + idx];
+	int16 idx = _yogiState.levelIndex[_yogiLevelId];
+	int16 snd = _yogiGlobal[_yogiLevelId * 0x10 + idx];
 	int16 flag = 0;
 
 	for (int16 var_4 = 0; var_4 < basketCount; var_4++) {
 		int16 rnd = _xp->getRandom(basketCount);
-		while (g_yogiState.basketSound[rnd] != 0x10) {
+		while (_yogiState.basketSound[rnd] != 0x10) {
 			rnd++;
 			if (rnd >= basketCount)
 				rnd = 0;
 		}
-		g_yogiState.basketSound[rnd] = snd;
+		_yogiState.basketSound[rnd] = snd;
 
 		if (flag == 0) {
 			flag = 1;
@@ -310,17 +310,17 @@ bool BoltEngine::initYogiLevel() {
 			idx++;
 			if (idx == 0x10)
 				idx = 0;
-			snd = g_yogiGlobal[g_yogiLevelId * 0x10 + idx];
+			snd = _yogiGlobal[_yogiLevelId * 0x10 + idx];
 		}
 	}
 
-	g_yogiState.levelIndex[g_yogiLevelId] = idx;
+	_yogiState.levelIndex[_yogiLevelId] = idx;
 	drawYogiLevel();
 	return true;
 }
 
 bool BoltEngine::resumeYogiLevel() {
-	g_yogiLevelId = g_yogiState.slotIndex[g_yogiState.currentSlot];
+	_yogiLevelId = _yogiState.slotIndex[_yogiState.currentSlot];
 
 	if (!loadYogiLevel())
 		return false;
@@ -330,31 +330,31 @@ bool BoltEngine::resumeYogiLevel() {
 }
 
 bool BoltEngine::initYogi() {
-	g_yogiSoundPlaying = 0;
+	_yogiSoundPlaying = 0;
 
 	if (!loadYogiBgPic())
 		return false;
 
-	if (!vLoad(&g_yogiGlobal, "YogiGlobal")) {
+	if (!vLoad(&_yogiGlobal, "YogiGlobal")) {
 		int16 slotVal = 0;
 		int16 globIdx = 0;
 		int16 idx = 0;
 
 		while (slotVal < 10) {
-			g_yogiState.slotIndex[idx] = slotVal;
-			g_yogiState.levelIndex[idx] = 0;
+			_yogiState.slotIndex[idx] = slotVal;
+			_yogiState.levelIndex[idx] = 0;
 
 			for (int16 i = 0; i < 16; i++)
-				g_yogiGlobal[globIdx + i] = 0x10;
+				_yogiGlobal[globIdx + i] = 0x10;
 
 			for (int16 di = 0; di < 16; di++) {
 				int16 rnd = _xp->getRandom(16);
-				while (g_yogiGlobal[globIdx + rnd] != 0x10) {
+				while (_yogiGlobal[globIdx + rnd] != 0x10) {
 					rnd++;
 					if (rnd >= 16)
 						rnd = 0;
 				}
-				g_yogiGlobal[globIdx + rnd] = di;
+				_yogiGlobal[globIdx + rnd] = di;
 			}
 
 			globIdx += 0x10;
@@ -366,43 +366,43 @@ bool BoltEngine::initYogi() {
 	byte yogiStateBuf[0x9C] = { 0 };
 
 	if (!vLoad(&yogiStateBuf, "Yogi")) {
-		g_yogiState.levelNumber = 1;
-		g_yogiState.currentSlot = -1;
+		_yogiState.levelNumber = 1;
+		_yogiState.currentSlot = -1;
 		return initYogiLevel();
 	}
 
 	Common::SeekableReadStream *yogiStateReadStream = new Common::MemoryReadStream(yogiStateBuf, sizeof(yogiStateBuf), DisposeAfterUse::NO);
 
-	g_yogiState.levelNumber = yogiStateReadStream->readSint16BE();
-	g_yogiState.currentSlot = yogiStateReadStream->readSint16BE();
+	_yogiState.levelNumber = yogiStateReadStream->readSint16BE();
+	_yogiState.currentSlot = yogiStateReadStream->readSint16BE();
 
 	for (int i = 0; i < 10; i++)
-		g_yogiState.levelIndex[i] = yogiStateReadStream->readSint16BE();
+		_yogiState.levelIndex[i] = yogiStateReadStream->readSint16BE();
 
 	for (int i = 0; i < 10; i++)
-		g_yogiState.slotIndex[i] = yogiStateReadStream->readSint16BE();
+		_yogiState.slotIndex[i] = yogiStateReadStream->readSint16BE();
 
-	g_yogiState.levelComplete = yogiStateReadStream->readSint16BE();
+	_yogiState.levelComplete = yogiStateReadStream->readSint16BE();
 
 	for (int i = 0; i < 24; i++)
-		g_yogiState.basketSound[i] = yogiStateReadStream->readSint16BE();
+		_yogiState.basketSound[i] = yogiStateReadStream->readSint16BE();
 
 	for (int i = 0; i < 24; i++)
-		g_yogiState.basketState[i] = yogiStateReadStream->readSint16BE();
+		_yogiState.basketState[i] = yogiStateReadStream->readSint16BE();
 
-	g_yogiState.basketCount = yogiStateReadStream->readSint16BE();
-	g_yogiState.matchCount = yogiStateReadStream->readSint16BE();
-	g_yogiState.selectionPending = yogiStateReadStream->readSint16BE();
-	g_yogiState.selected1Slot = yogiStateReadStream->readSint16BE();
-	g_yogiState.selected2Slot = yogiStateReadStream->readSint16BE();
-	g_yogiState.sound1 = yogiStateReadStream->readSint16BE();
-	g_yogiState.sound2 = yogiStateReadStream->readSint16BE();
+	_yogiState.basketCount = yogiStateReadStream->readSint16BE();
+	_yogiState.matchCount = yogiStateReadStream->readSint16BE();
+	_yogiState.selectionPending = yogiStateReadStream->readSint16BE();
+	_yogiState.selected1Slot = yogiStateReadStream->readSint16BE();
+	_yogiState.selected2Slot = yogiStateReadStream->readSint16BE();
+	_yogiState.sound1 = yogiStateReadStream->readSint16BE();
+	_yogiState.sound2 = yogiStateReadStream->readSint16BE();
 
 	// Sanity check: should be exactly 0x9C
 	assert(yogiStateReadStream->pos() == 0x9C);
 	delete yogiStateReadStream;
 
-	if (g_yogiState.levelComplete)
+	if (_yogiState.levelComplete)
 		return initYogiLevel();
 	else
 		return resumeYogiLevel();
@@ -418,48 +418,48 @@ void BoltEngine::yogiToggleBlinking(int16 which, int16 *state) {
 
 void BoltEngine::yogiUpdateHotSpots(int16 x, int16 y) {
 	Common::Rect helpRect(
-		READ_UINT16(g_yogiBasketPic + 0x74), READ_UINT16(g_yogiBasketPic + 0x78),
-		READ_UINT16(g_yogiBasketPic + 0x76), READ_UINT16(g_yogiBasketPic + 0x7A));
+		READ_UINT16(_yogiBasketPic + 0x74), READ_UINT16(_yogiBasketPic + 0x78),
+		READ_UINT16(_yogiBasketPic + 0x76), READ_UINT16(_yogiBasketPic + 0x7A));
 
 	Common::Rect exitRect(
-		READ_UINT16(g_yogiBasketPic + 0x86), READ_UINT16(g_yogiBasketPic + 0x8A),
-		READ_UINT16(g_yogiBasketPic + 0x88), READ_UINT16(g_yogiBasketPic + 0x8C));
+		READ_UINT16(_yogiBasketPic + 0x86), READ_UINT16(_yogiBasketPic + 0x8A),
+		READ_UINT16(_yogiBasketPic + 0x88), READ_UINT16(_yogiBasketPic + 0x8C));
 
 	if (helpRect.contains(x, y)) {
-		if (!g_yogiBlinkTimer1 && !g_yogiHotSpotCount)
+		if (!_yogiBlinkTimer1 && !_yogiHotSpotCount)
 			setYogiColors(0);
 	} else {
-		if (!g_yogiBlinkTimer1 && !g_yogiHotSpotCount)
+		if (!_yogiBlinkTimer1 && !_yogiHotSpotCount)
 			restoreYogiColors(0);
 	}
 
 	if (exitRect.contains(x, y)) {
-		if (!g_yogiBlinkTimer2)
+		if (!_yogiBlinkTimer2)
 			setYogiColors(1);
 		return;
 	} else {
-		if (!g_yogiBlinkTimer2)
+		if (!_yogiBlinkTimer2)
 			restoreYogiColors(1);
 	}
 }
 
 int16 BoltEngine::findBasket(int16 x, int16 y) {
-	int16 basketCount = READ_UINT16(g_yogiBasketPic);
+	int16 basketCount = READ_UINT16(_yogiBasketPic);
 	for (int16 i = basketCount - 1; i >= 0; i--) {
-		if (g_yogiState.basketState[i] != 0)
+		if (_yogiState.basketState[i] != 0)
 			continue;
 
-		int16 slotX = READ_UINT16(g_yogiBasketPic + i * 2 + 0x12);
-		int16 slotX2 = slotX + g_yogiSpriteStride - 1;
-		int16 slotY = READ_UINT16(g_yogiBasketPic + i * 2 + 0x42);
-		int16 slotY2 = slotY + g_yogiSpriteHeight - 1;
+		int16 slotX = READ_UINT16(_yogiBasketPic + i * 2 + 0x12);
+		int16 slotX2 = slotX + _yogiSpriteStride - 1;
+		int16 slotY = READ_UINT16(_yogiBasketPic + i * 2 + 0x42);
+		int16 slotY2 = slotY + _yogiSpriteHeight - 1;
 
 		if (x < slotX || x > slotX2 || y < slotY || y > slotY2)
 			continue;
 
-		byte *sprite = (i == g_yogiState.selected1Slot || i == g_yogiState.selected2Slot)
-						   ? g_yogiHlSprite
-						   : g_yogiNormalSprite;
+		byte *sprite = (i == _yogiState.selected1Slot || i == _yogiState.selected2Slot)
+						   ? _yogiHlSprite
+						   : _yogiNormalSprite;
 
 		if (!getPixel(sprite, x - slotX, y - slotY))
 			continue;
@@ -471,27 +471,27 @@ int16 BoltEngine::findBasket(int16 x, int16 y) {
 }
 
 void BoltEngine::resolveYogiSelection() {
-	if (g_yogiState.sound1 == g_yogiState.sound2) {
-		g_yogiState.basketState[g_yogiState.selected1Slot] = 1;
-		g_yogiState.basketState[g_yogiState.selected2Slot] = 1;
+	if (_yogiState.sound1 == _yogiState.sound2) {
+		_yogiState.basketState[_yogiState.selected1Slot] = 1;
+		_yogiState.basketState[_yogiState.selected2Slot] = 1;
 		handleYogiMatch();
-		g_yogiState.matchCount = 0;
-		g_yogiState.selectionPending = 0;
+		_yogiState.matchCount = 0;
+		_yogiState.selectionPending = 0;
 	} else {
-		int16 slot1 = g_yogiState.selected1Slot;
-		g_yogiState.matchCount = 0;
-		g_yogiState.selected1Slot = -1;
-		drawBasket(slot1, g_yogiNormalSprite);
+		int16 slot1 = _yogiState.selected1Slot;
+		_yogiState.matchCount = 0;


Commit: a4e8528b28b07a0a8a179823a171848ae9d2510b
    https://github.com/scummvm/scummvm/commit/a4e8528b28b07a0a8a179823a171848ae9d2510b
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-04-07T01:11:14+02:00

Commit Message:
BOLT: Fix module.mk

Changed paths:
    engines/bolt/module.mk


diff --git a/engines/bolt/module.mk b/engines/bolt/module.mk
index d11f1f1dafd..fe2ff69f77b 100644
--- a/engines/bolt/module.mk
+++ b/engines/bolt/module.mk
@@ -6,12 +6,12 @@ MODULE_OBJS = \
 	barker.o \
 	bolt.o \
 	booth.o \
-	booths\fred.o \
-	booths\george.o \
-	booths\huck.o \
-	booths\scooby.o \
-	booths\topcat.o \
-	booths\yogi.o \
+	booths/fred.o \
+	booths/george.o \
+	booths/huck.o \
+	booths/scooby.o \
+	booths/topcat.o \
+	booths/yogi.o \
 	metaengine.o \
 	resource.o \
 	rtf.o \
@@ -19,16 +19,16 @@ MODULE_OBJS = \
 	state.o \
 	swap.o \
 	utils.o \
-	xplib\blit.o \
-	xplib\cursor.o \
-	xplib\display.o \
-	xplib\events.o \
-	xplib\file.o \
-	xplib\palette.o \
-	xplib\random.o \
-	xplib\sound.o \
-	xplib\timer.o \
-	xplib\xplib.o
+	xplib/blit.o \
+	xplib/cursor.o \
+	xplib/display.o \
+	xplib/events.o \
+	xplib/file.o \
+	xplib/palette.o \
+	xplib/random.o \
+	xplib/sound.o \
+	xplib/timer.o \
+	xplib/xplib.o
 
 # This module can be built as a plugin
 ifeq ($(ENABLE_BOLT), DYNAMIC_PLUGIN)


Commit: 564a389c582e3cb632a7c209ea7c637f8a2080ed
    https://github.com/scummvm/scummvm/commit/564a389c582e3cb632a7c209ea7c637f8a2080ed
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-04-07T01:11:14+02:00

Commit Message:
BOLT: Fix linux build

Changed paths:
    engines/bolt/booths/huck.cpp
    engines/bolt/state.cpp


diff --git a/engines/bolt/booths/huck.cpp b/engines/bolt/booths/huck.cpp
index 8efc0886589..257f525b3fb 100644
--- a/engines/bolt/booths/huck.cpp
+++ b/engines/bolt/booths/huck.cpp
@@ -787,6 +787,8 @@ void BoltEngine::handleEvent(int16 eventType, uint32 eventData) {
 					break;
 			}
 		}
+
+		break;
 	}
 
 	case etSound: {
diff --git a/engines/bolt/state.cpp b/engines/bolt/state.cpp
index 4e6bfdc8331..b118a68e30d 100644
--- a/engines/bolt/state.cpp
+++ b/engines/bolt/state.cpp
@@ -49,7 +49,7 @@ bool BoltEngine::vLoad(void *dest, const char *name) {
 	char localName[82];
 	int16 recordOffset;
 
-	strcpy_s(localName, name);
+	Common::strcpy_s(localName, name);
 	uint32 nameLen = strlen(name);
 	if (!(nameLen & 1)) {
 		localName[nameLen] = ' ';
@@ -74,7 +74,7 @@ bool BoltEngine::vLoad(void *dest, const char *name) {
 bool BoltEngine::vSave(void *src, uint16 srcSize, const char *name) {
 	char localName[82];
 
-	strcpy_s(localName, name);
+	Common::strcpy_s(localName, name);
 	uint32 nameLen = strlen(name);
 	if (!(nameLen & 1)) {
 		localName[nameLen] = ' ';
@@ -113,7 +113,7 @@ bool BoltEngine::vDelete(const char *name) {
 	char localName[82];
 	int16 recordOffset;
 
-	strcpy_s(localName, name);
+	Common::strcpy_s(localName, name);
 	uint32 nameLen = strlen(name);
 	if (!(nameLen & 1)) {
 		localName[nameLen] = ' ';


Commit: 556f126ceb7bc5f39847291fc1db589bfa5c4b12
    https://github.com/scummvm/scummvm/commit/556f126ceb7bc5f39847291fc1db589bfa5c4b12
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-04-07T01:11:14+02:00

Commit Message:
BOLT: Fix warnings

Changed paths:
    engines/bolt/xplib/events.cpp
    engines/bolt/xplib/file.cpp


diff --git a/engines/bolt/xplib/events.cpp b/engines/bolt/xplib/events.cpp
index 5637069b224..45b8060f6cc 100644
--- a/engines/bolt/xplib/events.cpp
+++ b/engines/bolt/xplib/events.cpp
@@ -323,6 +323,8 @@ void XpLib::handleKey(Common::KeyCode vkey, int16 down) {
 	case Common::KEYCODE_DOWN:
 		dirPtr = &_keyStateDown;
 		break; // VK_DOWN
+	default:
+		break;
 	}
 
 	if (dirPtr == nullptr)
diff --git a/engines/bolt/xplib/file.cpp b/engines/bolt/xplib/file.cpp
index 386c05cabb9..48dac0ec895 100644
--- a/engines/bolt/xplib/file.cpp
+++ b/engines/bolt/xplib/file.cpp
@@ -28,7 +28,7 @@ namespace Bolt {
 
 void XpLib::fileError(const char *message) {
 	stopCycle();
-	error(message);
+	error("%s", message);
 }
 
 Common::File *XpLib::openFile(const char *fileName, int16 flags) {


Commit: 7960ce109f5ef28747b520cccd2d4c6764b81e28
    https://github.com/scummvm/scummvm/commit/7960ce109f5ef28747b520cccd2d4c6764b81e28
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-04-07T01:11:14+02:00

Commit Message:
BOLT: Clean-up detection

Changed paths:
    engines/bolt/detection.cpp
    engines/bolt/detection.h
    engines/bolt/detection_tables.h


diff --git a/engines/bolt/detection.cpp b/engines/bolt/detection.cpp
index 4defbfd281f..287a42849d4 100644
--- a/engines/bolt/detection.cpp
+++ b/engines/bolt/detection.cpp
@@ -29,17 +29,14 @@
 #include "bolt/detection.h"
 #include "bolt/detection_tables.h"
 
-const DebugChannelDef BoltMetaEngineDetection::debugFlagList[] = {
-	{ Bolt::kDebugGraphics, "Graphics", "Graphics debug level" },
-	{ Bolt::kDebugPath, "Path", "Pathfinding debug level" },
-	{ Bolt::kDebugFilePath, "FilePath", "File path debug level" },
-	{ Bolt::kDebugScan, "Scan", "Scan for unrecognised games" },
-	{ Bolt::kDebugScript, "Script", "Enable debug script dump" },
-	DEBUG_CHANNEL_END
+static const char *const directoryGlobs[] = {
+	"ASSETS",
+	nullptr
 };
 
-BoltMetaEngineDetection::BoltMetaEngineDetection() : AdvancedMetaEngineDetection(
-	Bolt::gameDescriptions, Bolt::boltGames) {
+BoltMetaEngineDetection::BoltMetaEngineDetection() : AdvancedMetaEngineDetection(Bolt::gameDescriptions, Bolt::boltGames) {
+	_maxScanDepth = 2;
+	_directoryGlobs = directoryGlobs;
 }
 
 REGISTER_PLUGIN_STATIC(BOLT_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, BoltMetaEngineDetection);
diff --git a/engines/bolt/detection.h b/engines/bolt/detection.h
index e36027700d0..8ce6d37c0bd 100644
--- a/engines/bolt/detection.h
+++ b/engines/bolt/detection.h
@@ -26,14 +26,6 @@
 
 namespace Bolt {
 
-enum BoltDebugChannels {
-	kDebugGraphics = 1,
-	kDebugPath,
-	kDebugScan,
-	kDebugFilePath,
-	kDebugScript,
-};
-
 extern const PlainGameDescriptor boltGames[];
 
 extern const ADGameDescription gameDescriptions[];
@@ -43,8 +35,6 @@ extern const ADGameDescription gameDescriptions[];
 } // End of namespace Bolt
 
 class BoltMetaEngineDetection : public AdvancedMetaEngineDetection<ADGameDescription> {
-	static const DebugChannelDef debugFlagList[];
-
 public:
 	BoltMetaEngineDetection();
 	~BoltMetaEngineDetection() override {}
@@ -54,15 +44,11 @@ public:
 	}
 
 	const char *getEngineName() const override {
-		return "Bolt";
+		return "Cartoon Carnival";
 	}
 
 	const char *getOriginalCopyright() const override {
-		return "Bolt (C)";
-	}
-
-	const DebugChannelDef *getDebugChannels() const override {
-		return debugFlagList;
+		return "Hanna-Barbera's Cartoon Carnival (C) 1993-1995 Funhouse Design";
 	}
 };
 
diff --git a/engines/bolt/detection_tables.h b/engines/bolt/detection_tables.h
index fcf6df448ff..9b7980161f1 100644
--- a/engines/bolt/detection_tables.h
+++ b/engines/bolt/detection_tables.h
@@ -45,24 +45,6 @@ const ADGameDescription gameDescriptions[] = {
 		ADGF_TESTING,
 		GUIO1(GAMEOPTION_EXTEND_SCREEN)
 	},
-	{
-		"carnival",
-		nullptr,
-		{
-			{"ASSETS/BOOTHS.BLT", 0, "db13e86a850b9844bf1bab19e9ec2f6f", 1326163},
-			{"ASSETS/FRED.BLT",   0, "19540d4ebce8bedd6e5d8523ad82b333", 302133 },
-			{"ASSETS/GEORGE.BLT", 0, "57cfd68c99dfb13f139886eaf36b09ca", 1017533},
-			{"ASSETS/HUCK.BLT",   0, "c4587a4d613616bfde5e0af9ccf8465e", 1225354},
-			{"ASSETS/SCOOBY.BLT", 0, "c0918e36e532a24e2fc4942261d907aa", 3733392},
-			{"ASSETS/TOPCAT.BLT", 0, "33ed048eef63c54962ef37998cc19366", 579091 },
-			{"ASSETS/YOGI.BLT",   0, "e47d619a79b94f0dfff72a49599fdbcb", 5533358},
-			AD_LISTEND
-		},
-		Common::EN_USA,
-		Common::kPlatformWindows,
-		ADGF_TESTING,
-		GUIO1(GAMEOPTION_EXTEND_SCREEN)
-	},
 	{
 		"carnival",
 		nullptr,
@@ -81,24 +63,6 @@ const ADGameDescription gameDescriptions[] = {
 		ADGF_TESTING,
 		GUIO1(GAMEOPTION_EXTEND_SCREEN)
 	},
-	{
-		"carnival",
-		nullptr,
-		{
-			{"ASSETS/BOOTHS.BLT", 0, "c4d7ce39d1dd754a50bd909c1c2ed695", 1328201},
-			{"ASSETS/FRED.BLT",   0, "2491420b98c2421e19a751422c223cc8", 302133 },
-			{"ASSETS/GEORGE.BLT", 0, "b86116c9dc113d4bb7d6744d378c805c", 1017533},
-			{"ASSETS/HUCK.BLT",   0, "394407bb42a1f0abbe7db73fd0e9b873", 1225354},
-			{"ASSETS/SCOOBY.BLT", 0, "acf03553bc48c070adf57679307c900c", 3733392},
-			{"ASSETS/TOPCAT.BLT", 0, "116facccce1982111a1edf962bec3771", 567135 },
-			{"ASSETS/YOGI.BLT",   0, "ba3d148cbd10cd51f323be7895f52145", 5533358},
-			AD_LISTEND
-		},
-		Common::IT_ITA,
-		Common::kPlatformWindows,
-		ADGF_TESTING,
-		GUIO1(GAMEOPTION_EXTEND_SCREEN)
-	},
 	{
 		"carnival",
 		"Demo",
@@ -114,21 +78,6 @@ const ADGameDescription gameDescriptions[] = {
 		ADGF_TESTING | ADGF_DEMO,
 		GUIO1(GAMEOPTION_EXTEND_SCREEN)
 	},
-	{
-		"carnival",
-		"Demo",
-		{
-			{"ASSETS/BOOTHS.BLT", 0, "3fc80c0feaaa300720a3ed921496233a", 878177 },
-			{"ASSETS/GEORGE.BLT", 0, "ca29738fe8655b9adec81aa48914c213", 388257 },
-			{"ASSETS/SCOOBY.BLT", 0, "4dfbdd87fdd2ec05fbdc7ae6435fb239", 677438 },
-			{"ASSETS/YOGI.BLT",   0, "06da5c2b889b5ecca371e99d8209347c", 5209352},
-			AD_LISTEND
-		},
-		Common::IT_ITA,
-		Common::kPlatformWindows,
-		ADGF_TESTING | ADGF_DEMO,
-		GUIO1(GAMEOPTION_EXTEND_SCREEN)
-	},
 
 	AD_TABLE_END_MARKER
 


Commit: b31b7569bb8f58e9680a3ecd443d69494325284d
    https://github.com/scummvm/scummvm/commit/b31b7569bb8f58e9680a3ecd443d69494325284d
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-04-07T01:11:14+02:00

Commit Message:
BOLT: Fix assets not being found in release build

Changed paths:
    engines/bolt/bolt.cpp


diff --git a/engines/bolt/bolt.cpp b/engines/bolt/bolt.cpp
index 76078ea854d..54db77bf4d6 100644
--- a/engines/bolt/bolt.cpp
+++ b/engines/bolt/bolt.cpp
@@ -56,6 +56,9 @@ Common::String BoltEngine::getGameId() const {
 }
 
 Common::Error BoltEngine::run() {
+	const Common::FSNode gameDataDir(ConfMan.getPath("path"));
+	SearchMan.addSubDirectoryMatching(gameDataDir, "assets");
+
 	ConfMan.registerDefault("extended_viewport", false);
 	if (ConfMan.hasKey("extended_viewport", _targetName)) {
 		_extendedViewport = ConfMan.getBool("extended_viewport");


Commit: 87e6e8d8c16113763d32151212ef853ac2d07a2c
    https://github.com/scummvm/scummvm/commit/87e6e8d8c16113763d32151212ef853ac2d07a2c
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-04-07T01:11:14+02:00

Commit Message:
BOLT: CARNIVAL: Some fixes and clean-ups

Changed paths:
    engines/bolt/booth.cpp
    engines/bolt/resource.cpp


diff --git a/engines/bolt/booth.cpp b/engines/bolt/booth.cpp
index 92ce101e054..beef808cc12 100644
--- a/engines/bolt/booth.cpp
+++ b/engines/bolt/booth.cpp
@@ -740,25 +740,25 @@ bool BoltEngine::handleButtonPress(int16 hotspot) {
 		int16 animId;
 		switch (_currentBoothScene) {
 		case 3:
-			animId = _isDemo ? 0x0F : 0x18;
+			animId = _isDemo ? 0x0F : 0x12;
 			break;
 		case 4:
-			animId = _isDemo ? 0x10 : 0x12;
+			animId = _isDemo ? 0x10 : 0x13;
 			break;
 		case 5:
-			animId = _isDemo ? 0x11 : 0x13;
+			animId = _isDemo ? 0x11 : 0x14;
 			break;
 		case 6:
-			animId = _isDemo ? 0x12 : 0x14;
+			animId = _isDemo ? 0x12 : 0x15;
 			break;
 		case 7:
-			animId = _isDemo ? 0x13 : 0x15;
+			animId = _isDemo ? 0x13 : 0x16;
 			break;
 		case 8:
-			animId = _isDemo ? 0x14 : 0x16;
+			animId = _isDemo ? 0x14 : 0x17;
 			break;
 		case 9:
-			animId = _isDemo ? 0x15 : 0x17;
+			animId = _isDemo ? 0x15 : 0x18;
 			break;
 		default:
 			animId = -1;
diff --git a/engines/bolt/resource.cpp b/engines/bolt/resource.cpp
index 23191227084..68ea9122273 100644
--- a/engines/bolt/resource.cpp
+++ b/engines/bolt/resource.cpp
@@ -147,8 +147,6 @@ void BoltEngine::resolveFunction(uint32 *ref) {
 		return;
 	}
 
-	// TODO!
-	warning("BoltEngine::resolveFunction(): TODO!");
 	uint16 index = (uint16)*ref;
 	*ref = index;
 }
@@ -272,7 +270,6 @@ bool BoltEngine::closeBOLTLib(BOLTLib **libPtr) {
 	int16 groupCount = (*libPtr)->groupCount;
 	if (groupCount == 0) {
 		groupCount = 256;
-		warning("DEBUG, closeBOLTLib(): CHECK FOR OVERFLOW ");
 	}
 
 	// Free all groups in reverse order...
@@ -463,7 +460,6 @@ byte *BoltEngine::getBOLTMember(BOLTLib *lib, int16 resId) {
 						compressedSize = nextEntry->fileOffset;
 					} else {
 						// Last entry: total = memberDirSize + memberDirOffset + memberDataOffset
-						warning("is this even working?");
 						compressedSize = (uint32)(memberCount * 16) + _boltCurrentGroupEntry->memberDataOffset + _boltCurrentGroupEntry->memberDirOffset;
 					}
 


Commit: 9fb3cfc7555ededb598041fcd3138643a2578c35
    https://github.com/scummvm/scummvm/commit/9fb3cfc7555ededb598041fcd3138643a2578c35
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-04-07T01:11:14+02:00

Commit Message:
BOLT: CARNIVAL: Fix more bugs

Changed paths:
    engines/bolt/booths/huck.cpp
    engines/bolt/booths/scooby.cpp
    engines/bolt/resource.cpp


diff --git a/engines/bolt/booths/huck.cpp b/engines/bolt/booths/huck.cpp
index 257f525b3fb..2da1115040c 100644
--- a/engines/bolt/booths/huck.cpp
+++ b/engines/bolt/booths/huck.cpp
@@ -638,7 +638,7 @@ void BoltEngine::giftSwap() {
 	int16 slotA = -1;
 	int16 slotB = 0;
 	int16 counter = -1;
-	for (int16 i = 0, j = 0; j <= READ_UINT16(_huckGiftPic); i++, j++) {
+	for (int16 i = 0, j = 0; j < READ_UINT16(_huckGiftPic); i++, j++) {
 		if (_huckState.drawTable2[j] != 0)
 			continue;
 
diff --git a/engines/bolt/booths/scooby.cpp b/engines/bolt/booths/scooby.cpp
index a6512a7dffc..72a9177d43d 100644
--- a/engines/bolt/booths/scooby.cpp
+++ b/engines/bolt/booths/scooby.cpp
@@ -254,7 +254,7 @@ void BoltEngine::clearPictMSb(byte *pic) {
 	byte *pixelData = getResolvedPtr(pic, 0x12);
 	uint32 size = (uint32)READ_UINT16(pic + 0x0A) * (uint32)READ_UINT16(pic + 0x0C);
 
-	for (uint32 i = 0; i <= size; i++) {
+	for (uint32 i = 0; i < size; i++) {
 		pixelData[i] &= 0x7F;
 	}
 }
diff --git a/engines/bolt/resource.cpp b/engines/bolt/resource.cpp
index 68ea9122273..eb15aaaf516 100644
--- a/engines/bolt/resource.cpp
+++ b/engines/bolt/resource.cpp
@@ -251,7 +251,7 @@ bool BoltEngine::openBOLTLib(BOLTLib **libPtr, BOLTCallbacks *callbacks, const c
 		_xp->closeFile(fileHandle);
 
 	if (_boltCurrentLib != nullptr) {
-		_xp->freeMem(_boltCurrentLib);
+		delete _boltCurrentLib;
 		_boltCurrentLib = nullptr;
 	}
 
@@ -280,7 +280,7 @@ bool BoltEngine::closeBOLTLib(BOLTLib **libPtr) {
 	}
 
 	_xp->closeFile((*libPtr)->fileHandle);
-	_xp->freeMem(*libPtr);
+	delete *libPtr;
 	*libPtr = nullptr;
 
 	return true;


Commit: 5b289038dc5101f7defe944e0f4d0362df83b9d1
    https://github.com/scummvm/scummvm/commit/5b289038dc5101f7defe944e0f4d0362df83b9d1
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-04-07T01:11:14+02:00

Commit Message:
BOLT: Reduce include list in detection.cpp

Changed paths:
    engines/bolt/detection.cpp


diff --git a/engines/bolt/detection.cpp b/engines/bolt/detection.cpp
index 287a42849d4..645887b698c 100644
--- a/engines/bolt/detection.cpp
+++ b/engines/bolt/detection.cpp
@@ -20,12 +20,6 @@
  */
 
 #include "base/plugins.h"
-#include "common/config-manager.h"
-#include "common/file.h"
-#include "common/md5.h"
-#include "common/str-array.h"
-#include "common/translation.h"
-#include "common/util.h"
 #include "bolt/detection.h"
 #include "bolt/detection_tables.h"
 


Commit: f21d8e6800e98ec18438739987c8075ca7b3b04d
    https://github.com/scummvm/scummvm/commit/f21d8e6800e98ec18438739987c8075ca7b3b04d
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-04-07T01:11:14+02:00

Commit Message:
BOLT: Add myself to engine credits

Changed paths:
    engines/bolt/credits.pl


diff --git a/engines/bolt/credits.pl b/engines/bolt/credits.pl
index 3e137cd1a0f..e4a88db0393 100644
--- a/engines/bolt/credits.pl
+++ b/engines/bolt/credits.pl
@@ -1,3 +1,3 @@
 begin_section("Bolt");
-	add_person("Name 1", "Handle 1", "");
+	add_person("Andrea Boscarino", "Bosca", "");
 end_section();


Commit: eb07f69e08c2576c36fa4c5311f5b9ec99e2d9de
    https://github.com/scummvm/scummvm/commit/eb07f69e08c2576c36fa4c5311f5b9ec99e2d9de
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-04-07T01:11:14+02:00

Commit Message:
BOLT: Reduce number of files for detection

Also mark the game as ADGF_UNSTABLE

Changed paths:
    engines/bolt/detection_tables.h


diff --git a/engines/bolt/detection_tables.h b/engines/bolt/detection_tables.h
index 9b7980161f1..dbc3954fc2c 100644
--- a/engines/bolt/detection_tables.h
+++ b/engines/bolt/detection_tables.h
@@ -32,17 +32,12 @@ const ADGameDescription gameDescriptions[] = {
 		nullptr,
 		{
 			{"BOOTHS.BLT", 0, "db13e86a850b9844bf1bab19e9ec2f6f", 1326163},
-			{"FRED.BLT",   0, "19540d4ebce8bedd6e5d8523ad82b333", 302133 },
-			{"GEORGE.BLT", 0, "57cfd68c99dfb13f139886eaf36b09ca", 1017533},
-			{"HUCK.BLT",   0, "c4587a4d613616bfde5e0af9ccf8465e", 1225354},
 			{"SCOOBY.BLT", 0, "c0918e36e532a24e2fc4942261d907aa", 3733392},
-			{"TOPCAT.BLT", 0, "33ed048eef63c54962ef37998cc19366", 579091 },
-			{"YOGI.BLT",   0, "e47d619a79b94f0dfff72a49599fdbcb", 5533358},
 			AD_LISTEND
 		},
 		Common::EN_USA,
 		Common::kPlatformWindows,
-		ADGF_TESTING,
+		ADGF_UNSTABLE,
 		GUIO1(GAMEOPTION_EXTEND_SCREEN)
 	},
 	{
@@ -50,17 +45,12 @@ const ADGameDescription gameDescriptions[] = {
 		nullptr,
 		{
 			{"BOOTHS.BLT", 0, "c4d7ce39d1dd754a50bd909c1c2ed695", 1328201},
-			{"FRED.BLT",   0, "2491420b98c2421e19a751422c223cc8", 302133 },
-			{"GEORGE.BLT", 0, "b86116c9dc113d4bb7d6744d378c805c", 1017533},
-			{"HUCK.BLT",   0, "394407bb42a1f0abbe7db73fd0e9b873", 1225354},
 			{"SCOOBY.BLT", 0, "acf03553bc48c070adf57679307c900c", 3733392},
-			{"TOPCAT.BLT", 0, "116facccce1982111a1edf962bec3771", 567135 },
-			{"YOGI.BLT",   0, "ba3d148cbd10cd51f323be7895f52145", 5533358},
 			AD_LISTEND
 		},
 		Common::IT_ITA,
 		Common::kPlatformWindows,
-		ADGF_TESTING,
+		ADGF_UNSTABLE,
 		GUIO1(GAMEOPTION_EXTEND_SCREEN)
 	},
 	{
@@ -68,14 +58,12 @@ const ADGameDescription gameDescriptions[] = {
 		"Demo",
 		{
 			{"BOOTHS.BLT", 0, "3fc80c0feaaa300720a3ed921496233a", 878177 },
-			{"GEORGE.BLT", 0, "ca29738fe8655b9adec81aa48914c213", 388257 },
 			{"SCOOBY.BLT", 0, "4dfbdd87fdd2ec05fbdc7ae6435fb239", 677438 },
-			{"YOGI.BLT",   0, "06da5c2b889b5ecca371e99d8209347c", 5209352},
 			AD_LISTEND
 		},
 		Common::IT_ITA,
 		Common::kPlatformWindows,
-		ADGF_TESTING | ADGF_DEMO,
+		ADGF_UNSTABLE | ADGF_DEMO,
 		GUIO1(GAMEOPTION_EXTEND_SCREEN)
 	},
 


Commit: 8e4480acb29a6b615fe054e2c474ad1b9fb5221c
    https://github.com/scummvm/scummvm/commit/8e4480acb29a6b615fe054e2c474ad1b9fb5221c
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-04-07T01:11:14+02:00

Commit Message:
BOLT: Don't build the engine by default

Changed paths:
    engines/bolt/configure.engine


diff --git a/engines/bolt/configure.engine b/engines/bolt/configure.engine
index 8956855cc72..6629b5b25b9 100644
--- a/engines/bolt/configure.engine
+++ b/engines/bolt/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] [components]
-add_engine bolt "Bolt" yes "" "" "" ""
+add_engine bolt "Bolt" no "" "" "" ""


Commit: 14ceee75112491512c383547e23176bbf1b979b0
    https://github.com/scummvm/scummvm/commit/14ceee75112491512c383547e23176bbf1b979b0
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-04-07T01:11:14+02:00

Commit Message:
BOLT: Tidy up includes order

Changed paths:
    engines/bolt/bolt.cpp
    engines/bolt/booths/huck.cpp
    engines/bolt/booths/scooby.cpp
    engines/bolt/booths/yogi.cpp
    engines/bolt/metaengine.cpp
    engines/bolt/resource.cpp
    engines/bolt/rtf.cpp
    engines/bolt/utils.cpp
    engines/bolt/xplib/cursor.cpp
    engines/bolt/xplib/events.cpp
    engines/bolt/xplib/file.cpp
    engines/bolt/xplib/palette.cpp
    engines/bolt/xplib/sound.cpp
    engines/bolt/xplib/xplib.h


diff --git a/engines/bolt/bolt.cpp b/engines/bolt/bolt.cpp
index 54db77bf4d6..70b53368b7f 100644
--- a/engines/bolt/bolt.cpp
+++ b/engines/bolt/bolt.cpp
@@ -19,10 +19,6 @@
  *
  */
 
-#include "bolt/bolt.h"
-#include "bolt/detection.h"
-#include "bolt/xplib/xplib.h"
-
 #include "common/scummsys.h"
 #include "common/config-manager.h"
 #include "common/debug-channels.h"
@@ -31,6 +27,10 @@
 #include "engines/util.h"
 #include "graphics/paletteman.h"
 
+#include "bolt/bolt.h"
+#include "bolt/detection.h"
+#include "bolt/xplib/xplib.h"
+
 namespace Bolt {
 
 BoltEngine *g_engine;
diff --git a/engines/bolt/booths/huck.cpp b/engines/bolt/booths/huck.cpp
index 2da1115040c..28c6ab4649a 100644
--- a/engines/bolt/booths/huck.cpp
+++ b/engines/bolt/booths/huck.cpp
@@ -19,10 +19,10 @@
  *
  */
 
-#include "bolt/bolt.h"
-
 #include "common/memstream.h"
 
+#include "bolt/bolt.h"
+
 namespace Bolt {
 
 void BoltEngine::playSoundMapHuck(int16 memberId) {
diff --git a/engines/bolt/booths/scooby.cpp b/engines/bolt/booths/scooby.cpp
index 72a9177d43d..4f401fe8cda 100644
--- a/engines/bolt/booths/scooby.cpp
+++ b/engines/bolt/booths/scooby.cpp
@@ -19,10 +19,10 @@
  *
  */
 
-#include "bolt/bolt.h"
-
 #include "common/memstream.h"
 
+#include "bolt/bolt.h"
+
 namespace Bolt {
 
 bool BoltEngine::loadScoobyBaseAssets() {
diff --git a/engines/bolt/booths/yogi.cpp b/engines/bolt/booths/yogi.cpp
index 9bea7ab3faf..52c5dbe5423 100644
--- a/engines/bolt/booths/yogi.cpp
+++ b/engines/bolt/booths/yogi.cpp
@@ -19,10 +19,10 @@
  *
  */
 
-#include "bolt/bolt.h"
-
 #include "common/memstream.h"
 
+#include "bolt/bolt.h"
+
 namespace Bolt {
 
 void BoltEngine::playSoundMapYogi(int16 memberId) {
diff --git a/engines/bolt/metaengine.cpp b/engines/bolt/metaengine.cpp
index f43b7fe8e24..ef801c093fc 100644
--- a/engines/bolt/metaengine.cpp
+++ b/engines/bolt/metaengine.cpp
@@ -21,9 +21,9 @@
 
 #include "common/translation.h"
 
+#include "bolt/bolt.h"
 #include "bolt/metaengine.h"
 #include "bolt/detection.h"
-#include "bolt/bolt.h"
 
 namespace Bolt {
 
diff --git a/engines/bolt/resource.cpp b/engines/bolt/resource.cpp
index eb15aaaf516..7fe4d40bc70 100644
--- a/engines/bolt/resource.cpp
+++ b/engines/bolt/resource.cpp
@@ -19,10 +19,10 @@
  *
  */
 
-#include "bolt/bolt.h"
-
 #include "common/memstream.h"
 
+#include "bolt/bolt.h"
+
 namespace Bolt {
 
 void BoltEngine::decompress(byte *dest, uint32 decompSize, byte *src) {
diff --git a/engines/bolt/rtf.cpp b/engines/bolt/rtf.cpp
index 9b002e4f21e..b953548ceef 100644
--- a/engines/bolt/rtf.cpp
+++ b/engines/bolt/rtf.cpp
@@ -19,10 +19,10 @@
  *
  */
 
-#include "bolt/bolt.h"
-
 #include "common/memstream.h"
 
+#include "bolt/bolt.h"
+
 namespace Bolt {
 
 RTFResource *BoltEngine::openRTF(const char *fileName) {
diff --git a/engines/bolt/utils.cpp b/engines/bolt/utils.cpp
index 293d7531e6b..26eea1a881f 100644
--- a/engines/bolt/utils.cpp
+++ b/engines/bolt/utils.cpp
@@ -19,10 +19,10 @@
  *
  */
 
-#include "bolt/bolt.h"
-
 #include "common/config-manager.h"
 
+#include "bolt/bolt.h"
+
 namespace Bolt {
 
 void BoltEngine::displayColors(byte *palette, int16 page, int16 flags) {
diff --git a/engines/bolt/xplib/cursor.cpp b/engines/bolt/xplib/cursor.cpp
index 57cdc49ec3a..0398522eb8e 100644
--- a/engines/bolt/xplib/cursor.cpp
+++ b/engines/bolt/xplib/cursor.cpp
@@ -19,11 +19,11 @@
  *
  */
 
+#include "graphics/cursorman.h"
+
 #include "bolt/bolt.h"
 #include "bolt/xplib/xplib.h"
 
-#include "graphics/cursorman.h"
-
 namespace Bolt {
 
 bool XpLib::initCursor() {
diff --git a/engines/bolt/xplib/events.cpp b/engines/bolt/xplib/events.cpp
index 45b8060f6cc..f4917624b64 100644
--- a/engines/bolt/xplib/events.cpp
+++ b/engines/bolt/xplib/events.cpp
@@ -19,11 +19,11 @@
  *
  */
 
+#include "common/events.h"
+
 #include "bolt/bolt.h"
 #include "bolt/xplib/xplib.h"
 
-#include "common/events.h"
-
 namespace Bolt {
 
 bool XpLib::initEvents() {
diff --git a/engines/bolt/xplib/file.cpp b/engines/bolt/xplib/file.cpp
index 48dac0ec895..b3b1bbcef02 100644
--- a/engines/bolt/xplib/file.cpp
+++ b/engines/bolt/xplib/file.cpp
@@ -19,11 +19,11 @@
  *
  */
 
+#include "common/file.h"
+
 #include "bolt/bolt.h"
 #include "bolt/xplib/xplib.h"
 
-#include "common/file.h"
-
 namespace Bolt {
 
 void XpLib::fileError(const char *message) {
diff --git a/engines/bolt/xplib/palette.cpp b/engines/bolt/xplib/palette.cpp
index 4ebde565844..8c76aa1619b 100644
--- a/engines/bolt/xplib/palette.cpp
+++ b/engines/bolt/xplib/palette.cpp
@@ -19,11 +19,11 @@
  *
  */
 
+#include "common/timer.h"
+
 #include "bolt/bolt.h"
 #include "bolt/xplib/xplib.h"
 
-#include "common/timer.h"
-
 namespace Bolt {
 
 void XpLib::getPalette(int16 startIndex, int16 count, byte *destBuf) {
diff --git a/engines/bolt/xplib/sound.cpp b/engines/bolt/xplib/sound.cpp
index 0d0af282a8b..108b6d1f9f1 100644
--- a/engines/bolt/xplib/sound.cpp
+++ b/engines/bolt/xplib/sound.cpp
@@ -19,13 +19,13 @@
  *
  */
 
-#include "bolt/bolt.h"
-#include "bolt/xplib/xplib.h"
-
 #include "audio/audiostream.h"
 #include "audio/decoders/raw.h"
 #include "audio/mixer.h"
 
+#include "bolt/bolt.h"
+#include "bolt/xplib/xplib.h"
+
 namespace Bolt {
 
 bool XpLib::initSound() {
diff --git a/engines/bolt/xplib/xplib.h b/engines/bolt/xplib/xplib.h
index 0bdf0765809..ac42467a819 100644
--- a/engines/bolt/xplib/xplib.h
+++ b/engines/bolt/xplib/xplib.h
@@ -22,8 +22,6 @@
 #ifndef XPLIB_XPLIB_H
 #define XPLIB_XPLIB_H
 
-#include "bolt/bolt.h"
-
 #include "audio/audiostream.h"
 #include "audio/mixer.h"
 
@@ -33,6 +31,8 @@
 
 #include "graphics/paletteman.h"
 
+#include "bolt/bolt.h"
+
 namespace Bolt {
 
 // RENDER FLAGS


Commit: 6bab26725ee6faf5ed1c005af1e364624459993a
    https://github.com/scummvm/scummvm/commit/6bab26725ee6faf5ed1c005af1e364624459993a
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-04-07T01:11:14+02:00

Commit Message:
BOLT: Address more review comments

Changed paths:
    engines/bolt/booths/fred.cpp
    engines/bolt/booths/george.cpp
    engines/bolt/booths/scooby.cpp
    engines/bolt/xplib/events.cpp


diff --git a/engines/bolt/booths/fred.cpp b/engines/bolt/booths/fred.cpp
index dd2158b1b78..0bba92b44ce 100644
--- a/engines/bolt/booths/fred.cpp
+++ b/engines/bolt/booths/fred.cpp
@@ -101,7 +101,7 @@ bool BoltEngine::initFred() {
 		_fredLevelIndex++;
 	}
 
-	// Flush timer events...
+	// Flush non-timer events...
 	uint32 dummy;
 	while (_xp->getEvent(etTimer, &dummy) != etTimer);
 
@@ -381,7 +381,7 @@ int16 BoltEngine::playFred() {
 		// Process events...
 		int16 eventType;
 		uint32 eventData;
-		while ((eventType = _xp->getEvent(0, &eventData)) != 0) {
+		while ((eventType = _xp->getEvent(etEmpty, &eventData)) != etEmpty) {
 			switch (eventType) {
 			case etJoystick:
 				joystickX = (int16)(eventData >> 16);
@@ -790,7 +790,7 @@ int16 BoltEngine::helpFred() {
 
 		// Process events...
 		uint32 eventData;
-		int16 eventType = _xp->getEvent(0, &eventData);
+		int16 eventType = _xp->getEvent(etEmpty, &eventData);
 
 		switch (eventType) {
 		case etTimer: {
diff --git a/engines/bolt/booths/george.cpp b/engines/bolt/booths/george.cpp
index 12f816bcd8f..c4e21972bf2 100644
--- a/engines/bolt/booths/george.cpp
+++ b/engines/bolt/booths/george.cpp
@@ -85,7 +85,7 @@ bool BoltEngine::initGeorge() {
 		_georgeCollectedSatellitesNum++;
 	}
 
-	// Drain timer events...
+	// Drain non-timer events...
 	uint32 dummy;
 	while (_xp->getEvent(etTimer, &dummy) != etTimer);
 
@@ -855,8 +855,8 @@ int16 BoltEngine::helpGeorge() {
 
 			_georgeHelpTimer = _xp->startTimer(500);
 			
-				bool lit = !(READ_UINT32(_georgePrevActiveHelpObject + 0x08) & 1u);
-				hiliteGeorgeHelpObject(_georgePrevActiveHelpObject, lit ? 1 : 0);
+			bool lit = !(READ_UINT32(_georgePrevActiveHelpObject + 0x08) & 1u);
+			hiliteGeorgeHelpObject(_georgePrevActiveHelpObject, lit ? 1 : 0);
 			
 			break;
 		}
diff --git a/engines/bolt/booths/scooby.cpp b/engines/bolt/booths/scooby.cpp
index 4f401fe8cda..0d126965e52 100644
--- a/engines/bolt/booths/scooby.cpp
+++ b/engines/bolt/booths/scooby.cpp
@@ -274,7 +274,7 @@ void BoltEngine::initScoobyLevelGraphics() {
 	_scoobyBgPic = memberAddr(_scoobyBoltLib, picMember);
 	clearPictMSb(_scoobyBgPic);
 
-	// Drain timer events
+	// Drain non-timer events
 	uint32 dummy;
 	while (_xp->getEvent(etTimer, &dummy) != etTimer);
 
@@ -1666,7 +1666,7 @@ int16 BoltEngine::helpScooby() {
 		}
 
 		uint32 eventData;
-		int16 eventType = _xp->getEvent(0, &eventData);
+		int16 eventType = _xp->getEvent(etEmpty, &eventData);
 
 		switch (eventType) {
 		case etTimer: {
diff --git a/engines/bolt/xplib/events.cpp b/engines/bolt/xplib/events.cpp
index f4917624b64..eae1ee3562b 100644
--- a/engines/bolt/xplib/events.cpp
+++ b/engines/bolt/xplib/events.cpp
@@ -58,7 +58,7 @@ int16 XpLib::getEvent(int16 filter, uint32 *outData, byte **outPtrData) {
 
 	// Search event queue for matching event type...
 	XPEvent *node = _eventQueueHead;
-	if (filter != 0) {
+	if (filter != etEmpty) {
 		while (node) {
 			if (node->type == filter)
 				break;
@@ -89,7 +89,7 @@ int16 XpLib::peekEvent(int16 filter, uint32 *outData, byte **outPtrData) {
 	pumpMessages();
 
 	XPEvent *node = _eventQueueHead;
-	if (filter != 0) {
+	if (filter != etEmpty) {
 		while (node) {
 			if (node->type == filter)
 				break;
@@ -99,7 +99,7 @@ int16 XpLib::peekEvent(int16 filter, uint32 *outData, byte **outPtrData) {
 	}
 
 	if (!node)
-		return 0;
+		return etEmpty;
 
 	// Copy data but don't remove from queue...
 	*outData = node->payload;




More information about the Scummvm-git-logs mailing list