[Scummvm-git-logs] scummvm master -> a50d09aa31c1b1a17a07bce369f4f416f7f4c546

dreammaster noreply at scummvm.org
Wed Apr 13 03:35:38 UTC 2022


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

Summary:
49e26ac8bd GLK: Add typedef for glsi32.
7188ce034b GLK: SCOTT: Update scott.
7b97270214 GLK: SCOTT: Add game command parser.
fb7180e379 GLK: SCOTT: Add support for .z80 game files.
6d6d4fd434 GLK: SCOTT: Support games with compressed text data.
abecb9a388 GLK: SCOTT: Add game detection and loading for ZX Spectrum games.
4b31312480 GLK: SCOTT: Add support for different drawing methods.
5a4bda94a9 GLK: SCOTT: Add ring buffer for drawing methods.
f9521be168 GLK: SCOTT: Game specific info to load game data.
855e9b1ae6 GLK: SCOTT: Various enums for game data.
9cae726fca GLK: SCOTT: Support .z80 files.
f994bd2859 GLK: SCOTT: Add detection for 11 Mysterious Adventures.
faa4ae472d GLK: SCOTT: Class for holding global data.
7f84f25804 GLK: SCOTT: Add Questprobe: The Hulk specific functions.
a864ce872f GLK: SCOTT: Add text formatting.
ef36219245 GLK: SCOTT: Add support for saving and loading game state.
72dc87f4b4 GLK: Add new source files.
8b846ec0dd GLK: SCOTT: Import size_t.
a7c29553f7 GLK: SCOTT: Typedefs for used types.
67adecb2d7 GLK: SCOTT: Remove standard library includes.
a50d09aa31 GLK: SCOTT: Define SIZE_MAX.


Commit: 49e26ac8bdeb2bac461bed20bdc204eab3803180
    https://github.com/scummvm/scummvm/commit/49e26ac8bdeb2bac461bed20bdc204eab3803180
Author: Avijeet (am388488 at gmail.com)
Date: 2022-04-12T20:35:24-07:00

Commit Message:
GLK: Add typedef for glsi32.

Changed paths:
    engines/glk/glk_types.h


diff --git a/engines/glk/glk_types.h b/engines/glk/glk_types.h
index e824268edbf..11fcdb1723a 100644
--- a/engines/glk/glk_types.h
+++ b/engines/glk/glk_types.h
@@ -251,6 +251,8 @@ typedef gidispatch_intconst_struct gidispatch_intconst_t;
 
 typedef uint32 glui32;
 
+typedef int32 glsi32;
+
 } // End of namespace Glk
 
 #endif


Commit: 7188ce034b50a96ee42a85091c0f373feee73534
    https://github.com/scummvm/scummvm/commit/7188ce034b50a96ee42a85091c0f373feee73534
Author: Avijeet (am388488 at gmail.com)
Date: 2022-04-12T20:35:24-07:00

Commit Message:
GLK: SCOTT: Update scott.

Changed paths:
    engines/glk/scott/scott.cpp
    engines/glk/scott/scott.h


diff --git a/engines/glk/scott/scott.cpp b/engines/glk/scott/scott.cpp
index 8e1cd56259c..b1f08c4a51c 100644
--- a/engines/glk/scott/scott.cpp
+++ b/engines/glk/scott/scott.cpp
@@ -20,122 +20,178 @@
  */
 
 #include "glk/scott/scott.h"
-#include "glk/quetzal.h"
 #include "common/config-manager.h"
 #include "common/translation.h"
 #include "common/ustr.h"
+#include "glk/quetzal.h"
+#include "glk/scott/command_parser.h"
+#include "glk/scott/definitions.h"
+#include "glk/scott/detect_game.h"
+#include "glk/scott/game_info.h"
+#include "glk/scott/globals.h"
+#include "glk/scott/hulk.h"
+#include "glk/scott/layout_text.h"
+#include "glk/scott/line_drawing.h"
+#include "glk/scott/saga_draw.h"
+#include "glk/scott/restore_state.h"
 
 namespace Glk {
 namespace Scott {
 
-Scott::Scott(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gameDesc),
-		_currentCounter(0), _savedRoom(0), _options(0), _width(0), _topHeight(0), _splitScreen(true),
-		_bottomWindow(nullptr), _topWindow(nullptr), _bitFlags(0), _saveSlot(-1) {
+Scott *g_scott;
+
+Scott::Scott(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gameDesc) {
+	g_scott = this;
 	Common::fill(&_nounText[0], &_nounText[16], '\0');
-	Common::fill(&_counters[0], &_counters[16], 0);
-	Common::fill(&_roomSaved[0], &_roomSaved[16], 0);
 }
 
 void Scott::runGame() {
 	int vb, no;
 	initialize();
 
-	_bottomWindow = glk_window_open(nullptr, 0, 0, wintype_TextBuffer, 1);
-	if (_bottomWindow == nullptr) {
+	glk_stylehint_set(wintype_TextBuffer, style_User1, stylehint_Proportional, 0);
+	glk_stylehint_set(wintype_TextBuffer, style_User1, stylehint_Indentation, 20);
+	glk_stylehint_set(wintype_TextBuffer, style_User1, stylehint_ParaIndentation, 20);
+	glk_stylehint_set(wintype_TextBuffer, style_Preformatted, stylehint_Justification, stylehint_just_Centered);
+
+	_G(_bottomWindow) = glk_window_open(0, 0, 0, wintype_TextBuffer, GLK_BUFFER_ROCK);
+	if (_G(_bottomWindow) == nullptr)
 		glk_exit();
-		return;
+	glk_set_window(_G(_bottomWindow));
+
+	for (int i = 0; i < MAX_SYSMESS; i++) {
+		_G(_sys)[i] = g_sysDict[i];
+	}
+
+	const char **dictpointer;
+
+	if (_options & YOUARE)
+		dictpointer = g_sysDict;
+	else
+		dictpointer = g_sysDictIAm;
+
+	for (int i = 0; i < MAX_SYSMESS && dictpointer[i] != nullptr; i++) {
+		_G(_sys)[i] = dictpointer[i];
+	}
+
+	GameIDType gameType = detectGame(&_gameFile);
+
+	if (gameType == SCOTTFREE)
+		loadDatabase(&_gameFile, (_options & DEBUGGING) ? 1 : 0);
+
+	if (!gameType)
+		fatal("Unsupported game!");
+
+	if (gameType != SCOTTFREE && gameType != TI994A) {
+		_options |= SPECTRUM_STYLE;
+		_splitScreen = 1;
+	} else {
+		if (gameType != TI994A)
+			_options |= TRS80_STYLE;
+		_splitScreen = 1;
+	}
+
+	if (!_titleScreen.empty()) {
+		if (_splitScreen)
+			printTitleScreenGrid();
+		else
+			printTitleScreenBuffer();
 	}
-	glk_set_window(_bottomWindow);
 
 	if (_options & TRS80_STYLE) {
-		_width = 64;
+		_topWidth = 64;
 		_topHeight = 11;
 	} else {
-		_width = 80;
+		_topWidth = 80;
 		_topHeight = 10;
 	}
 
-	if (_splitScreen) {
-		_topWindow = glk_window_open(_bottomWindow, winmethod_Above | winmethod_Fixed, _topHeight, wintype_TextGrid, 0);
-		if (_topWindow == nullptr) {
-			_splitScreen = 0;
-			_topWindow = _bottomWindow;
-		}
-	} else {
-		_topWindow = _bottomWindow;
+	if (CURRENT_GAME == TI994A) {
+		display(_G(_bottomWindow), "In this adventure, you may abbreviate any word \
+				by typing its first %d letters, and directions by typing \
+				one letter.\n\nDo you want to restore previously saved game?\n",
+				_G(_gameHeader)->_wordLength);
+		if (yesOrNo())
+			loadGame();
+		clearScreen();
 	}
 
-	output("ScummVM support adapted from Scott Free, A Scott Adams game driver in C.\n\n");
+	openTopWindow();
+
+	if (gameType == SCOTTFREE)
+		output("ScummVM support adapted from Scott Free, A Scott Adams game driver in C.\n\n");
 
 	// Check for savegame
 	_saveSlot = ConfMan.hasKey("save_slot") ? ConfMan.getInt("save_slot") : -1;
 
-	// Load the game
-	loadDatabase(&_gameFile, (_options & DEBUGGING) ? 1 : 0);
+	_G(_initialState) = saveCurrentState();
 
-	// Main game loop
 	while (!shouldQuit()) {
 		glk_tick();
 
-		performActions(0, 0);
+		if (_G(_shouldRestart))
+			restartGame();
+
+		if (!_G(_stopTime))
+			performActions(0, 0);
+
 		if (shouldQuit())
 			break;
 
-		if (_saveSlot >= 0) {
-			// Load any savegame during startup
-			loadGameState(_saveSlot);
-			_saveSlot = -1;
+		if (!(_G(_currentCommand) && _G(_currentCommand)->_allFlag && !(_G(_currentCommand)->_allFlag & LASTALL))) {
+			_printLookToTranscript = _shouldLookInTranscript;
+			look();
+			_printLookToTranscript = _shouldLookInTranscript = 0;
+			if (!_G(_stopTime) && !_G(_shouldRestart))
+				saveUndo();
 		}
 
-		look();
+		if (_G(_shouldRestart))
+			continue;
 
-		if (getInput(&vb, &no) == -1)
+		if (getInput(&vb, &no) == 1)
 			continue;
 		if (g_vm->shouldQuit())
 			break;
 
 		switch (performActions(vb, no)) {
-		case -1:
-			output(_("I don't understand your command. "));
+		case ER_RAN_ALL_LINES_NO_MATCH:
+			if (!recheckForExtraCommand()) {
+				output(_G(_sys)[I_DONT_UNDERSTAND]);
+				freeCommands();
+			}
 			break;
-		case -2:
-			output(_("I can't do that yet. "));
+		case ER_RAN_ALL_LINES:
+			output(_G(_sys)[YOU_CANT_DO_THAT_YET]);
+			freeCommands();
 			break;
 		default:
-			break;
+			_G(_justStarted) = 0;
 		}
-		if (shouldQuit())
-			return;
-
-		// Brian Howarth games seem to use -1 for forever
-		if (_items[LIGHT_SOURCE]._location != DESTROYED && _gameHeader._lightTime != -1) {
-			_gameHeader._lightTime--;
-			if (_gameHeader._lightTime < 1) {
-				_bitFlags |= (1 << LIGHTOUTBIT);
-				if (_items[LIGHT_SOURCE]._location == CARRIED ||
-						_items[LIGHT_SOURCE]._location == MY_LOC) {
-					if (_options & SCOTTLIGHT)
-						output(_("Light has run out! "));
-					else
-						output(_("Your light has run out. "));
+
+		/* Brian Howarth games seem to use -1 for forever */
+		if (_G(_items)[LIGHT_SOURCE]._location != DESTROYED && _G(_gameHeader)->_lightTime != -1 && !_G(_stopTime)) {
+			_G(_gameHeader)->_lightTime--;
+			if (_G(_gameHeader)->_lightTime < 1) {
+				_G(_bitFlags) |= (1 << LIGHTOUTBIT);
+				if (_G(_items)[LIGHT_SOURCE]._location == CARRIED || _G(_items)[LIGHT_SOURCE]._location == MY_LOC) {
+					output(_G(_sys)[LIGHT_HAS_RUN_OUT]);
 				}
-				if (_options & PREHISTORIC_LAMP)
-					_items[LIGHT_SOURCE]._location = DESTROYED;
-			} else if (_gameHeader._lightTime < 25) {
-				if (_items[LIGHT_SOURCE]._location == CARRIED ||
-						_items[LIGHT_SOURCE]._location == MY_LOC) {
-
-					if (_options & SCOTTLIGHT) {
-						output(_("Light runs out in "));
-						outputNumber(_gameHeader._lightTime);
-						output(_(" turns. "));
+				if ((_options & PREHISTORIC_LAMP) || (_G(_game)->_subType & MYSTERIOUS) || CURRENT_GAME == TI994A)
+					_G(_items)[LIGHT_SOURCE]._location = DESTROYED;
+			} else if (_G(_gameHeader)->_lightTime < 25) {
+				if (_G(_items)[LIGHT_SOURCE]._location == CARRIED || _G(_items)[LIGHT_SOURCE]._location == MY_LOC) {
+					if ((_options & SCOTTLIGHT) || (_G(_game)->_subType & MYSTERIOUS)) {
+						display(_G(_bottomWindow), "%s %d %s\n", _G(_sys)[LIGHT_RUNS_OUT_IN].c_str(), _G(_gameHeader)->_lightTime, _G(_sys)[TURNS].c_str());
 					} else {
-						if (_gameHeader._lightTime % 5 == 0)
-							output(_("Your light is growing dim. "));
+						if (_G(_gameHeader)->_lightTime % 5 == 0)
+							output(_G(_sys)[LIGHT_GROWING_DIM].c_str());
 					}
 				}
 			}
 		}
+		if (_G(_stopTime))
+			_G(_stopTime)--;
 	}
 }
 
@@ -164,6 +220,8 @@ void Scott::display(winid_t w, const char *fmt, ...) {
 	va_end(ap);
 
 	glk_put_string_stream(glk_window_get_stream(w), msg.c_str());
+	if (_G(_transcript))
+		glk_put_string_stream(_G(_transcript), msg.c_str());
 }
 
 void Scott::display(winid_t w, const Common::U32String fmt, ...) {
@@ -176,29 +234,103 @@ void Scott::display(winid_t w, const Common::U32String fmt, ...) {
 	va_end(ap);
 
 	glk_put_string_stream_uni(glk_window_get_stream(w), msg.u32_str());
+	if (_G(_transcript))
+		glk_put_string_stream_uni(_G(_transcript), msg.u32_str());
+}
+
+void Scott::updateSettings() {
+	if (drawingVector())
+		glk_request_timer_events(20);
+
+	PaletteType previousPal = _G(_palChosen);
+	if (_options & FORCE_PALETTE_ZX)
+		_G(_palChosen) = ZXOPT;
+	else if (_options & FORCE_PALETTE_C64) {
+		if (_G(_game)->_pictureFormatVersion == 99)
+			_G(_palChosen) = C64A;
+		else
+			_G(_palChosen) = C64B;
+	} else
+		_G(_palChosen) = _G(_game)->_palette;
+	if (_G(_palChosen) != previousPal) {
+		definePalette();
+		if (_G(_vectorState) != NO_VECTOR_IMAGE)
+			drawSomeVectorPixels(1);
+	}
+}
+
+void Scott::updates(event_t ev) {
+	if (ev.type == evtype_Arrange) {
+		updateSettings();
+
+		_G(_vectorState) = NO_VECTOR_IMAGE;
+
+		closeGraphicsWindow();
+		openGraphicsWindow();
+
+		if (_splitScreen) {
+			look();
+		}
+	} else if (ev.type == evtype_Timer) {
+		switch (_G(_game->_type)) {
+		case SHERWOOD_VARIANT:
+			// TODO
+			// UpdateRobinOfSherwoodAnimations();
+			break;
+		case GREMLINS_VARIANT:
+			// TODO
+			// UpdateGremlinsAnimations();
+			break;
+		case SECRET_MISSION_VARIANT:
+			// TODO
+			// UpdateSecretAnimations();
+			break;
+		default:
+			if (_G(_game)->_pictureFormatVersion == 99 && drawingVector())
+				drawSomeVectorPixels((_G(_vectorState) == NO_VECTOR_IMAGE));
+			break;
+		}
+	}
 }
 
 void Scott::delay(int seconds) {
+	if (_options & NO_DELAYS)
+		return;
+
 	event_t ev;
 
 	if (!glk_gestalt(gestalt_Timer, 0))
 		return;
 
+	glk_request_char_event(_G(_bottomWindow));
+	glk_cancel_char_event(_G(_bottomWindow));
+
+	if (drawingVector()) {
+		do {
+			glk_select(&ev);
+			updates(ev);
+		} while (drawingVector());
+		if (_G(_gliSlowDraw))
+			seconds = 0.5;
+	}
+
 	glk_request_timer_events(1000 * seconds);
 
 	do {
 		glk_select(&ev);
-	} while (ev.type != evtype_Timer && ev.type != evtype_Quit);
+		updates(ev);
+	} while (ev.type != evtype_Timer);
 
 	glk_request_timer_events(0);
 }
 
 void Scott::fatal(const char *x) {
-	error("%s", x);
+	display(_G(_bottomWindow), "%s\n", x);
+	cleanupAndExit();
 }
 
 void Scott::clearScreen(void) {
-	glk_window_clear(_bottomWindow);
+	glk_window_clear(_G(_bottomWindow));
 }
 
 bool Scott::randomPercent(uint n) {
@@ -208,41 +340,41 @@ bool Scott::randomPercent(uint n) {
 int Scott::countCarried(void) {
 	int ct = 0;
 	int n = 0;
-	while (ct <= _gameHeader._numItems) {
-		if (_items[ct]._location == CARRIED)
+	while (ct <= _G(_gameHeader)->_numItems) {
+		if (_G(_items)[ct]._location == CARRIED)
 			n++;
 		ct++;
 	}
 	return n;
 }
 
-const char *Scott::mapSynonym(const char *word) {
+const char *Scott::mapSynonym(int noun) {
 	int n = 1;
 	const char *tp;
-	static char lastword[16];   // Last non synonym
-	while (n <= _gameHeader._numWords) {
-		tp = _nouns[n].c_str();
+	static char lastword[16]; // Last non synonym
+	while (n <= _G(_gameHeader)->_numWords) {
+		tp = _G(_nouns)[n].c_str();
 		if (*tp == '*')
 			tp++;
 		else
 			strcpy(lastword, tp);
-		if (scumm_strnicmp(word, tp, _gameHeader._wordLength) == 0)
+		if (n == noun)
 			return lastword;
 		n++;
 	}
 	return nullptr;
 }
 
-int Scott::matchUpItem(const char *text, int loc) {
-	const char *word = mapSynonym(text);
+int Scott::matchUpItem(int noun, int loc) {
+	const char *word = mapSynonym(noun);
 	int ct = 0;
 
 	if (word == nullptr)
-		word = text;
+		word = _G(_nouns)[noun].c_str();
 
-	while (ct <= _gameHeader._numItems) {
-		if (!_items[ct]._autoGet.empty() && _items[ct]._location == loc &&
-				scumm_strnicmp(_items[ct]._autoGet.c_str(), word, _gameHeader._wordLength) == 0)
+	while (ct <= _G(_gameHeader)->_numItems) {
+		if (!_G(_items)[ct]._autoGet.empty() && _G(_items)[ct]._location == loc &&
+			scumm_strnicmp(_G(_items)[ct]._autoGet.c_str(), word, _G(_gameHeader)->_wordLength) == 0)
 			return ct;
 		ct++;
 	}
@@ -302,61 +434,61 @@ void Scott::loadDatabase(Common::SeekableReadStream *f, bool loud) {
 	// Load the header
 	readInts(f, 12, &unused, &ni, &na, &nw, &nr, &mc, &pr, &tr, &wl, &lt, &mn, &trm);
 
-	_gameHeader._numItems = ni;
-	_items.resize(ni + 1);
-	_gameHeader._numActions = na;
-	_actions.resize(na + 1);
-	_gameHeader._numWords = nw;
-	_gameHeader._wordLength = wl;
-	_verbs.resize(nw + 1);
-	_nouns.resize(nw + 1);
-	_gameHeader._numRooms = nr;
-	_rooms.resize(nr + 1);
-	_gameHeader._maxCarry = mc;
-	_gameHeader._playerRoom = pr;
-	_gameHeader._treasures = tr;
-	_gameHeader._lightTime = lt;
-	_lightRefill = lt;
-	_gameHeader._numMessages = mn;
-	_messages.resize(mn + 1);
-	_gameHeader._treasureRoom = trm;
+	_G(_gameHeader)->_numItems = ni;
+	_G(_items).resize(ni + 1);
+	_G(_gameHeader)->_numActions = na;
+	_G(_actions).resize(na + 1);
+	_G(_gameHeader)->_numWords = nw;
+	_G(_gameHeader)->_wordLength = wl;
+	_G(_verbs).resize(nw + 1);
+	_G(_nouns).resize(nw + 1);
+	_G(_gameHeader)->_numRooms = nr;
+	_G(_rooms).resize(nr + 1);
+	_G(_gameHeader)->_maxCarry = mc;
+	_G(_gameHeader)->_playerRoom = pr;
+	_G(_gameHeader)->_treasures = tr;
+	_G(_gameHeader)->_lightTime = lt;
+	_G(_lightRefill) = lt;
+	_G(_gameHeader)->_numMessages = mn;
+	_G(_messages).resize(mn + 1);
+	_G(_gameHeader)->_treasureRoom = trm;
 
 	// Load the actions
 	if (loud)
 		debug("Reading %d actions.", na);
 
 	for (int idx = 0; idx < na + 1; ++idx) {
-		Action &a = _actions[idx];
+		Action &a = _G(_actions)[idx];
 		readInts(f, 8,
-			&a._vocab, &a._condition[0], &a._condition[1], &a._condition[2],
-			&a._condition[3], &a._condition[4], &a._action[0], &a._action[1]);
+				 &a._vocab, &a._condition[0], &a._condition[1], &a._condition[2],
+				 &a._condition[3], &a._condition[4], &a._action[0], &a._action[1]);
 	}
 
 	if (loud)
 		debug("Reading %d word pairs.", nw);
 	for (int idx = 0; idx < nw + 1; ++idx) {
-		_verbs[idx] = readString(f);
-		_nouns[idx] = readString(f);
+		_G(_verbs)[idx] = readString(f);
+		_G(_nouns)[idx] = readString(f);
 	}
 
 	if (loud)
 		debug("Reading %d rooms.", nr);
 	for (int idx = 0; idx < nr + 1; ++idx) {
-		Room &r = _rooms[idx];
+		Room &r = _G(_rooms)[idx];
 		readInts(f, 6, &r._exits[0], &r._exits[1], &r._exits[2],
 				 &r._exits[3], &r._exits[4], &r._exits[5]);
-		r._text =  readString(f);
+		r._text = readString(f);
 	}
 
 	if (loud)
 		debug("Reading %d messages.", mn);
 	for (int idx = 0; idx < mn + 1; ++idx)
-		_messages[idx] = readString(f);
+		_G(_messages)[idx] = readString(f);
 
 	if (loud)
 		debug("Reading %d items.", ni);
 	for (int idx = 0; idx < ni + 1; ++idx) {
-		Item &i = _items[idx];
+		Item &i = _G(_items)[idx];
 		i._text = readString(f);
 
 		const char *p = strchr(i._text.c_str(), '/');
@@ -369,8 +501,9 @@ void Scott::loadDatabase(Common::SeekableReadStream *f, bool loud) {
 				i._autoGet.deleteChar(0);
 
 				const char *t = strchr(i._autoGet.c_str(), '/');
-				if (t)
+				if (t) {
 					i._autoGet = Common::String(i._autoGet.c_str(), t);
+				}
 			}
 		}
 
@@ -394,155 +527,134 @@ void Scott::loadDatabase(Common::SeekableReadStream *f, bool loud) {
 
 void Scott::output(const Common::String &a) {
 	if (_saveSlot == -1)
-		display(_bottomWindow, "%s", a.c_str());
+		display(_G(_bottomWindow), "%s", a.c_str());
 }
 
 void Scott::output(const Common::U32String &a) {
 	if (_saveSlot == -1)
-		display(_bottomWindow, Common::U32String("%S"), a.c_str());
+		display(_G(_bottomWindow), Common::U32String("%S"), a.c_str());
 }
 
 void Scott::outputNumber(int a) {
-	display(_bottomWindow, "%d", a);
+	display(_G(_bottomWindow), "%d", a);
 }
 
 void Scott::look(void) {
-	const char *const ExitNames[6] = {
-		_s("North"), _s("South"), _s("East"), _s("West"), _s("Up"), _s("Down")
-	};
+	drawRoomImage();
+
+	if (_splitScreen && _G(_topWindow) == nullptr)
+		return;
+
+	char *buf = new char[1000];
+	buf = static_cast<char *>(memset(buf, 0, 1000));
+	_roomDescriptionStream = glk_stream_open_memory(buf, 1000, filemode_Write, 0);
+
 	Room *r;
 	int ct, f;
-	int pos;
 
-	if (_splitScreen)
-		glk_window_clear(_topWindow);
+	if (!_splitScreen) {
+		writeToRoomDescriptionStream("\n");
+	} else if (_G(_transcript) && _printLookToTranscript) {
+		glk_put_char_stream_uni(_G(_transcript), 10);
+	}
 
-	if ((_bitFlags & (1 << DARKBIT)) && _items[LIGHT_SOURCE]._location != CARRIED
-			&& _items[LIGHT_SOURCE]._location != MY_LOC) {
-		if (_options & YOUARE)
-			display(_topWindow, _("You can't see. It is too dark!\n"));
-		else
-			display(_topWindow, _("I can't see. It is too dark!\n"));
-		if (_options & TRS80_STYLE)
-			display(_topWindow, TRS80_LINE);
+	if ((_G(_bitFlags) & (1 << DARKBIT)) && _G(_items)[LIGHT_SOURCE]._location != CARRIED && _G(_items)[LIGHT_SOURCE]._location != MY_LOC) {
+		writeToRoomDescriptionStream("%s", _G(_sys)[TOO_DARK_TO_SEE].c_str());
+		flushRoomDescription(buf);
 		return;
 	}
-	r = &_rooms[MY_LOC];
+
+	r = &_G(_rooms)[MY_LOC];
+
+	if (r->_text.empty())
+		return;
+
 	if (r->_text.hasPrefix("*"))
-		display(_topWindow, "%s\n", r->_text.c_str() + 1);
+		writeToRoomDescriptionStream("%s", r->_text.substr(1).c_str());
 	else {
-		if (_options & YOUARE)
-			display(_topWindow, _("You are in a %s\n"), r->_text.c_str());
-		else
-			display(_topWindow, _("I'm in a %s\n"), r->_text.c_str());
+		writeToRoomDescriptionStream("%s%s", _G(_sys)[YOU_ARE].c_str(), r->_text.c_str());
 	}
 
-	ct = 0;
-	f = 0;
-	display(_topWindow, _("\nObvious exits: "));
-	while (ct < 6) {
-		if (r->_exits[ct] != 0) {
-			if (f == 0)
-				f = 1;
-			else
-				display(_topWindow, ", ");
-			display(_topWindow, Common::U32String("%S"), _(ExitNames[ct]).c_str());
-		}
-		ct++;
+	if (!(_options & SPECTRUM_STYLE)) {
+		listExits();
+		writeToRoomDescriptionStream(".\n");
 	}
 
-	if (f == 0)
-		display(_topWindow, _("none"));
-	display(_topWindow, ".\n");
 	ct = 0;
 	f = 0;
-	pos = 0;
-	while (ct <= _gameHeader._numItems) {
-		if (_items[ct]._location == MY_LOC) {
+	while (ct <= _G(_gameHeader)->_numItems) {
+		if (_G(_items)[ct]._location == MY_LOC) {
+			if (_G(_items)[ct]._text[0] == 0) {
+				error("Invisible item in room: %d\n", ct);
+				ct++;
+				continue;
+			}
 			if (f == 0) {
-				if (_options & YOUARE) {
-					display(_topWindow, _("\nYou can also see: "));
-					pos = 18;
-				} else {
-					display(_topWindow, _("\nI can also see: "));
-					pos = 16;
-				}
+				writeToRoomDescriptionStream("%s", _G(_sys)[YOU_SEE].c_str());
 				f++;
-			} else if (!(_options & TRS80_STYLE)) {
-				display(_topWindow, " - ");
-				pos += 3;
-			}
-			if (pos + (int)_items[ct]._text.size() > (_width - 10)) {
-				pos = 0;
-				display(_topWindow, "\n");
+				if (_options & SPECTRUM_STYLE)
+					writeToRoomDescriptionStream("\n");
+			} else if (!(_options & (TRS80_STYLE | SPECTRUM_STYLE))) {
+				writeToRoomDescriptionStream("%s", _G(_sys)[ITEM_DELIMITER].c_str());
 			}
-			display(_topWindow, "%s", _items[ct]._text.c_str());
-			pos += _items[ct]._text.size();
-			if (_options & TRS80_STYLE) {
-				display(_topWindow, ". ");
-				pos += 2;
+			writeToRoomDescriptionStream("%s", _G(_items)[ct]._text.c_str());
+			if (_options & (TRS80_STYLE | SPECTRUM_STYLE)) {
+				writeToRoomDescriptionStream("%s", _G(_sys)[ITEM_DELIMITER].c_str());
 			}
 		}
 		ct++;
 	}
 
-	display(_topWindow, "\n");
-	if (_options & TRS80_STYLE)
-		display(_topWindow, TRS80_LINE);
+	if ((_options & TI994A_STYLE) && f) {
+		writeToRoomDescriptionStream("%s", ".");
+	}
+
+	if (_options & SPECTRUM_STYLE) {
+		listExitsSpectrumStyle();
+	} else if (f) {
+		writeToRoomDescriptionStream("\n");
+	}
+
+	if ((_G(_autoInventory) || (_options & FORCE_INVENTORY)) && !(_options & FORCE_INVENTORY_OFF))
+		listInventoryInUpperWindow();
+
+	flushRoomDescription(buf);
 }
 
 int Scott::whichWord(const char *word, const Common::StringArray &list) {
 	int n = 1;
 	int ne = 1;
 	const char *tp;
-	while (ne <= _gameHeader._numWords) {
+	while (ne <= _G(_gameHeader)->_numWords) {
 		tp = list[ne].c_str();
 		if (*tp == '*')
 			tp++;
 		else
 			n = ne;
-		if (scumm_strnicmp(word, tp, _gameHeader._wordLength) == 0)
+		if (scumm_strnicmp(word, tp, _G(_gameHeader)->_wordLength) == 0)
 			return n;
 		ne++;
 	}
 	return -1;
 }
 
-void Scott::lineInput(char *buf, size_t n) {
-	event_t ev;
-
-	glk_request_line_event(_bottomWindow, buf, n - 1, 0);
-
-	do {
-		glk_select(&ev);
-		if (ev.type == evtype_Quit)
-			return;
-		else if (ev.type == evtype_LineInput)
-			break;
-		else if (ev.type == evtype_Arrange && _splitScreen)
-			look();
-	} while (ev.type != evtype_Quit);
-
-	buf[ev.val1] = 0;
-}
-
 Common::Error Scott::writeGameData(Common::WriteStream *ws) {
 	Common::String msg;
 
 	for (int ct = 0; ct < 16; ct++) {
-		msg = Common::String::format("%d %d\n", _counters[ct], _roomSaved[ct]);
+		msg = Common::String::format("%d %d\n", _G(_counters)[ct], _G(_roomSaved)[ct]);
 		ws->write(msg.c_str(), msg.size());
 		ws->writeByte(0);
 	}
 
-	msg = Common::String::format("%u %d %d %d %d %d\n",
-								 _bitFlags, (_bitFlags & (1 << DARKBIT)) ? 1 : 0,
-								 MY_LOC, _currentCounter, _savedRoom, _gameHeader._lightTime);
+	msg = Common::String::format("%u %d %d %d %d %d %d\n",
+								 _G(_bitFlags), (_G(_bitFlags) & (1 << DARKBIT)) ? 1 : 0,
+								 MY_LOC, _G(_currentCounter), _G(_savedRoom), _G(_gameHeader)->_lightTime, _G(_autoInventory));
 	ws->write(msg.c_str(), msg.size());
 	ws->writeByte(0);
 
-	for (int ct = 0; ct <= _gameHeader._numItems; ct++) {
-		msg = Common::String::format("%hd\n", (short)_items[ct]._location);
+	for (int ct = 0; ct <= _G(_gameHeader)->_numItems; ct++) {
+		msg = Common::String::format("%hd\n", (short)_G(_items)[ct]._location);
 		ws->write(msg.c_str(), msg.size());
 		ws->writeByte(0);
 	}
@@ -557,199 +669,220 @@ Common::Error Scott::readSaveData(Common::SeekableReadStream *rs) {
 	short lo;
 	short darkFlag;
 
+	int previousAutoInventory = _G(_autoInventory);
+
+	SavedState *state = saveCurrentState();
+
+	int result = 0;
+
 	for (ct = 0; ct < 16; ct++) {
 		line = QuetzalReader::readString(rs);
-		sscanf(line.c_str(), "%d %d", &_counters[ct], &_roomSaved[ct]);
+		result = sscanf(line.c_str(), "%d %d", &_G(_counters)[ct], &_G(_roomSaved)[ct]);
+		if (result != 2 || _G(_roomSaved)[ct] > _G(_gameHeader)->_numRooms) {
+			recoverFromBadRestore(state);
+			return Common::kNoError;
+		}
 	}
 
 	line = QuetzalReader::readString(rs);
-	sscanf(line.c_str(), "%u %hd %d %d %d %d\n",
-		   &_bitFlags, &darkFlag, &MY_LOC, &_currentCounter, &_savedRoom,
-		   &_gameHeader._lightTime);
-
+	result = sscanf(line.c_str(), "%u %hd %d %d %d %d %d\n",
+		   &_G(_bitFlags), &darkFlag, &MY_LOC, &_G(_currentCounter), &_G(_savedRoom),
+		   &_G(_gameHeader)->_lightTime, &_G(_autoInventory));
+
+	if (result == 6)
+		_G(_autoInventory) = previousAutoInventory;
+	if ((result != 7 && result != 6) || MY_LOC > _G(_gameHeader)->_numRooms || MY_LOC < 1 || _G(_savedRoom) > _G(_gameHeader)->_numRooms) {
+		recoverFromBadRestore(state);
+		return Common::kNoError;
+	}
 	// Backward compatibility
 	if (darkFlag)
-		_bitFlags |= (1 << 15);
-	for (ct = 0; ct <= _gameHeader._numItems; ct++) {
+		_G(_bitFlags) |= (1 << 15);
+	for (ct = 0; ct <= _G(_gameHeader)->_numItems; ct++) {
 		line = QuetzalReader::readString(rs);
-		sscanf(line.c_str(), "%hd\n", &lo);
-		_items[ct]._location = (unsigned char)lo;
+		result = sscanf(line.c_str(), "%hd\n", &lo);
+		_G(_items)[ct]._location = (unsigned char)lo;
+		if (result != 1 || (_G(_items)[ct]._location > _G(_gameHeader)->_numRooms && _G(_items)[ct]._location != CARRIED)) {
+			recoverFromBadRestore(state);
+			return Common::kNoError;
+		}
 	}
 
+	saveUndo();
+	_G(_justStarted) = 0;
+	_G(_stopTime) = 1;
 	return Common::kNoError;
 }
 
-int Scott::getInput(int *vb, int *no) {
-	char buf[256];
-	char verb[10], noun[10];
-	int vc, nc;
-	int num;
-
-	do {
-		do {
-			output("\nTell me what to do ? ");
-			lineInput(buf, sizeof buf);
-			if (g_vm->shouldQuit())
-				return 0;
-
-			num = sscanf(buf, "%9s %9s", verb, noun);
-		} while (num == 0 || *buf == '\n');
-
-		if (scumm_stricmp(verb, "restore") == 0) {
-			loadGame();
-			return -1;
-		}
-		if (num == 1)
-			*noun = 0;
-		if (*noun == 0 && strlen(verb) == 1) {
-			switch (Common::isUpper((unsigned char)*verb) ? tolower((unsigned char)*verb) : *verb) {
-			case 'n':
-				strcpy(verb, "NORTH");
-				break;
-			case 'e':
-				strcpy(verb, "EAST");
-				break;
-			case 's':
-				strcpy(verb, "SOUTH");
-				break;
-			case 'w':
-				strcpy(verb, "WEST");
-				break;
-			case 'u':
-				strcpy(verb, "UP");
-				break;
-			case 'd':
-				strcpy(verb, "DOWN");
-				break;
-			// Brian Howarth interpreter also supports this
-			case 'i':
-				strcpy(verb, "INVENTORY");
-				break;
-			default:
-				break;
-			}
-		}
-		nc = whichWord(verb, _nouns);
-		// The Scott Adams system has a hack to avoid typing 'go'
-		if (nc >= 1 && nc <= 6) {
-			vc = 1;
-		} else {
-			vc = whichWord(verb, _verbs);
-			nc = whichWord(noun, _nouns);
-		}
-		*vb = vc;
-		*no = nc;
-		if (vc == -1) {
-			output(_("You use word(s) I don't know! "));
-		}
-	} while (vc == -1);
-
-	strcpy(_nounText, noun); // Needed by GET/DROP hack
-	return 0;
-}
-
-int Scott::performLine(int ct) {
-	int continuation = 0;
+ActionResultType Scott::performLine(int ct) {
+#ifdef DEBUG_ACTIONS
+	debug("Performing line %d: ", ct);
+#endif
+	int continuation = 0, dead = 0;
 	int param[5], pptr = 0;
+	int p;
 	int act[4];
 	int cc = 0;
-
 	while (cc < 5) {
 		int cv, dv;
-		cv = _actions[ct]._condition[cc];
+		cv = _G(_actions)[ct]._condition[cc];
 		dv = cv / 20;
 		cv %= 20;
+#ifdef DEBUG_ACTIONS
+		debug("Testing condition %d: ", cv);
+#endif
 		switch (cv) {
 		case 0:
 			param[pptr++] = dv;
 			break;
 		case 1:
-			if (_items[dv]._location != CARRIED)
-				return 0;
+#ifdef DEBUG_ACTIONS
+			debug("Does the player carry %s?\n", _G(_items)[dv].Text);
+#endif
+			if (_G(_items)[dv]._location != CARRIED)
+				return ACT_FAILURE;
 			break;
 		case 2:
-			if (_items[dv]._location != MY_LOC)
-				return 0;
+#ifdef DEBUG_ACTIONS
+			debug("Is %s in location?\n", _G(_items)[dv].Text);
+#endif
+			if (_G(_items)[dv]._location != MY_LOC)
+				return ACT_FAILURE;
 			break;
 		case 3:
-			if (_items[dv]._location != CARRIED &&
-					_items[dv]._location != MY_LOC)
-				return 0;
+#ifdef DEBUG_ACTIONS
+			debug("Is %s held or in location?\n", _G(_items)[dv].Text);
+#endif
+			if (_G(_items)[dv]._location != CARRIED && _G(_items)[dv]._location != MY_LOC)
+				return ACT_FAILURE;
 			break;
 		case 4:
+#ifdef DEBUG_ACTIONS
+			debug("Is location %s?\n", _G(_rooms)[dv].Text);
+#endif
 			if (MY_LOC != dv)
-				return 0;
+				return ACT_FAILURE;
 			break;
 		case 5:
-			if (_items[dv]._location == MY_LOC)
-				return 0;
+#ifdef DEBUG_ACTIONS
+			debug("Is %s NOT in location?\n", _G(_items)[dv].Text);
+#endif
+			if (_G(_items)[dv]._location == MY_LOC)
+				return ACT_FAILURE;
 			break;
 		case 6:
-			if (_items[dv]._location == CARRIED)
-				return 0;
+#ifdef DEBUG_ACTIONS
+			debug("Does the player NOT carry %s?\n", _G(_items)[dv].Text);
+#endif
+			if (_G(_items)[dv]._location == CARRIED)
+				return ACT_FAILURE;
 			break;
 		case 7:
+#ifdef DEBUG_ACTIONS
+			debug("Is location NOT %s?\n", _G(_rooms)[dv].Text);
+#endif
 			if (MY_LOC == dv)
-				return 0;
+				return ACT_FAILURE;
 			break;
 		case 8:
-			if ((_bitFlags & (1 << dv)) == 0)
-				return 0;
+#ifdef DEBUG_ACTIONS
+			debug("Is bitflag %d set?\n", dv);
+#endif
+			if ((_G(_bitFlags) & (1 << dv)) == 0)
+				return ACT_FAILURE;
 			break;
 		case 9:
-			if (_bitFlags & (1 << dv))
-				return 0;
+#ifdef DEBUG_ACTIONS
+			debug("Is bitflag %d NOT set?\n", dv);
+#endif
+			if (_G(_bitFlags) & (1 << dv))
+				return ACT_FAILURE;
 			break;
 		case 10:
+#ifdef DEBUG_ACTIONS
+			debug("Does the player carry anything?\n");
+#endif
 			if (countCarried() == 0)
-				return 0;
+				return ACT_FAILURE;
 			break;
 		case 11:
+#ifdef DEBUG_ACTIONS
+			debug("Does the player carry nothing?\n");
+#endif
 			if (countCarried())
-				return 0;
+				return ACT_FAILURE;
 			break;
 		case 12:
-			if (_items[dv]._location == CARRIED || _items[dv]._location == MY_LOC)
-				return 0;
+#ifdef DEBUG_ACTIONS
+			debug("Is %s neither carried nor in room?\n", _G(_items)[dv].Text);
+#endif
+			if (_G(_items)[dv]._location == CARRIED || _G(_items)[dv]._location == MY_LOC)
+				return ACT_FAILURE;
 			break;
 		case 13:
-			if (_items[dv]._location == 0)
-				return 0;
+#ifdef DEBUG_ACTIONS
+			debug("Is %s (%d) in play?\n", _G(_items)[dv].Text, dv);
+#endif
+			if (_G(_items)[dv]._location == 0)
+				return ACT_FAILURE;
 			break;
 		case 14:
-			if (_items[dv]._location)
-				return 0;
+#ifdef DEBUG_ACTIONS
+			debug("Is %s NOT in play?\n", _G(_items)[dv].Text);
+#endif
+			if (_G(_items)[dv]._location)
+				return ACT_FAILURE;
 			break;
 		case 15:
-			if (_currentCounter > dv)
-				return 0;
+#ifdef DEBUG_ACTIONS
+			debug("Is _G(_currentCounter) <= %d?\n", dv);
+#endif
+			if (_G(_currentCounter) > dv)
+				return ACT_FAILURE;
 			break;
 		case 16:
-			if (_currentCounter <= dv)
-				return 0;
+#ifdef DEBUG_ACTIONS
+			debug("Is _G(_currentCounter) > %d?\n", dv);
+#endif
+			if (_G(_currentCounter) <= dv)
+				return ACT_FAILURE;
 			break;
 		case 17:
-			if (_items[dv]._location != _items[dv]._initialLoc)
-				return 0;
+#ifdef DEBUG_ACTIONS
+			debug("Is %s still in initial room?\n", _G(_items)[dv].Text);
+#endif
+			if (_G(_items)[dv]._location != _G(_items)[dv]._initialLoc)
+				return ACT_FAILURE;
 			break;
 		case 18:
-			if (_items[dv]._location == _items[dv]._initialLoc)
-				return 0;
-			break;
-		case 19:
-			// Only seen in Brian Howarth games so far
-			if (_currentCounter != dv)
-				return 0;
+#ifdef DEBUG_ACTIONS
+			debug("Has %s been moved?\n", _G(_items)[dv].Text);
+#endif
+			if (_G(_items)[dv]._location == _G(_items)[dv]._initialLoc)
+				return ACT_FAILURE;
 			break;
-		default:
+		case 19: /* Only seen in Brian Howarth games so far */
+#ifdef DEBUG_ACTIONS
+			debug("Is current counter == %d?\n", dv);
+			if (_G(_currentCounter) != dv)
+				debug("Nope, current counter is %d\n", _G(_currentCounter));
+#endif
+			if (_G(_currentCounter) != dv)
+				return ACT_FAILURE;
 			break;
 		}
+#ifdef DEBUG_ACTIONS
+		debug("YES\n");
+#endif
 		cc++;
 	}
+#if defined(__clang__)
+#pragma mark Subcommands
+#endif
 
-	// _actions
-	act[0] = _actions[ct]._action[0];
-	act[2] = _actions[ct]._action[1];
+	/* Actions */
+	act[0] = _G(_actions)[ct]._action[0];
+	act[2] = _G(_actions)[ct]._action[1];
 	act[1] = act[0] % 150;
 	act[3] = act[2] % 150;
 	act[0] /= 150;
@@ -757,426 +890,466 @@ int Scott::performLine(int ct) {
 	cc = 0;
 	pptr = 0;
 	while (cc < 4) {
+#ifdef DEBUG_ACTIONS
+		debug("Performing action %d: ", act[cc]);
+#endif
 		if (act[cc] >= 1 && act[cc] < 52) {
-			output(_messages[act[cc]]);
-			output("\n");
+			printMessage(act[cc]);
 		} else if (act[cc] > 101) {
-			output(_messages[act[cc] - 50]);
-			output("\n");
-		} else {
+			printMessage(act[cc] - 50);
+		} else
 			switch (act[cc]) {
-			case 0:// NOP
+			case 0: /* NOP */
 				break;
 			case 52:
-				if (countCarried() == _gameHeader._maxCarry) {
-					if (_options & YOUARE)
-						output(_("You are carrying too much. "));
-					else
-						output(_("I've too much to carry! "));
-					break;
+				if (countCarried() >= _G(_gameHeader)->_maxCarry) {
+					output(_G(_sys)[YOURE_CARRYING_TOO_MUCH]);
+					return ACT_SUCCESS;
 				}
-				_items[param[pptr++]]._location = CARRIED;
+				_G(_items)[param[pptr++]]._location = CARRIED;
 				break;
 			case 53:
-				_items[param[pptr++]]._location = MY_LOC;
+#ifdef DEBUG_ACTIONS
+				debug("item %d (\"%s\") is now in location.\n", param[pptr],
+					  _G(_items)[param[pptr]].Text);
+#endif
+				_G(_items)[param[pptr++]]._location = MY_LOC;
+				_shouldLookInTranscript = 1;
 				break;
 			case 54:
+#ifdef DEBUG_ACTIONS
+				debug("player location is now room %d (%s).\n", param[pptr],
+					  _G(_rooms)[param[pptr]].Text);
+#endif
 				MY_LOC = param[pptr++];
+				_shouldLookInTranscript = 1;
+				look();
 				break;
 			case 55:
-				_items[param[pptr++]]._location = 0;
+#ifdef DEBUG_ACTIONS
+				fprintf(stderr,
+						"Item %d (%s) is removed from the game (put in room 0).\n",
+						param[pptr], _G(_items)[param[pptr]].Text);
+#endif
+				_G(_items)[param[pptr++]]._location = 0;
 				break;
 			case 56:
-				_bitFlags |= 1 << DARKBIT;
+				_G(_bitFlags) |= 1 << DARKBIT;
 				break;
 			case 57:
-				_bitFlags &= ~(1 << DARKBIT);
+				_G(_bitFlags) &= ~(1 << DARKBIT);
 				break;
 			case 58:
-				_bitFlags |= (1 << param[pptr++]);
+#ifdef DEBUG_ACTIONS
+				debug("Bitflag %d is set\n", param[pptr]);
+#endif
+				_G(_bitFlags) |= (1 << param[pptr++]);
 				break;
 			case 59:
-				_items[param[pptr++]]._location = 0;
+#ifdef DEBUG_ACTIONS
+				debug("Item %d (%s) is removed from play.\n", param[pptr],
+					  _G(_items)[param[pptr]].Text);
+#endif
+				_G(_items)[param[pptr++]]._location = 0;
 				break;
 			case 60:
-				_bitFlags &= ~(1 << param[pptr++]);
+#ifdef DEBUG_ACTIONS
+				debug("BitFlag %d is cleared\n", param[pptr]);
+#endif
+				_G(_bitFlags) &= ~(1 << param[pptr++]);
 				break;
 			case 61:
-				if (_options & YOUARE)
-					output(_("You are dead.\n"));
-				else
-					output(_("I am dead.\n"));
-				_bitFlags &= ~(1 << DARKBIT);
-				MY_LOC = _gameHeader._numRooms;// It seems to be what the code says!
+				playerIsDead();
 				break;
-			case 62: {
-				// Bug fix for some systems - before it could get parameters wrong */
-				int i = param[pptr++];
-				_items[i]._location = param[pptr++];
+			case 62:
+				p = param[pptr++];
+				putItemAInRoomB(p, param[pptr++]);
 				break;
-			}
 			case 63:
-doneit:
-				output(_("The game is now over.\n"));
-				glk_exit();
-				return 0;
+#ifdef DEBUG_ACTIONS
+				debug("Game over.\n");
+#endif
+				doneIt();
+				dead = 1;
+				break;
 			case 64:
 				break;
-			case 65: {
-				int i = 0;
-				int n = 0;
-				while (i <= _gameHeader._numItems) {
-					if (_items[i]._location == _gameHeader._treasureRoom &&
-							_items[i]._text.hasPrefix("*"))
-						n++;
-					i++;
-				}
-				if (_options & YOUARE)
-					output(_("You have stored "));
-				else
-					output(_("I've stored "));
-				outputNumber(n);
-				output(_(" treasures.  On a scale of 0 to 100, that rates "));
-				outputNumber((n * 100) / _gameHeader._treasures);
-				output(".\n");
-				if (n == _gameHeader._treasures) {
-					output(_("Well done.\n"));
-					goto doneit;
-				}
+			case 65:
+				dead = printScore();
+				_G(_stopTime) = 2;
 				break;
-			}
-			case 66: {
-				int i = 0;
-				int f = 0;
-				if (_options & YOUARE)
-					output(_("You are carrying:\n"));
+			case 66:
+				if (_G(_game)->_type == SEAS_OF_BLOOD_VARIANT)
+					// TODO
+					// AdventureSheet();
+					debug("case 66 not implemented\n");
 				else
-					output(_("I'm carrying:\n"));
-				while (i <= _gameHeader._numItems) {
-					if (_items[i]._location == CARRIED) {
-						if (f == 1) {
-							if (_options & TRS80_STYLE)
-								output(". ");
-							else
-								output(" - ");
-						}
-						f = 1;
-						output(_items[i]._text);
-					}
-					i++;
-				}
-				if (f == 0)
-					output(_("Nothing"));
-				output(".\n");
+					listInventory();
+				_G(_stopTime) = 2;
 				break;
-			}
 			case 67:
-				_bitFlags |= (1 << 0);
+				_G(_bitFlags) |= (1 << 0);
 				break;
 			case 68:
-				_bitFlags &= ~(1 << 0);
+				_G(_bitFlags) &= ~(1 << 0);
 				break;
 			case 69:
-				_gameHeader._lightTime = _lightRefill;
-				_items[LIGHT_SOURCE]._location = CARRIED;
-				_bitFlags &= ~(1 << LIGHTOUTBIT);
+				_G(_gameHeader)->_lightTime = _G(_lightRefill);
+				_G(_items)[LIGHT_SOURCE]._location = CARRIED;
+				_G(_bitFlags) &= ~(1 << LIGHTOUTBIT);
 				break;
 			case 70:
-				clearScreen(); // pdd.
+				clearScreen(); /* pdd. */
 				break;
 			case 71:
 				saveGame();
+				_G(_stopTime) = 2;
 				break;
-			case 72: {
-				int i1 = param[pptr++];
-				int i2 = param[pptr++];
-				int t = _items[i1]._location;
-				_items[i1]._location = _items[i2]._location;
-				_items[i2]._location = t;
+			case 72:
+				p = param[pptr++];
+				swapItemLocations(p, param[pptr++]);
 				break;
-			}
 			case 73:
+#ifdef DEBUG_ACTIONS
+				debug("Continue with next line\n");
+#endif
 				continuation = 1;
 				break;
 			case 74:
-				_items[param[pptr++]]._location = CARRIED;
+				_G(_items)[param[pptr++]]._location = CARRIED;
 				break;
-			case 75: {
-				int i1, i2;
-				i1 = param[pptr++];
-				i2 = param[pptr++];
-				_items[i1]._location = _items[i2]._location;
+			case 75:
+				p = param[pptr++];
+				moveItemAToLocOfItemB(p, param[pptr++]);
 				break;
-			}
-			case 76:
-				// Looking at adventure ..
+			case 76: /* Looking at adventure .. */
+#ifdef DEBUG_ACTIONS
+				debug("LOOK\n");
+#endif
+				if (_splitScreen)
+					look();
+				_shouldLookInTranscript = 1;
 				break;
 			case 77:
-				if (_currentCounter >= 0)
-					_currentCounter--;
+				if (_G(_currentCounter) >= 1)
+					_G(_currentCounter)--;
+#ifdef DEBUG_ACTIONS
+				fprintf(stderr,
+						"decrementing current counter. Current counter is now %d.\n",
+						_G(_currentCounter));
+#endif
 				break;
 			case 78:
-				outputNumber(_currentCounter);
+				outputNumber(_G(_currentCounter));
+				output(" ");
 				break;
 			case 79:
-				_currentCounter = param[pptr++];
+#ifdef DEBUG_ACTIONS
+				debug("_G(_currentCounter) is set to %d.\n", param[pptr]);
+#endif
+				_G(_currentCounter) = param[pptr++];
 				break;
-			case 80: {
-				int t = MY_LOC;
-				MY_LOC = _savedRoom;
-				_savedRoom = t;
+			case 80:
+				goToStoredLoc();
 				break;
-			}
-			case 81: {
-				// This is somewhat guessed. Claymorgue always seems to do
-				// select counter n, thing, select counter n, but uses one value that always
-				// seems to exist. Trying a few options I found this gave sane results on ageing
-				int t = param[pptr++];
-				int c1 = _currentCounter;
-				_currentCounter = _counters[t];
-				_counters[t] = c1;
+			case 81:
+				swapCounters(param[pptr++]);
 				break;
-			}
 			case 82:
-				_currentCounter += param[pptr++];
+				_G(_currentCounter) += param[pptr++];
 				break;
 			case 83:
-				_currentCounter -= param[pptr++];
-				if (_currentCounter < -1)
-					_currentCounter = -1;
-				// Note: This seems to be needed. I don't yet know if there
-				// is a maximum value to limit too
+				_G(_currentCounter) -= param[pptr++];
+				if (_G(_currentCounter) < -1)
+					_G(_currentCounter) = -1;
+				/* Note: This seems to be needed. I don't yet
+						 know if there is a maximum value to limit too */
 				break;
 			case 84:
-				output(_nounText);
+				if (_G(_currentCommand))
+					glk_put_string_stream_uni(
+						glk_window_get_stream(_G(_bottomWindow)),
+						_G(_unicodeWords)[_G(_currentCommand)->_nounWordIndex]);
 				break;
 			case 85:
-				output(_nounText);
+				if (_G(_currentCommand))
+					glk_put_string_stream_uni(
+						glk_window_get_stream(_G(_bottomWindow)),
+						_G(_unicodeWords)[_G(_currentCommand)->_nounWordIndex]);
 				output("\n");
 				break;
 			case 86:
-				output("\n");
+				if (!(_options & SPECTRUM_STYLE))
+					output("\n");
 				break;
-			case 87: {
-				// Changed this to swap location<->roomflag[x] not roomflag 0 and x
-				int p = param[pptr++];
-				int sr = MY_LOC;
-				MY_LOC = _roomSaved[p];
-				_roomSaved[p] = sr;
+			case 87:
+				swapLocAndRoomFlag(param[pptr++]);
 				break;
-			}
 			case 88:
-				delay(2);
+#ifdef DEBUG_ACTIONS
+				debug("Delay\n");
+#endif
+				delay(1);
 				break;
 			case 89:
-				pptr++;
-				// SAGA draw picture n
-				// Spectrum Seas of Blood - start combat ?
-				// Poking this into older spectrum games causes a crash
+#ifdef DEBUG_ACTIONS
+				debug("Action 89, parameter %d\n", param[pptr]);
+#endif
+				p = param[pptr++];
+				switch (CURRENT_GAME) {
+				case SPIDERMAN:
+				case SPIDERMAN_C64:
+					// TODO
+					// DrawBlack();
+					break;
+				case SECRET_MISSION:
+				case SECRET_MISSION_C64:
+					// TODO
+					// SecretAction(p);
+					break;
+				case ADVENTURELAND:
+				case ADVENTURELAND_C64:
+					// TODO
+					// AdventurelandAction(p);
+					break;
+				case SEAS_OF_BLOOD:
+				case SEAS_OF_BLOOD_C64:
+					// TODO
+					// BloodAction(p);
+					break;
+				case ROBIN_OF_SHERWOOD:
+				case ROBIN_OF_SHERWOOD_C64:
+					// TODO
+					// SherwoodAction(p);
+					break;
+				case GREMLINS:
+				case GREMLINS_SPANISH:
+				case GREMLINS_GERMAN:
+				case GREMLINS_GERMAN_C64:
+					// TODO
+					// GremlinsAction(p);
+					break;
+				default:
+					break;
+				}
+				break;
+
+			case 90:
+#ifdef DEBUG_ACTIONS
+				debug("Draw Hulk image, parameter %d\n", param[pptr]);
+#endif
+				if (CURRENT_GAME != HULK && CURRENT_GAME != HULK_C64) {
+					pptr++;
+				} else if (!(_G(_bitFlags) & (1 << DARKBIT)))
+					drawHulkImage(param[pptr++]);
 				break;
 			default:
-				error("Unknown action %d [Param begins %d %d]\n",
-					  act[cc], param[pptr], param[pptr + 1]);
+				debug("Unknown action %d [Param begins %d %d]\n", act[cc],
+					  param[pptr], param[pptr + 1]);
 				break;
 			}
-		}
-
 		cc++;
 	}
 
-	return 1 + continuation;
+	if (dead) {
+		return ACT_GAMEOVER;
+	} else if (continuation) {
+		return ACT_CONTINUE;
+	} else {
+		return ACT_SUCCESS;
+	}
 }
 
-int Scott::performActions(int vb, int no) {
-	static bool disableSysFunc = false; // Recursion lock
-	int d = _bitFlags & (1 << DARKBIT);
-
+ExplicitResultType Scott::performActions(int vb, int no) {
+	int dark = _G(_bitFlags) & (1 << DARKBIT);
 	int ct = 0;
-	int fl;
-	int doagain = 0;
-	if (vb == 1 && no == -1) {
-		output(_("Give me a direction too."));
-		return 0;
+	ExplicitResultType flag = ER_NO_RESULT;
+	int doAgain = 0;
+	int foundMatch = 0;
+
+	if (vb == GO && no == -1) {
+		output(_G(_sys)[DIRECTION]);
+		return ER_SUCCESS;
 	}
 	if (vb == 1 && no >= 1 && no <= 6) {
 		int nl;
-		if (_items[LIGHT_SOURCE]._location == MY_LOC ||
-				_items[LIGHT_SOURCE]._location == CARRIED)
-			d = 0;
-		if (d)
-			output(_("Dangerous to move in the dark! "));
-		nl = _rooms[MY_LOC]._exits[no - 1];
+		if (_G(_items)[LIGHT_SOURCE]._location == MY_LOC || _G(_items)[LIGHT_SOURCE]._location == CARRIED)
+			dark = 0;
+		if (dark)
+			output(_G(_sys)[DANGEROUS_TO_MOVE_IN_DARK]);
+		nl = _G(_rooms)[MY_LOC]._exits[no - 1];
 		if (nl != 0) {
+			/* Seas of Blood needs this to be able to flee back to the last room */
+			if (_G(_game)->_type == SEAS_OF_BLOOD_VARIANT)
+				_G(_savedRoom) = MY_LOC;
+			if (_options & (SPECTRUM_STYLE | TI994A_STYLE))
+				output(_G(_sys)[OK]);
 			MY_LOC = nl;
-			return 0;
+			_shouldLookInTranscript = 1;
+			if (_G(_currentCommand) && _G(_currentCommand)->_next) {
+				lookWithPause();
+			}
+			return ER_SUCCESS;
 		}
-		if (d) {
-			if (_options & YOUARE)
-				output(_("You fell down and broke your neck. "));
-			else
-				output(_("I fell down and broke my neck. "));
-			glk_exit();
-			return 0;
+		if (dark) {
+			_G(_bitFlags) &= ~(1 << DARKBIT);
+			MY_LOC = _G(_gameHeader)->_numRooms; /* It seems to be what the code says! */
+			output(_G(_sys)[YOU_FELL_AND_BROKE_YOUR_NECK]);
+			_G(_bitFlags) &= ~(1 << DARKBIT);
+			MY_LOC = _G(_gameHeader)->_numRooms; /* It seems to be what the code says! */
+			return ER_SUCCESS;
 		}
-		if (_options & YOUARE)
-			output(_("You can't go in that direction. "));
-		else
-			output(_("I can't go in that direction. "));
-		return 0;
+		output(_G(_sys)[YOU_CANT_GO_THAT_WAY]);
+		return ER_SUCCESS;
 	}
 
-	fl = -1;
-	while (ct <= _gameHeader._numActions) {
-		int vv, nv;
-		vv = _actions[ct]._vocab;
-		// Think this is now right. If a line we run has an action73
-		// run all following lines with vocab of 0,0
-		if (vb != 0 && (doagain && vv != 0))
-			break;
-		// Oops.. added this minor cockup fix 1.11
-		if (vb != 0 && !doagain && fl == 0)
-			break;
-		nv = vv % 150;
-		vv /= 150;
-		if ((vv == vb) || (doagain && _actions[ct]._vocab == 0)) {
-			if ((vv == 0 && randomPercent(nv)) || doagain ||
-					(vv != 0 && (nv == no || nv == 0))) {
-				int f2;
-				if (fl == -1)
-					fl = -2;
-				if ((f2 = performLine(ct)) > 0) {
-					// ahah finally figured it out !
-					fl = 0;
-					if (f2 == 2)
-						doagain = 1;
-					if (vb != 0 && doagain == 0)
-						return 0;
-				}
+	if ((CURRENT_GAME == HULK || CURRENT_GAME == HULK_C64) && vb == 39 && !dark) {
+		hulkShowImageOnExamine(no);
+	}
 
-				if (shouldQuit())
-					return 0;
+	if (_G(_currentCommand) && _G(_currentCommand)->_allFlag && vb == _G(_currentCommand)->_verb && !(dark && vb == TAKE)) {
+		output(_G(_items)[_G(_currentCommand)->_item]._text);
+		output("....");
+	}
+	flag = ER_RAN_ALL_LINES_NO_MATCH;
+	if (CURRENT_GAME != TI994A) {
+		while (ct <= _G(_gameHeader)->_numActions) {
+			int verbvalue, nounvalue;
+			verbvalue = _G(_actions)[ct]._vocab;
+			/* Think this is now right. If a line we run has an action73
+		   run all following lines with vocab of 0,0 */
+			if (vb != 0 && (doAgain && verbvalue != 0))
+				break;
+			/* Oops.. added this minor cockup fix 1.11 */
+			if (vb != 0 && !doAgain && flag == 0)
+				break;
+			nounvalue = verbvalue % 150;
+			verbvalue /= 150;
+			if ((verbvalue == vb) || (doAgain && _G(_actions)[ct]._vocab == 0)) {
+				if ((verbvalue == 0 && randomPercent(nounvalue)) || doAgain || (verbvalue != 0 && (nounvalue == no || nounvalue == 0))) {
+					if (verbvalue == vb && vb != 0 && nounvalue == no)
+						foundMatch = 1;
+					ActionResultType flag2;
+					if (flag == ER_RAN_ALL_LINES_NO_MATCH)
+						flag = ER_RAN_ALL_LINES;
+					if ((flag2 = performLine(ct)) != ACT_FAILURE) {
+						/* ahah finally figured it out ! */
+						flag = ER_SUCCESS;
+						if (flag2 == ACT_CONTINUE)
+							doAgain = 1;
+						else if (flag2 == ACT_GAMEOVER)
+							return ER_SUCCESS;
+						if (vb != 0 && doAgain == 0)
+							return ER_SUCCESS;
+					}
+				}
 			}
+
+			ct++;
+
+			/* Previously this did not check ct against
+			 * GameHeader.NumActions and would read past the end of
+			 * Actions.  I don't know what should happen on the last
+			 * action, but doing nothing is better than reading one
+			 * past the end.
+			 * --Chris
+			 */
+			if (ct <= _G(_gameHeader)->_numActions && _G(_actions)[ct]._vocab != 0)
+				doAgain = 0;
 		}
-		ct++;
+	} else {
+		if (vb == 0) {
+			// TODO
+			// RunImplicitTI99Actions();
+			return ER_NO_RESULT;
+		} else {
+			// TODO
+			// flag = RunExplicitTI99Actions(vb, no);
+		}
+	}
 
-		// Previously this did not check ct against _gameHeader._numActions and would read
-		// past the end of _actions.  I don't know what should happen on the last action,
-		// but doing nothing is better than reading one past the end.
-		// --Chris
-		if (ct <= _gameHeader._numActions && _actions[ct]._vocab != 0)
-			doagain = 0;
-	}
-	if (fl != 0 && disableSysFunc == 0) {
-		int item;
-		if (_items[LIGHT_SOURCE]._location == MY_LOC ||
-				_items[LIGHT_SOURCE]._location == CARRIED)
-			d = 0;
-		if (vb == 10 || vb == 18) {
-			// Yes they really _are_ hardcoded values
-			if (vb == 10) {
-				if (scumm_stricmp(_nounText, "ALL") == 0) {
-					int i = 0;
-					int f = 0;
-
-					if (d) {
-						output(_("It is dark.\n"));
-						return 0;
-					}
-					while (i <= _gameHeader._numItems) {
-						if (_items[i]._location == MY_LOC && _items[i]._autoGet != nullptr && _items[i]._autoGet[0] != '*') {
-							no = whichWord(_items[i]._autoGet.c_str(), _nouns);
-							disableSysFunc = true;    // Don't recurse into auto get !
-							performActions(vb, no);   // Recursively check each items table code
-							disableSysFunc = false;
-							if (shouldQuit())
-								return 0;
-
-							if (countCarried() == _gameHeader._maxCarry) {
-								if (_options & YOUARE)
-									output(_("You are carrying too much. "));
-								else
-									output(_("I've too much to carry. "));
-								return 0;
-							}
-							_items[i]._location = CARRIED;
-							output(_items[i]._text);
-							output(_(": O.K.\n"));
-							f = 1;
-						}
-						i++;
+	if (foundMatch)
+		return flag;
+
+	if (flag != ER_SUCCESS) {
+		int item = 0;
+		if (_G(_items)[LIGHT_SOURCE]._location == MY_LOC || _G(_items)[LIGHT_SOURCE]._location == CARRIED)
+			dark = 0;
+
+		if (vb == TAKE || vb == DROP) {
+			if (_G(_currentCommand)->_allFlag) {
+				if (vb == TAKE && dark) {
+					output(_G(_sys)[TOO_DARK_TO_SEE]);
+					while (!(_G(_currentCommand)->_allFlag & LASTALL)) {
+						_G(_currentCommand) = _G(_currentCommand)->_next;
 					}
-					if (f == 0)
-						output(_("Nothing taken."));
-					return 0;
+					return ER_SUCCESS;
 				}
+				item = _G(_currentCommand)->_item;
+				int location = CARRIED;
+				if (vb == TAKE)
+					location = MY_LOC;
+				while (_G(_items)[item]._location != location && !(_G(_currentCommand)->_allFlag & LASTALL)) {
+					_G(_currentCommand) = _G(_currentCommand)->_next;
+				}
+				if (_G(_items)[item]._location != location)
+					return ER_SUCCESS;
+			}
+
+			/* Yes they really _are_ hardcoded values */
+			if (vb == TAKE) {
 				if (no == -1) {
-					output(_("What ? "));
-					return 0;
+					output(_G(_sys)[WHAT]);
+					return ER_SUCCESS;
 				}
-				if (countCarried() == _gameHeader._maxCarry) {
-					if (_options & YOUARE)
-						output(_("You are carrying too much. "));
-					else
-						output(_("I've too much to carry. "));
-					return 0;
+				if (countCarried() >= _G(_gameHeader)->_maxCarry) {
+					output(_G(_sys)[YOURE_CARRYING_TOO_MUCH]);
+					return ER_SUCCESS;
 				}
-				item = matchUpItem(_nounText, MY_LOC);
+				if (!item)
+					item = matchUpItem(no, MY_LOC);
 				if (item == -1) {
-					if (_options & YOUARE)
-						output(_("It is beyond your power to do that. "));
-					else
-						output(_("It's beyond my power to do that. "));
-					return 0;
-				}
-				_items[item]._location = CARRIED;
-				output(_("O.K. "));
-				return 0;
-			}
-			if (vb == 18) {
-				if (scumm_stricmp(_nounText, "ALL") == 0) {
-					int i = 0;
-					int f = 0;
-					while (i <= _gameHeader._numItems) {
-						if (_items[i]._location == CARRIED && !_items[i]._autoGet.empty()
-								&& !_items[i]._autoGet.hasPrefix("*")) {
-							no = whichWord(_items[i]._autoGet.c_str(), _nouns);
-							disableSysFunc = true;
-							performActions(vb, no);
-							disableSysFunc = false;
-							if (shouldQuit())
-								return 0;
-
-							_items[i]._location = MY_LOC;
-							output(_items[i]._text);
-							output(_(": O.K.\n"));
-							f = 1;
+					item = matchUpItem(no, CARRIED);
+					if (item == -1) {
+						item = matchUpItem(no, 0);
+						if (item == -1) {
+							output(_G(_sys)[THATS_BEYOND_MY_POWER]);
+						} else {
+							output(_G(_sys)[YOU_DONT_SEE_IT]);
 						}
-						i++;
+					} else {
+						output(_G(_sys)[YOU_HAVE_IT]);
 					}
-					if (f == 0)
-						output(_("Nothing dropped.\n"));
-					return 0;
+					return ER_SUCCESS;
 				}
+				_G(_items)[item]._location = CARRIED;
+				printTakenOrDropped(TAKEN);
+				return ER_SUCCESS;
+			}
+
+			if (vb == DROP) {
 				if (no == -1) {
-					output(_("What ? "));
-					return 0;
+					output(_G(_sys)[WHAT]);
+					return ER_SUCCESS;
 				}
-				item = matchUpItem(_nounText, CARRIED);
+				if (!item)
+					item = matchUpItem(no, CARRIED);
 				if (item == -1) {
-					if (_options & YOUARE)
-						output(_("It's beyond your power to do that.\n"));
-					else
-						output(_("It's beyond my power to do that.\n"));
-					return 0;
+					item = matchUpItem(no, 0);
+					if (item == -1) {
+						output(_G(_sys)[THATS_BEYOND_MY_POWER]);
+					} else {
+						output(_G(_sys)[YOU_HAVENT_GOT_IT]);
+					}
+					return ER_SUCCESS;
 				}
-				_items[item]._location = MY_LOC;
-				output("O.K. ");
-				return 0;
+				_G(_items)[item]._location = MY_LOC;
+				printTakenOrDropped(DROPPED);
+				return ER_SUCCESS;
 			}
 		}
 	}
-
-	return fl;
+	return flag;
 }
 
 void Scott::readInts(Common::SeekableReadStream *f, size_t count, ...) {
@@ -1201,11 +1374,800 @@ void Scott::readInts(Common::SeekableReadStream *f, size_t count, ...) {
 			c = f->readByte();
 		}
 
-		*val *= factor;		// Handle negatives
+		*val *= factor; // Handle negatives
 	}
 
 	va_end(va);
 }
 
+void Scott::writeToRoomDescriptionStream(const char *fmt, ...) {
+	if (_roomDescriptionStream == nullptr)
+		return;
+	va_list ap;
+
+	va_start(ap, fmt);
+	Common::String msg = Common::String::vformat(fmt, ap);
+	va_end(ap);
+
+	glk_put_string_stream(_roomDescriptionStream, msg.c_str());
+}
+
+void Scott::flushRoomDescription(char *buf) {
+	glk_stream_close(_roomDescriptionStream, 0);
+
+	strid_t storedTranscript = _G(_transcript);
+	if (!_printLookToTranscript)
+		_G(_transcript) = nullptr;
+
+	int printDelimiter = (_options & (TRS80_STYLE | SPECTRUM_STYLE | TI994A_STYLE));
+
+	if (_splitScreen) {
+		glk_window_clear(_G(_topWindow));
+		glk_window_get_size(_G(_topWindow), (uint *)&_topWidth, (uint *)&_topHeight);
+		int rows, length;
+		char *textWithBreaks = lineBreakText(buf, _topWidth, &rows, &length);
+
+		glui32 bottomheight;
+		glk_window_get_size(_G(_bottomWindow), nullptr, &bottomheight);
+		winid_t o2 = glk_window_get_parent(_G(_topWindow));
+		if (!(bottomheight < 3 && _topHeight < rows)) {
+			glk_window_get_size(_G(_topWindow), (uint *)&_topWidth, (uint *)&_topHeight);
+			glk_window_set_arrangement(o2, winmethod_Above | winmethod_Fixed, rows, _G(_topWindow));
+		} else {
+			printDelimiter = 0;
+		}
+
+		int line = 0;
+		int index = 0;
+		int i;
+		char *string = new char[_topWidth + 1];
+		for (line = 0; line < rows && index < length; line++) {
+			for (i = 0; i < _topWidth; i++) {
+				string[i] = textWithBreaks[index++];
+				if (string[i] == 10 || string[i] == 13 || index >= length)
+					break;
+			}
+			if (i < _topWidth + 1) {
+				string[i++] = '\n';
+			}
+			string[i] = 0;
+			if (strlen(string) == 0)
+				break;
+			glk_window_move_cursor(_G(_topWindow), 0, line);
+			display(_G(_topWindow), "%s", string);
+		}
+
+		if (line < rows - 1) {
+			glk_window_get_size(_G(_topWindow), (uint *)&_topWidth, (uint *)&_topHeight);
+			glk_window_set_arrangement(o2, winmethod_Above | winmethod_Fixed, MIN(rows - 1, _topHeight - 1), _G(_topWindow));
+		}
+
+		delete[] textWithBreaks;
+	} else {
+		display(_G(_bottomWindow), "%s", buf);
+	}
+
+	if (printDelimiter) {
+		printWindowDelimiter();
+	}
+
+	if (_pauseNextRoomDescription) {
+		delay(0.8);
+		_pauseNextRoomDescription = 0;
+	}
+
+	_G(_transcript) = storedTranscript;
+	if (buf != nullptr) {
+		delete[] buf;
+		buf = nullptr;
+	}
+}
+
+void Scott::printWindowDelimiter() {
+	glk_window_get_size(_G(_topWindow), (uint *)&_topWidth, (uint *)&_topHeight);
+	glk_window_move_cursor(_G(_topWindow), 0, _topHeight - 1);
+	glk_stream_set_current(glk_window_get_stream(_G(_topWindow)));
+	if (_options & SPECTRUM_STYLE)
+		for (int i = 0; i < _topWidth; i++)
+			glk_put_char('*');
+	else {
+		glk_put_char('<');
+		for (int i = 0; i < _topWidth - 2; i++)
+			glk_put_char('-');
+		glk_put_char('>');
+	}
+}
+
+void Scott::listExits() {
+	int ct = 0;
+	int f = 0;
+
+	writeToRoomDescriptionStream("\n\n%s", _G(_sys)[EXITS].c_str());
+
+	while (ct < 6) {
+		if ((&_G(_rooms)[MY_LOC])->_exits[ct] != 0) {
+			if (f) {
+				writeToRoomDescriptionStream("%s", _G(_sys)[EXITS_DELIMITER].c_str());
+			}
+			/* _G(_sys)[] begins with the exit names */
+			writeToRoomDescriptionStream("%s", _G(_sys)[ct].c_str());
+			f = 1;
+		}
+		ct++;
+	}
+	if (f == 0)
+		writeToRoomDescriptionStream("%s", _G(_sys)[NONE].c_str());
+	return;
+}
+
+void Scott::listExitsSpectrumStyle() {
+	int ct = 0;
+	int f = 0;
+
+	while (ct < 6) {
+		if ((&_G(_rooms)[MY_LOC])->_exits[ct] != 0) {
+			if (f == 0) {
+				writeToRoomDescriptionStream("\n\n%s", _G(_sys)[EXITS].c_str());
+			} else {
+				writeToRoomDescriptionStream("%s", _G(_sys)[EXITS_DELIMITER].c_str());
+			}
+			/* sys[] begins with the exit names */
+			writeToRoomDescriptionStream("%s", _G(_sys)[ct].c_str());
+			f = 1;
+		}
+		ct++;
+	}
+	writeToRoomDescriptionStream("\n");
+	return;
+}
+
+void Scott::listInventoryInUpperWindow() {
+	int i = 0;
+	int lastitem = -1;
+	writeToRoomDescriptionStream("\n%s", _G(_sys)[INVENTORY].c_str());
+	while (i <= _G(_gameHeader)->_numItems) {
+		if (_G(_items)[i]._location == CARRIED) {
+			if (_G(_items)[i]._text[0] == 0) {
+				error("Invisible item in inventory: %d\n", i);
+				i++;
+				continue;
+			}
+			if (lastitem > -1 && (_options & (TRS80_STYLE | SPECTRUM_STYLE)) == 0) {
+				writeToRoomDescriptionStream("%s", _G(_sys)[ITEM_DELIMITER].c_str());
+			}
+			lastitem = i;
+			writeToRoomDescriptionStream("%s", _G(_items)[i]._text.c_str());
+			if (_options & (TRS80_STYLE | SPECTRUM_STYLE)) {
+				writeToRoomDescriptionStream("%s", _G(_sys)[ITEM_DELIMITER].c_str());
+			}
+		}
+		i++;
+	}
+	if (lastitem == -1) {
+		writeToRoomDescriptionStream("%s\n", _G(_sys)[NOTHING].c_str());
+	} else {
+		if (_options & TI994A_STYLE && !itemEndsWithPeriod(lastitem))
+			writeToRoomDescriptionStream(".");
+		writeToRoomDescriptionStream("\n");
+	}
+}
+
+int Scott::itemEndsWithPeriod(int item) {
+	if (item < 0 || item > _G(_gameHeader)->_numItems)
+		return 0;
+	Common::String desc = _G(_items)[item]._text;
+	if (!desc.empty() && desc[0] != 0) {
+		const char lastchar = desc[desc.size() - 1];
+		if (lastchar == '.' || lastchar == '!') {
+			return 1;
+		}
+	}
+	return 0;
+}
+
+void Scott::closeGraphicsWindow() {
+	if (_G(_graphics) == nullptr)
+		_G(_graphics) = findGlkWindowWithRock(GLK_GRAPHICS_ROCK);
+	if (_G(_graphics)) {
+		glk_window_close(_G(_graphics), nullptr);
+		_G(_graphics) = nullptr;
+		glk_window_get_size(_G(_topWindow), (uint *)&_topWidth, (uint *)&_topHeight);
+	}
+}
+
+winid_t Scott::findGlkWindowWithRock(glui32 rock) {
+	glui32 rockptr;
+	winid_t win = glk_window_iterate(nullptr, &rockptr);
+	while (win) {
+		if (rockptr == rock)
+			return win;
+		win = glk_window_iterate(win, &rockptr);
+	}
+	return 0;
+}
+
+void Scott::openGraphicsWindow() {
+	if (!glk_gestalt(gestalt_Graphics, 0))
+		return;
+	glui32 graphwidth, graphheight, optimalWidth, optimalHeight;
+
+	if (_G(_topWindow) == nullptr)
+		_G(_topWindow) = findGlkWindowWithRock(GLK_STATUS_ROCK);
+	if (_G(_graphics) == nullptr)
+		_G(_graphics) = findGlkWindowWithRock(GLK_GRAPHICS_ROCK);
+	if (_G(_graphics) == nullptr && _G(_topWindow) != nullptr) {
+		glk_window_get_size(_G(_topWindow), (uint *)&_topWidth, (uint *)&_topHeight);
+		glk_window_close(_G(_topWindow), nullptr);
+		_G(_graphics) = glk_window_open(_G(_bottomWindow), winmethod_Above | winmethod_Proportional, 60, wintype_Graphics, GLK_GRAPHICS_ROCK);
+		glk_window_get_size(_G(_graphics), &graphwidth, &graphheight);
+		_G(_pixelSize) = optimalPictureSize(&optimalWidth, &optimalHeight);
+		_G(_xOffset) = ((int)graphwidth - (int)optimalWidth) / 2;
+
+		if (graphheight > optimalHeight) {
+			winid_t parent = glk_window_get_parent(_G(_graphics));
+			glk_window_set_arrangement(parent, winmethod_Above | winmethod_Fixed, optimalHeight, nullptr);
+		}
+
+		// Set the graphics window background to match the main window background, best as we can, and clear the window.
+		glui32 backgroundColor;
+		if (glk_style_measure(_G(_bottomWindow), style_Normal, stylehint_BackColor, &backgroundColor)) {
+			glk_window_set_background_color(_G(_graphics), backgroundColor);
+			glk_window_clear(_G(_graphics));
+		}
+
+		_G(_topWindow) = glk_window_open(_G(_bottomWindow), winmethod_Above | winmethod_Fixed, _topHeight, wintype_TextGrid, GLK_STATUS_ROCK);
+		glk_window_get_size(_G(_topWindow), (uint *)&_topWidth, (uint *)&_topHeight);
+	} else {
+		if (!_G(_graphics))
+			_G(_graphics) = glk_window_open(_G(_bottomWindow), winmethod_Above | winmethod_Proportional, 60, wintype_Graphics, GLK_GRAPHICS_ROCK);
+		glk_window_get_size(_G(_graphics), &graphwidth, &graphheight);
+		_G(_pixelSize) = optimalPictureSize(&optimalWidth, &optimalHeight);
+		_G(_xOffset) = (graphwidth - optimalWidth) / 2;
+		winid_t parent = glk_window_get_parent(_G(_graphics));
+		glk_window_set_arrangement(parent, winmethod_Above | winmethod_Fixed, optimalHeight, nullptr);
+	}
+}
+
+glui32 Scott::optimalPictureSize(glui32 *width, glui32 *height) {
+	*width = 255;
+	*height = 96;
+	int multiplier = 1;
+	glui32 graphwidth, graphheight;
+	glk_window_get_size(_G(_graphics), &graphwidth, &graphheight);
+	multiplier = graphheight / 96;
+	if (static_cast<glui32>(255 * multiplier) > graphwidth)
+		multiplier = graphwidth / 255;
+
+	if (multiplier == 0)
+		multiplier = 1;
+
+	*width = 255 * multiplier;
+	*height = 96 * multiplier;
+
+	return multiplier;
+}
+
+void Scott::openTopWindow() {
+	_G(_topWindow) = findGlkWindowWithRock(GLK_STATUS_ROCK);
+	if (_G(_topWindow) == nullptr) {
+		if (_splitScreen) {
+			_G(_topWindow) = glk_window_open(_G(_bottomWindow), winmethod_Above | winmethod_Fixed, _topHeight, wintype_TextGrid, GLK_STATUS_ROCK);
+			if (_G(_topWindow) == nullptr) {
+				_splitScreen = 0;
+				_G(_topWindow) = _G(_bottomWindow);
+			} else {
+				glk_window_get_size(_G(_topWindow), (uint *)&_topWidth, nullptr);
+			}
+		} else {
+			_G(_topWindow) = _G(_bottomWindow);
+		}
+	}
+}
+
+void Scott::cleanupAndExit() {
+	if (_G(_transcript))
+		glk_stream_close(_G(_transcript), nullptr);
+	if (drawingVector()) {
+		_G(_gliSlowDraw) = 0;
+		drawSomeVectorPixels(0);
+	}
+	glk_exit();
+}
+
+void Scott::drawBlack() {
+	glk_window_fill_rect(_G(_graphics), 0, _G(_xOffset), 0, 32 * 8 * _G(_pixelSize), 12 * 8 * _G(_pixelSize));
+}
+
+void Scott::drawImage(int image) {
+	if (!glk_gestalt(gestalt_Graphics, 0))
+		return;
+	openGraphicsWindow();
+	if (_G(_graphics) == nullptr) {
+		error("DrawImage: Graphic window nullptr?\n");
+		return;
+	}
+	if (_G(_game)->_pictureFormatVersion == 99)
+		drawVectorPicture(image);
+	else
+		drawSagaPictureNumber(image);
+}
+
+void Scott::drawRoomImage() {
+	if (CURRENT_GAME == ADVENTURELAND || CURRENT_GAME == ADVENTURELAND_C64) {
+		// TODO
+		// AdventurelandDarkness();
+	}
+
+	int dark = ((_G(_bitFlags) & (1 << DARKBIT)) && _G(_items)[LIGHT_SOURCE]._location != CARRIED && _G(_items)[LIGHT_SOURCE]._location != MY_LOC);
+
+	if (dark && _G(_graphics) != nullptr && !(_G(_rooms)[MY_LOC]._image == 255)) {
+		_G(_vectorImageShown) = -1;
+		_G(_vectorState) = NO_VECTOR_IMAGE;
+		glk_request_timer_events(0);
+		drawBlack();
+		return;
+	}
+
+	switch (CURRENT_GAME) {
+	case SEAS_OF_BLOOD:
+	case SEAS_OF_BLOOD_C64:
+		// TODO
+		// SeasOfBloodRoomImage();
+		return;
+	case ROBIN_OF_SHERWOOD:
+	case ROBIN_OF_SHERWOOD_C64:
+		// TODO
+		// RobinOfSherwoodLook();
+		return;
+	case HULK:
+	case HULK_C64:
+		hulkLook();
+		return;
+	default:
+		break;
+	}
+
+	if (_G(_rooms)[MY_LOC]._image == 255) {
+		closeGraphicsWindow();
+		return;
+	}
+
+	if (dark)
+		return;
+
+	if (_G(_game)->_pictureFormatVersion == 99) {
+		drawImage(MY_LOC - 1);
+		return;
+	}
+
+	if (_G(_game)->_type == GREMLINS_VARIANT) {
+		// TODO
+		// GremlinsLook();
+	} else {
+		drawImage(_G(_rooms)[MY_LOC]._image & 127);
+	}
+	for (int ct = 0; ct <= _G(_gameHeader)->_numItems; ct++)
+		if (_G(_items)[ct]._image && _G(_items)[ct]._location == MY_LOC) {
+			if ((_G(_items)[ct]._flag & 127) == MY_LOC) {
+				drawImage(_G(_items)[ct]._image);
+				/* Draw the correct image of the bear on the beach */
+			} else if (_G(_game)->_type == SAVAGE_ISLAND_VARIANT && ct == 20 && MY_LOC == 8) {
+				drawImage(9);
+			}
+		}
+}
+
+void Scott::restartGame() {
+	if (_G(_currentCommand))
+		freeCommands();
+	restoreState(_G(_initialState));
+	_G(_justStarted) = 0;
+	_G(_stopTime) = 0;
+	glk_window_clear(_G(_bottomWindow));
+	openTopWindow();
+	_G(_shouldRestart) = 0;
+}
+
+void Scott::transcriptOn() {
+	frefid_t ref;
+
+	if (_G(_transcript)) {
+		output(_G(_sys)[TRANSCRIPT_ALREADY]);
+		return;
+	}
+
+	ref = glk_fileref_create_by_prompt(fileusage_TextMode | fileusage_Transcript, filemode_Write, 0);
+	if (ref == nullptr)
+		return;
+
+	_G(_transcript) = glk_stream_open_file_uni(ref, filemode_Write, 0);
+	glk_fileref_destroy(ref);
+
+	if (_G(_transcript) == nullptr) {
+		output(_G(_sys)[FAILED_TRANSCRIPT]);
+		return;
+	}
+
+	glui32 *startOfTranscript = toUnicode(_G(_sys)[TRANSCRIPT_START].c_str());
+	glk_put_string_stream_uni(_G(_transcript), startOfTranscript);
+	delete[] startOfTranscript;
+	glk_put_string_stream(glk_window_get_stream(_G(_bottomWindow)), _G(_sys)[TRANSCRIPT_ON].c_str());
+}
+
+void Scott::transcriptOff() {
+	if (_G(_transcript) == nullptr) {
+		output(_G(_sys)[NO_TRANSCRIPT]);
+		return;
+	}
+
+	glui32 *endOfTranscript = toUnicode(_G(_sys)[TRANSCRIPT_END].c_str());
+	glk_put_string_stream_uni(_G(_transcript), endOfTranscript);
+	delete[] endOfTranscript;
+
+	glk_stream_close(_G(_transcript), nullptr);
+	_G(_transcript) = nullptr;
+	output(_G(_sys)[TRANSCRIPT_OFF]);
+}
+
+int Scott::performExtraCommand(int extraStopTime) {
+	Command command = *_G(_currentCommand);
+	int verb = command._verb;
+	if (verb > _G(_gameHeader)->_numWords)
+		verb -= _G(_gameHeader)->_numWords;
+	int noun = command._noun;
+	if (noun > _G(_gameHeader)->_numWords)
+		noun -= _G(_gameHeader)->_numWords;
+	else if (noun) {
+		const char *nounWord = _G(_charWords)[_G(_currentCommand)->_nounWordIndex];
+		int newnoun = whichWord(nounWord, _G(_extraNouns));
+		newnoun = _G(_extraNounsKey)[newnoun];
+		if (newnoun)
+			noun = newnoun;
+	}
+
+	_G(_stopTime) = 1 + extraStopTime;
+
+	switch (verb) {
+	case RESTORE:
+		if (noun == 0 || noun == GAME) {
+			loadGame();
+			return 1;
+		}
+		break;
+	case RESTART:
+		if (noun == 0 || noun == GAME) {
+			output(_G(_sys)[ARE_YOU_SURE]);
+			if (yesOrNo()) {
+				_G(_shouldRestart) = 1;
+			}
+			return 1;
+		}
+		break;
+	case SAVE:
+		if (noun == 0 || noun == GAME) {
+			saveGame();
+			return 1;
+		}
+		break;
+	case UNDO:
+		if (noun == 0 || noun == COMMAND) {
+			restoreUndo();
+			return 1;
+		}
+		break;
+	case RAM:
+		if (noun == RAMLOAD) {
+			ramRestore();
+			return 1;
+		} else if (noun == RAMSAVE) {
+			ramSave();
+			return 1;
+		}
+		break;
+	case RAMSAVE:
+		if (noun == 0) {
+			ramSave();
+			return 1;
+		}
+		break;
+	case RAMLOAD:
+		if (noun == 0) {
+			ramRestore();
+			return 1;
+		}
+		break;
+	case SCRIPT:
+		if (noun == ON || noun == 0) {
+			transcriptOn();
+			return 1;
+		} else if (noun == OFF) {
+			transcriptOff();
+			return 1;
+		}
+		break;
+	case EXCEPT:
+		freeCommands();
+	}
+
+	_G(_stopTime) = 0;
+	return 0;
+}
+
+int Scott::yesOrNo() {
+	glk_request_char_event(_G(_bottomWindow));
+
+	event_t ev;
+	int result = 0;
+	const char y = tolower((unsigned char)_G(_sys)[YES][0]);
+	const char n = tolower((unsigned char)_G(_sys)[NO][0]);
+
+	do {
+		glk_select(&ev);
+		if (ev.type == evtype_CharInput) {
+			const char reply = tolower(ev.val1);
+			if (reply == y) {
+				result = 1;
+			} else if (reply == n) {
+				result = 2;
+			} else {
+				output(_G(_sys)[ANSWER_YES_OR_NO]);
+				glk_request_char_event(_G(_bottomWindow));
+			}
+		} else
+			updates(ev);
+	} while (result == 0);
+
+	return (result == 1);
+}
+
+void Scott::hitEnter() {
+	glk_request_char_event(_G(_bottomWindow));
+
+	event_t ev;
+	int result = 0;
+	do {
+		glk_select(&ev);
+		if (ev.type == evtype_CharInput) {
+			if (ev.val1 == keycode_Return) {
+				result = 1;
+			} else {
+				glk_request_char_event(_G(_bottomWindow));
+			}
+		} else
+			updates(ev);
+	} while (result == 0);
+
+	return;
+}
+
+void Scott::listInventory() {
+	int i = 0;
+	int lastitem = -1;
+	output(_G(_sys)[INVENTORY]);
+	while (i <= _G(_gameHeader)->_numItems) {
+		if (_G(_items)[i]._location == CARRIED) {
+			if (_G(_items)[i]._text[0] == 0) {
+				warning("Invisible item in inventory: %d\n", i);
+				i++;
+				continue;
+			}
+			if (lastitem > -1 && (_options & (TRS80_STYLE | SPECTRUM_STYLE)) == 0) {
+				output(_G(_sys)[ITEM_DELIMITER]);
+			}
+			lastitem = i;
+			output(_G(_items)[i]._text);
+			if (_options & (TRS80_STYLE | SPECTRUM_STYLE)) {
+				output(_G(_sys)[ITEM_DELIMITER]);
+			}
+		}
+		i++;
+	}
+	if (lastitem == -1)
+		output(_G(_sys)[NOTHING]);
+	else if (_options & TI994A_STYLE) {
+		if (!itemEndsWithPeriod(lastitem))
+			output(".");
+		output(" ");
+	}
+	if (_G(_transcript)) {
+		glk_put_char_stream_uni(_G(_transcript), 10);
+	}
+}
+
+void Scott::lookWithPause() {
+	char fc = _G(_rooms)[MY_LOC]._text[0];
+	if (_G(_rooms)[MY_LOC]._text.empty() || MY_LOC == 0 || fc == 0 || fc == '.' || fc == ' ')
+		return;
+	_shouldLookInTranscript = 1;
+	_pauseNextRoomDescription = 1;
+	look();
+}
+
+void Scott::doneIt() {
+	if (_splitScreen && _G(_topWindow))
+		look();
+	output("\n\n");
+	output(_G(_sys)[PLAY_AGAIN]);
+	output("\n");
+	if (yesOrNo()) {
+		_G(_shouldRestart) = 1;
+	} else {
+		cleanupAndExit();
+	}
+}
+
+int Scott::printScore() {
+	int i = 0;
+	int n = 0;
+	while (i <= _G(_gameHeader)->_numItems) {
+		if (_G(_items)[i]._location == _G(_gameHeader)->_treasureRoom && _G(_items)[i]._text[0] == '*')
+			n++;
+		i++;
+	}
+	display(_G(_bottomWindow), "%s %d %s%s %d.\n", _G(_sys)[IVE_STORED].c_str(), n, _G(_sys)[TREASURES].c_str(),
+			_G(_sys)[ON_A_SCALE_THAT_RATES].c_str(), (n * 100) / _G(_gameHeader)->_treasures);
+	if (n == _G(_gameHeader)->_treasures) {
+		output(_G(_sys)[YOUVE_SOLVED_IT].c_str());
+		doneIt();
+		return 1;
+	}
+	return 0;
+}
+
+void Scott::printNoun() {
+	if (_G(_currentCommand))
+		glk_put_string_stream_uni(glk_window_get_stream(_G(_bottomWindow)), _G(_unicodeWords)[_G(_currentCommand)->_nounWordIndex]);
+}
+
+void Scott::moveItemAToLocOfItemB(int itemA, int itemB) {
+	_G(_items)
+	[itemA]._location = _G(_items)[itemB]._location;
+	if (_G(_items)[itemB]._location == MY_LOC)
+		_shouldLookInTranscript = 1;
+}
+
+void Scott::goToStoredLoc() {
+#ifdef DEBUG_ACTIONS
+	debug("switch location to stored location (%d) (%s).\n",
+		  SavedRoom, _G(_rooms)[SavedRoom].Text);
+#endif
+	int t = MY_LOC;
+	MY_LOC = _G(_savedRoom);
+	_G(_savedRoom) = t;
+	_shouldLookInTranscript = 1;
+}
+
+void Scott::swapLocAndRoomFlag(int index) {
+#ifdef DEBUG_ACTIONS
+	debug("swap location<->roomflag[%d]\n", index);
+#endif
+	int temp = MY_LOC;
+	MY_LOC = _G(_roomSaved)[index];
+	_G(_roomSaved)[index] = temp;
+	_shouldLookInTranscript = 1;
+	look();
+}
+
+void Scott::swapItemLocations(int itemA, int itemB) {
+	int temp = _G(_items)[itemA]._location;
+	_G(_items)[itemA]._location = _G(_items)[itemB]._location;
+	_G(_items)[itemB]._location = temp;
+	if (_G(_items)[itemA]._location == MY_LOC || _G(_items)[itemB]._location == MY_LOC)
+		_shouldLookInTranscript = 1;
+}
+
+void Scott::putItemAInRoomB(int itemA, int roomB) {
+#ifdef DEBUG_ACTIONS
+	debug("Item %d (%s) is put in room %d (%s). MY_LOC: %d (%s)\n",
+		  itemA, _G(_items)[arg1].Text, roomB, _G(_rooms)[roomB].Text, MY_LOC,
+		  _G(_rooms)[MY_LOC].Text);
+#endif
+	if (_G(_items)[itemA]._location == MY_LOC)
+		lookWithPause();
+	_G(_items)[itemA]._location = roomB;
+}
+
+void Scott::swapCounters(int index) {
+#ifdef DEBUG_ACTIONS
+	debug("Select a counter.Current counter is swapped with backup "
+		  "counter %d\n",
+		  index);
+#endif
+	if (index > 15) {
+		error("ERROR! parameter out of range. Max 15, got %d\n", index);
+		index = 15;
+	}
+	int temp = _G(_currentCounter);
+
+	_G(_currentCounter) = _G(_counters)[index];
+	_G(_counters)[index] = temp;
+#ifdef DEBUG_ACTIONS
+	debug("Value of new selected counter is %d\n", _G(_currentCounter));
+#endif
+}
+
+void Scott::printMessage(int index) {
+#ifdef DEBUG_ACTIONS
+	debug("Print message %d: \"%s\"\n", index, Messages[index]);
+#endif
+	Common::String message = _G(_messages)[index];
+	if (!message.empty() && message[0] != 0) {
+		output(message);
+		const char lastchar = message[message.size() - 1];
+		if (lastchar != 13 && lastchar != 10)
+			output(_G(_sys)[MESSAGE_DELIMITER]);
+	}
+}
+
+void Scott::playerIsDead() {
+#ifdef DEBUG_ACTIONS
+	debug("Player is dead\n");
+#endif
+	output(_G(_sys)[IM_DEAD]);
+	_G(_bitFlags) &= ~(1 << DARKBIT);
+	MY_LOC = _G(_gameHeader)->_numRooms; /* It seems to be what the code says! */
+}
+
+void Scott::printTakenOrDropped(int index) {
+	output(_G(_sys[index]));
+	int length = _G(_sys)[index].size();
+	char last = _G(_sys)[index][length - 1];
+	if (last == 10 || last == 13)
+		return;
+	output(" ");
+	if ((!(_G(_currentCommand)->_allFlag & LASTALL)) || _splitScreen == 0) {
+		output("\n");
+	}
+}
+
+void Scott::printTitleScreenBuffer() {
+	glk_stream_set_current(glk_window_get_stream(_G(_bottomWindow)));
+	glk_set_style(style_User1);
+	clearScreen();
+	output(_titleScreen);
+	glk_set_style(style_Normal);
+	hitEnter();
+	clearScreen();
+}
+
+void Scott::printTitleScreenGrid() {
+	int titleLength = _titleScreen.size();
+	int rows = 0;
+	for (int i = 0; i < titleLength; i++)
+		if (_titleScreen[i] == '\n')
+			rows++;
+	winid_t titlewin = glk_window_open(_G(_bottomWindow), winmethod_Above | winmethod_Fixed, rows + 2,
+									   wintype_TextGrid, 0);
+	glui32 width, height;
+	glk_window_get_size(titlewin, &width, &height);
+	if (width < 40 || height < static_cast<glui32>(rows + 2)) {
+		glk_window_close(titlewin, nullptr);
+		printTitleScreenBuffer();
+		return;
+	}
+	int offset = (width - 40) / 2;
+	int pos = 0;
+	for (int i = 1; i <= rows; i++) {
+		glk_window_move_cursor(titlewin, offset, i);
+		while (_titleScreen[pos] != '\n' && pos < titleLength)
+			display(titlewin, "%c", _titleScreen[pos++]);
+		pos++;
+	}
+	hitEnter();
+	glk_window_close(titlewin, nullptr);
+}
+
+void writeToRoomDescriptionStream(const char *fmt, ...) {
+	if (_G(_roomDescriptionStream == nullptr))
+		return;
+	va_list ap;
+
+	va_start(ap, fmt);
+	Common::String msg = Common::String::vformat(fmt, ap);
+	va_end(ap);
+
+	g_scott->glk_put_string_stream(_G(_roomDescriptionStream), msg.c_str());
+}
+
 } // End of namespace Scott
 } // End of namespace Glk
diff --git a/engines/glk/scott/scott.h b/engines/glk/scott/scott.h
index f0f7d426c6c..23561404271 100644
--- a/engines/glk/scott/scott.h
+++ b/engines/glk/scott/scott.h
@@ -30,32 +30,49 @@
 
 #include "common/scummsys.h"
 #include "glk/glk_api.h"
+#include "glk/scott/definitions.h"
+#include "glk/scott/globals.h"
+#include <cstdint>
 
 namespace Glk {
 namespace Scott {
 
-#define LIGHT_SOURCE    9   // Always 9 how odd
-#define CARRIED     255     // Carried
-#define DESTROYED   0       // Destroyed
-#define DARKBIT     15
-#define LIGHTOUTBIT 16      // Light gone out
+struct Command;
+
+#define LIGHT_SOURCE 9 // Always 9 how odd
+#define CARRIED 255    // Carried
+#define DESTROYED 0    // Destroyed
+#define DARKBIT 15
+#define LIGHTOUTBIT 16 // Light gone out
 
 enum GameOption {
-	YOUARE      = 1,        ///< You are not I am
-	SCOTTLIGHT  = 2,        ///< Authentic Scott Adams light messages
-	DEBUGGING   = 4,        ///< Info from database load
-	TRS80_STYLE = 8,        ///< Display in style used on TRS-80
-	PREHISTORIC_LAMP = 16   ///< Destroy the lamp (very old databases)
+	YOUARE = 1,                ///< You are not I am
+	SCOTTLIGHT = 2,            ///< Authentic Scott Adams light messages
+	DEBUGGING = 4,             ///< Info from database load
+	TRS80_STYLE = 8,           ///< Display in style used on TRS-80
+	PREHISTORIC_LAMP = 16,     ///< Destroy the lamp (very old databases)
+	SPECTRUM_STYLE = 32,       ///< Display in style used on ZX Spectrum
+	TI994A_STYLE = 64,         ///< Display in style used on TI-99/4A
+	NO_DELAYS = 128,           ///< Skip all pauses
+	FORCE_PALETTE_ZX = 256,    ///< Force ZX Spectrum image palette
+	FORCE_PALETTE_C64 = 512,   ///< Force CBM 64 image palette
+	FORCE_INVENTORY = 1024,    ///< Inventory in upper window always on
+	FORCE_INVENTORY_OFF = 2048 ///< Inventory in upper window always off
 };
 
-#define TRS80_LINE  "\n<------------------------------------------------------------>\n"
-#define MY_LOC   (_gameHeader._playerRoom)
+#define GLK_BUFFER_ROCK 1
+#define GLK_STATUS_ROCK 1010
+#define GLK_GRAPHICS_ROCK 1020
+
+#define TRS80_LINE "\n<------------------------------------------------------------>\n"
+#define MY_LOC (_G(_gameHeader)->_playerRoom)
+#define CURRENT_GAME (_G(_game->_gameID))
 
 struct Header {
 	int _unknown;
 	int _numItems;
 	int _numActions;
-	int _numWords;           ///< Smaller of verb/noun is padded to same size
+	int _numWords; ///< Smaller of verb/noun is padded to same size
 	int _numRooms;
 	int _maxCarry;
 	int _playerRoom;
@@ -66,8 +83,8 @@ struct Header {
 	int _treasureRoom;
 
 	Header() : _unknown(0), _numItems(0), _numActions(0), _numWords(0), _numRooms(0),
-		_maxCarry(0), _playerRoom(0), _treasures(0), _wordLength(0), _lightTime(0),
-		_numMessages(0), _treasureRoom(0) {}
+			   _maxCarry(0), _playerRoom(0), _treasures(0), _wordLength(0), _lightTime(0),
+			   _numMessages(0), _treasureRoom(0) {}
 };
 
 struct Action {
@@ -84,8 +101,9 @@ struct Action {
 struct Room {
 	Common::String _text;
 	int _exits[6];
+	byte _image;
 
-	Room() {
+	Room() : _image(255) {
 		Common::fill(&_exits[0], &_exits[6], 0);
 	}
 };
@@ -95,8 +113,10 @@ struct Item {
 	byte _location;
 	byte _initialLoc;
 	Common::String _autoGet;
+	byte _flag;
+	byte _image;
 
-	Item() : _location(0), _initialLoc(0) {}
+	Item() : _location(0), _initialLoc(0), _flag(0), _image(0) {}
 };
 
 struct Tail {
@@ -112,55 +132,93 @@ struct Tail {
  */
 class Scott : public GlkAPI {
 private:
-	Header _gameHeader;
-	Common::Array<Item> _items;
-	Common::Array<Room> _rooms;
-	Common::StringArray _verbs;
-	Common::StringArray _nouns;
-	Common::StringArray _messages;
-	Common::Array<Action> _actions;
-	int _lightRefill;
+	Globals _globals;
 	char _nounText[16];
-	int _counters[16];   ///< Range unknown
-	int _currentCounter;
-	int _savedRoom;
-	int _roomSaved[16];  ///< Range unknown
-	int _options;        ///< Option flags set
-	int _width;          ///< Terminal width
-	int _topHeight;      ///< Height of top window
-
-	bool _splitScreen;
-	winid_t _bottomWindow, _topWindow;
-	uint32 _bitFlags;    ///< Might be >32 flags - I haven't seen >32 yet
-	int _saveSlot;		 ///< Save slot when loading savegame from launcher
+	int _options = 0;   ///< Option flags set
+	int _width = 0;     ///< Terminal width
+	int _topHeight = 0; ///< Height of top window
+	int _topWidth = 0;
+
+	bool _splitScreen = true;
+	int _saveSlot = -1;   ///< Save slot when loading savegame from launcher
+	Common::String _titleScreen;
+
+	int _shouldLookInTranscript = 0;
+	int _printLookToTranscript = 0;
+	int _pauseNextRoomDescription = 0;
+
+	strid_t _roomDescriptionStream = nullptr;
+
 private:
 	/**
 	 * Initialization code
 	 */
 	void initialize();
 
-	void display(winid_t w, const char *fmt, ...);
-	void display(winid_t w, const Common::U32String fmt, ...);
+	void updateSettings();
 	void delay(int seconds);
-	void fatal(const char *x);
 	void clearScreen(void);
 	bool randomPercent(uint n);
 	int countCarried(void);
-	const char *mapSynonym(const char *word);
-	int matchUpItem(const char *text, int loc);
+	int matchUpItem(int noun, int loc);
 	Common::String readString(Common::SeekableReadStream *f);
 	void loadDatabase(Common::SeekableReadStream *f, bool loud);
-	void output(const Common::String &a);
-	void output(const Common::U32String &a);
 	void outputNumber(int a);
 	void look(void);
 	int whichWord(const char *word, const Common::StringArray &list);
-	void lineInput(char *buf, size_t n);
-	int getInput(int *vb, int *no);
-	int performLine(int ct);
-	int performActions(int vb, int no);
+
+	ActionResultType performLine(int ct);
+	ExplicitResultType performActions(int vb, int no);
 
 	void readInts(Common::SeekableReadStream *f, size_t count, ...);
+	void writeToRoomDescriptionStream(const char *fmt, ...);
+	void flushRoomDescription(char *buf);
+	void printWindowDelimiter();
+	void listExits();
+	void listExitsSpectrumStyle();
+	void listInventoryInUpperWindow();
+	int itemEndsWithPeriod(int item);
+	void closeGraphicsWindow();
+	winid_t findGlkWindowWithRock(glui32 rock);
+	void openGraphicsWindow();
+	glui32 optimalPictureSize(glui32 *width, glui32 *height);
+	void openTopWindow();
+	void cleanupAndExit();
+	void drawBlack();
+	void drawRoomImage();
+	void restartGame();
+	void transcriptOn();
+	void transcriptOff();
+	int yesOrNo();
+	void listInventory();
+	void lookWithPause();
+	void doneIt();
+	int printScore();
+	void printNoun();
+	void moveItemAToLocOfItemB(int itemA, int itemB);
+	void goToStoredLoc();
+	void swapLocAndRoomFlag(int index);
+	void swapItemLocations(int itemA, int itemB);
+	void putItemAInRoomB(int itemA, int roomB);
+	void swapCounters(int index);
+	void printMessage(int index);
+	void playerIsDead();
+	void printTakenOrDropped(int index);
+	void printTitleScreenBuffer();
+	void printTitleScreenGrid();
+
+public:
+	void drawImage(int image);
+	void output(const Common::String &a);
+	void output(const Common::U32String &a);
+	void display(winid_t w, const char *fmt, ...);
+	void display(winid_t w, const Common::U32String fmt, ...);
+	void fatal(const char *x);
+	void hitEnter();
+	void updates(event_t ev);
+	const char *mapSynonym(int noun);
+	int performExtraCommand(int extraStopTime);
+
 public:
 	/**
 	 * Constructor
@@ -189,6 +247,8 @@ public:
 	Common::Error writeGameData(Common::WriteStream *ws) override;
 };
 
+extern Scott *g_scott;
+
 } // End of namespace Scott
 } // End of namespace Glk
 


Commit: 7b97270214c4cce5c41058799043466126236b2b
    https://github.com/scummvm/scummvm/commit/7b97270214c4cce5c41058799043466126236b2b
Author: Avijeet (am388488 at gmail.com)
Date: 2022-04-12T20:35:24-07:00

Commit Message:
GLK: SCOTT: Add game command parser.

Changed paths:
  A engines/glk/scott/command_parser.cpp
  A engines/glk/scott/command_parser.h


diff --git a/engines/glk/scott/command_parser.cpp b/engines/glk/scott/command_parser.cpp
new file mode 100644
index 00000000000..e6a58614013
--- /dev/null
+++ b/engines/glk/scott/command_parser.cpp
@@ -0,0 +1,861 @@
+/* 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 "glk/scott/command_parser.h"
+#include "glk/scott/globals.h"
+#include "glk/scott/scott.h"
+#include "common/str.h"
+
+namespace Glk {
+namespace Scott {
+
+#define MAX_WORDLENGTH 128
+#define MAX_WORDS 128
+#define MAX_BUFFER 128
+
+void freeStrings() {
+	if (_G(_firstErrorMessage) != nullptr) {
+		delete[] _G(_firstErrorMessage);
+		_G(_firstErrorMessage) = nullptr;
+	}
+	if (_G(_wordsInInput) == 0) {
+		if (_G(_unicodeWords) != nullptr || _G(_charWords) != nullptr) {
+			g_scott->fatal("ERROR! Wordcount 0 but word arrays not empty!\n");
+		}
+		return;
+	}
+	for (int i = 0; i < _G(_wordsInInput); i++) {
+		if (_G(_unicodeWords)[i] != nullptr)
+			delete _G(_unicodeWords)[i];
+		if (_G(_charWords)[i] != nullptr)
+			delete _G(_charWords)[i];
+	}
+
+	delete _G(_unicodeWords);
+	_G(_unicodeWords) = nullptr;
+	delete _G(_charWords);
+	_G(_charWords) = nullptr;
+	_G(_wordsInInput) = 0;
+}
+
+void createErrorMessage(const char *fchar, glui32 *second, const char *tchar) {
+	if (_G(_firstErrorMessage) != nullptr)
+		return;
+	glui32 *first = toUnicode(fchar);
+	glui32 *third = toUnicode(tchar);
+	glui32 buffer[MAX_BUFFER];
+	int i, j = 0, k = 0;
+	for (i = 0; first[i] != 0 && i < MAX_BUFFER; i++)
+		buffer[i] = first[i];
+	if (second != nullptr) {
+		for (j = 0; second[j] != 0 && i + j < MAX_BUFFER; j++)
+			buffer[i + j] = second[j];
+	}
+	if (third != nullptr) {
+		for (k = 0; third[k] != 0 && i + j + k < MAX_BUFFER; k++)
+			buffer[i + j + k] = third[k];
+		delete[] third;
+	}
+	int length = i + j + k;
+	_G(_firstErrorMessage) = new glui32[(length + 1) * 4];
+	memcpy(_G(_firstErrorMessage), buffer, length * 4);
+	_G(_firstErrorMessage)[length] = 0;
+	delete[] first;
+}
+
+void printPendingError(void) {
+	if (_G(_firstErrorMessage)) {
+		g_scott->glk_put_string_stream_uni(g_scott->glk_window_get_stream(_G(_bottomWindow)), _G(_firstErrorMessage));
+		delete[] _G(_firstErrorMessage);
+		_G(_firstErrorMessage) = nullptr;
+		_G(_stopTime) = 1;
+	}
+}
+
+char **LineInput(void) {
+	event_t ev;
+	glui32 unibuf[512];
+
+	do {
+		g_scott->display(_G(_bottomWindow), "\n%s", _G(_sys)[WHAT_NOW].c_str());
+		g_scott->glk_request_line_event_uni(_G(_bottomWindow), unibuf, (glui32)511, 0);
+
+		while (ev.type != evtype_Quit) {
+			g_scott->glk_select(&ev);
+			if (ev.type == evtype_Quit)
+				return nullptr;
+
+			if (ev.type == evtype_LineInput)
+				break;
+			else
+				g_scott->updates(ev);
+		}
+
+		unibuf[ev.val1] = 0;
+
+		if (_G(_transcript)) {
+			g_scott->glk_put_string_stream_uni(_G(_transcript), unibuf);
+			g_scott->glk_put_char_stream_uni(_G(_transcript), 10);
+		}
+
+		_G(_charWords) = splitIntoWords(unibuf, ev.val1);
+
+		if (_G(_wordsInInput) == 0 || _G(_charWords) == nullptr)
+			g_scott->output(_G(_sys)[HUH]);
+		else {
+			return _G(_charWords);
+		}
+
+	} while (_G(_wordsInInput) == 0 || _G(_charWords) == nullptr);
+	return nullptr;
+}
+
+int matchYMCA(glui32 *string, int length, int index) {
+	const char *ymca = "y.m.c.a.";
+	int i;
+	for (i = 0; i < 8; i++) {
+		if (i + index > length || string[index + i] != static_cast<unsigned int>(ymca[i]))
+			return i;
+	}
+	return i;
+}
+
+char **splitIntoWords(glui32 *string, int length) {
+	if (length < 1) {
+		return nullptr;
+	}
+
+	g_scott->glk_buffer_to_lower_case_uni(string, 256, length);
+	g_scott->glk_buffer_canon_normalize_uni(string, 256, length);
+
+	int startpos[MAX_WORDS];
+	int wordlength[MAX_WORDS];
+
+	int words_found = 0;
+	int word_index = 0;
+	int foundspace = 0;
+	int foundcomma = 0;
+	startpos[0] = 0;
+	wordlength[0] = 0;
+	int lastwasspace = 1;
+	for (int i = 0; string[i] != 0 && i < length && word_index < MAX_WORDS; i++) {
+		foundspace = 0;
+		switch (string[i]) {
+		case 'y': {
+			int ymca = matchYMCA(string, length, i);
+			if (ymca > 3) {
+				/* Start a new word */
+				startpos[words_found] = i;
+				wordlength[words_found] = ymca;
+				words_found++;
+				wordlength[words_found] = 0;
+				i += ymca;
+				if (i < length)
+					foundspace = 1;
+				lastwasspace = 0;
+			}
+		} break;
+			/* Unicode space and tab variants */
+		case ' ':
+		case '\t':
+		case '!':
+		case '?':
+		case '\"':
+		case 0x83:   // ¿
+		case 0x80:   // ¡
+		case 0xa0:   // non-breaking space
+		case 0x2000: // en quad
+		case 0x2001: // em quad
+		case 0x2003: // em
+		case 0x2004: // three-per-em
+		case 0x2005: // four-per-em
+		case 0x2006: // six-per-em
+		case 0x2007: // figure space
+		case 0x2009: // thin space
+		case 0x200A: // hair space
+		case 0x202f: // narrow no-break space
+		case 0x205f: // medium mathematical space
+		case 0x3000: // ideographic space
+			foundspace = 1;
+			break;
+		case '.':
+		case ',':
+			foundcomma = 1;
+			break;
+		default:
+			break;
+		}
+		if (!foundspace) {
+			if (lastwasspace || foundcomma) {
+				/* Start a new word */
+				startpos[words_found] = i;
+				words_found++;
+				wordlength[words_found] = 0;
+			}
+			wordlength[words_found - 1]++;
+			lastwasspace = 0;
+		} else {
+			/* Check if the last character of previous word was a period or comma */
+			lastwasspace = 1;
+			foundcomma = 0;
+		}
+	}
+
+	if (words_found == 0) {
+		return nullptr;
+	}
+
+	wordlength[words_found]--; /* Don't count final newline character */
+
+	/* Now we've created two arrays, one for starting postions
+	 and one for word length. Now we convert these into an array of strings */
+	glui32 **words = new glui32 *[words_found];
+	char **words8 = new char *[words_found];
+
+	for (int i = 0; i < words_found; i++) {
+		words[i] = new glui32[(wordlength[i] + 1) * 4];
+		memcpy(words[i], string + startpos[i], wordlength[i] * 4);
+		words[i][wordlength[i]] = 0;
+		words8[i] = fromUnicode(words[i], wordlength[i]);
+	}
+	_G(_unicodeWords) = words;
+	_G(_wordsInInput) = words_found;
+
+	return words8;
+}
+
+int findVerb(const char *string, Common::StringArray *list) {
+	*list = _G(_verbs);
+	int verb = whichWord(string, *list, _G(_gameHeader)->_wordLength);
+	if (verb) {
+		return verb;
+	}
+	*list = _G(_directions);
+	verb = whichWord(string, *list, _G(_gameHeader)->_wordLength);
+	if (verb) {
+		if (verb == 13)
+			verb = 4;
+		if (verb > 6)
+			verb -= 6;
+		return verb;
+	}
+	*list = _G(_abbreviations);
+	verb = whichWord(string, *list, _G(_gameHeader)->_wordLength);
+	if (verb) {
+		verb = whichWord(_G(_abbreviationsKey)[verb].c_str(), _G(_verbs), _G(_gameHeader)->_wordLength);
+		if (verb) {
+			list = &_G(_verbs);
+			return verb;
+		}
+	}
+
+	int stringlength = strlen(string);
+
+	*list = _G(_skipList);
+	verb = whichWord(string, *list, stringlength);
+	if (verb) {
+		return 0;
+	}
+	*list = _G(_nouns);
+	verb = whichWord(string, *list, _G(_gameHeader)->_wordLength);
+	if (verb) {
+		return verb;
+	}
+
+	*list = _G(_extraCommands);
+	verb = whichWord(string, *list, stringlength);
+	if (verb) {
+		verb = _G(_extraCommandsKey)[verb];
+		return verb + _G(_gameHeader)->_numWords;
+	}
+
+	*list = _G(_extraNouns);
+	verb = whichWord(string, *list, stringlength);
+	if (verb) {
+		verb = _G(_extraNounsKey)[verb];
+		return verb + _G(_gameHeader)->_numWords;
+	}
+
+	*list = _G(_delimiterList);
+	verb = whichWord(string, *list, stringlength);
+	if (!verb)
+		*list = Common::StringArray();
+	return verb;
+}
+
+int findExtraneousWords(int *index, int noun) {
+	/* Looking for extraneous words that should invalidate the command */
+	int originalIndex = *index;
+	if (*index >= _G(_wordsInInput)) {
+		return 0;
+	}
+	Common::StringArray list;
+	int verb = 0;
+	int stringlength = strlen(_G(_charWords)[*index]);
+
+	list = _G(_skipList);
+	do {
+		verb = whichWord(_G(_charWords)[*index], _G(_skipList), stringlength);
+		if (verb)
+			*index = *index + 1;
+	} while (verb && *index < _G(_wordsInInput));
+
+	if (*index >= _G(_wordsInInput))
+		return 0;
+
+	verb = findVerb(_G(_charWords)[*index], &list);
+
+	if (list == _G(_delimiterList)) {
+		if (*index > originalIndex)
+			*index = *index - 1;
+		return 0;
+	}
+
+	if (list == _G(_nouns) && noun) {
+		if (g_scott->mapSynonym(noun) == g_scott->mapSynonym(verb)) {
+			*index = *index + 1;
+			return 0;
+		}
+	}
+
+	if (list.empty()) {
+		if (*index >= _G(_wordsInInput))
+			*index = _G(_wordsInInput) - 1;
+		createErrorMessage(_G(_sys)[I_DONT_KNOW_WHAT_A].c_str(), _G(_unicodeWords)[*index], _G(_sys)[IS].c_str());
+	} else {
+		createErrorMessage(_G(_sys)[I_DONT_UNDERSTAND].c_str(), nullptr, nullptr);
+	}
+
+	return 1;
+}
+
+Command *commandFromStrings(int index, Command *previous);
+
+Command *createCommandStruct(int verb, int noun, int verbIndex, int nounIndex, Command *previous) {
+	Command *command = new Command;
+	command->_verb = verb;
+	command->_noun = noun;
+	command->_allFlag = 0;
+	command->_item = 0;
+	command->_previous = previous;
+	command->_verbWordIndex = verbIndex;
+	if (noun && nounIndex > 0) {
+		command->_nounWordIndex = nounIndex - 1;
+	} else {
+		command->_nounWordIndex = 0;
+	}
+	command->_next = commandFromStrings(nounIndex, command);
+	return command;
+}
+
+int findNoun(const char *string, Common::StringArray *list) {
+	*list = _G(_nouns);
+	int noun = whichWord(string, *list, _G(_gameHeader)->_wordLength);
+	if (noun) {
+		return noun;
+	}
+
+	*list = _G(_directions);
+	noun = whichWord(string, *list, _G(_gameHeader)->_wordLength);
+	if (noun) {
+		if (noun > 6)
+			noun -= 6;
+		*list = _G(_nouns);
+		return noun;
+	}
+
+	int stringLength = strlen(string);
+
+	*list = _G(_extraNouns);
+
+	noun = whichWord(string, *list, stringLength);
+	if (noun) {
+		noun = _G(_extraNounsKey)[noun];
+		return noun + _G(_gameHeader)->_numWords;
+	}
+
+	*list = _G(_skipList);
+	noun = whichWord(string, *list, stringLength);
+	if (noun) {
+		return 0;
+	}
+
+	*list = _G(_verbs);
+	noun = whichWord(string, *list, _G(_gameHeader)->_wordLength);
+	if (noun) {
+		return noun;
+	}
+
+	*list = _G(_delimiterList);
+	noun = whichWord(string, *list, stringLength);
+
+	if (!noun)
+		*list = Common::StringArray();
+	return 0;
+}
+
+Command *commandFromStrings(int index, Command *previous) {
+	if (index < 0 || index >= _G(_wordsInInput)) {
+		return nullptr;
+	}
+	Common::StringArray list;
+	int verb = 0;
+	int i = index;
+
+	do {
+		/* Checking if it is a verb */
+		verb = findVerb(_G(_charWords)[i++], &list);
+	} while ((list == _G(_skipList) || list == _G(_delimiterList)) && i < _G(_wordsInInput));
+
+	int verbindex = i - 1;
+
+	if (list == _G(_directions)) {
+		/* It is a direction */
+		if (verb == 0 || findExtraneousWords(&i, 0) != 0)
+			return nullptr;
+		return createCommandStruct(GO, verb, 0, i, previous);
+	}
+
+	int found_noun_at_verb_position = 0;
+	int lastverb = 0;
+
+	if (list == _G(_nouns) || list == _G(_extraNouns)) {
+		/* It is a noun */
+		/* If we find no verb, we try copying the verb from the previous command */
+		if (previous) {
+			lastverb = previous->_verb;
+		}
+		/* Unless the game is German, where we allow the noun to come before the
+		 * verb */
+		if (CURRENT_GAME != GREMLINS_GERMAN && CURRENT_GAME != GREMLINS_GERMAN_C64) {
+			if (!previous) {
+				createErrorMessage(_G(_sys)[I_DONT_KNOW_HOW_TO].c_str(), _G(_unicodeWords)[i - 1], _G(_sys)[SOMETHING].c_str());
+				return nullptr;
+			} else {
+				verbindex = previous->_verbWordIndex;
+			}
+			if (findExtraneousWords(&i, verb) != 0)
+				return nullptr;
+
+			return createCommandStruct(lastverb, verb, verbindex, i, previous);
+		} else {
+			found_noun_at_verb_position = 1;
+		}
+	}
+
+	if (list.empty() || list == _G(_skipList)) {
+		createErrorMessage(_G(_sys)[I_DONT_KNOW_HOW_TO].c_str(), _G(_unicodeWords)[i - 1], _G(_sys)[SOMETHING].c_str());
+		return nullptr;
+	}
+
+	if (i == _G(_wordsInInput)) {
+		if (lastverb) {
+			return createCommandStruct(lastverb, verb, previous->_verbWordIndex, i, previous);
+		} else if (found_noun_at_verb_position) {
+			createErrorMessage(_G(_sys)[I_DONT_KNOW_HOW_TO].c_str(), _G(_unicodeWords)[i - 1], _G(_sys)[SOMETHING].c_str());
+			return nullptr;
+		} else {
+			return createCommandStruct(verb, 0, i - 1, i, previous);
+		}
+	}
+
+	int noun = 0;
+
+	do {
+		/* Check if it is a noun */
+		noun = findNoun(_G(_charWords)[i++], &list);
+	} while (list == _G(_skipList) && i < _G(_wordsInInput));
+
+	if (list == _G(_nouns) || list == _G(_extraNouns)) {
+		/* It is a noun */
+
+		/* Check if it is an ALL followed by EXCEPT */
+		int except = 0;
+		if (list == _G(_extraNouns) && i < _G(_wordsInInput) && noun - _G(_gameHeader)->_numWords == ALL) {
+			int stringlength = strlen(_G(_charWords)[i]);
+			except = whichWord(_G(_charWords)[i], _G(_extraCommands), stringlength);
+		}
+		if (_G(_extraCommandsKey)[except] != EXCEPT && findExtraneousWords(&i, noun) != 0)
+			return nullptr;
+		if (found_noun_at_verb_position) {
+			int realverb = whichWord(_G(_charWords)[i - 1], _G(_verbs), _G(_gameHeader)->_wordLength);
+			if (realverb) {
+				noun = verb;
+				verb = realverb;
+			} else if (lastverb) {
+				noun = verb;
+				verb = lastverb;
+			}
+		}
+		return createCommandStruct(verb, noun, verbindex, i, previous);
+	}
+
+	if (list == _G(_delimiterList)) {
+		/* It is a delimiter */
+		return createCommandStruct(verb, 0, verbindex, i, previous);
+	}
+
+	if (list == _G(_verbs) && found_noun_at_verb_position) {
+		/* It is a verb */
+		/* Check if it is an ALL followed by EXCEPT */
+		int except = 0;
+		if (i < _G(_wordsInInput) && verb - _G(_gameHeader)->_numWords == ALL) {
+			int stringlength = strlen(_G(_charWords)[i]);
+			except = whichWord(_G(_charWords)[i], _G(_extraCommands), stringlength);
+		}
+		if (_G(_extraCommandsKey)[except] != EXCEPT && findExtraneousWords(&i, 0) != 0)
+			return nullptr;
+		return createCommandStruct(noun, verb, i - 1, i, previous);
+	}
+
+	createErrorMessage(_G(_sys)[I_DONT_KNOW_WHAT_A].c_str(), _G(_unicodeWords)[i - 1], _G(_sys)[IS].c_str());
+	return nullptr;
+}
+
+int createAllCommands(Command *command) {
+
+	Common::Array<int> exceptions(_G(_gameHeader)->_numItems);
+	int exceptioncount = 0;
+
+	int location = CARRIED;
+	if (command->_verb == TAKE)
+		location = MY_LOC;
+
+	Command *next = command->_next;
+	/* Check if the ALL command is followed by EXCEPT */
+	/* and if it is, build an array of items to be excepted */
+	while (next && next->_verb == _G(_gameHeader)->_numWords + EXCEPT) {
+		for (int i = 0; i <= _G(_gameHeader)->_numItems; i++) {
+			if (!_G(_items)[i]._autoGet.empty() && scumm_strnicmp(_G(_items)[i]._autoGet.c_str(), _G(_charWords)[next->_nounWordIndex], _G(_gameHeader)->_wordLength) == 0) {
+				exceptions[exceptioncount++] = i;
+			}
+		}
+		/* Remove the EXCEPT command from the linked list of commands */
+		next = next->_next;
+		delete command->_next;
+		command->_next = next;
+	}
+
+	Command *c = command;
+	int found = 0;
+	for (int i = 0; i < _G(_gameHeader)->_numItems; i++) {
+		if (!_G(_items)[i]._autoGet.empty() && _G(_items)[i]._autoGet[0] != '*' && _G(_items)[i]._location == location) {
+			int exception = 0;
+			for (int j = 0; j < exceptioncount; j++) {
+				if (exceptions[j] == i) {
+					exception = 1;
+					break;
+				}
+			}
+			if (!exception) {
+				if (found) {
+					c->_next = new Command;
+					c->_next->_previous = c;
+					c = c->_next;
+				}
+				found = 1;
+				c->_verb = command->_verb;
+				c->_noun = whichWord(_G(_items)[i]._autoGet.c_str(), _G(_nouns), _G(_gameHeader)->_wordLength);
+				c->_item = i;
+				c->_next = nullptr;
+				c->_nounWordIndex = 0;
+				c->_allFlag = 1;
+			}
+		}
+	}
+	if (found == 0) {
+		if (command->_verb == TAKE)
+			createErrorMessage(_G(_sys)[NOTHING_HERE_TO_TAKE].c_str(), nullptr, nullptr);
+		else
+			createErrorMessage(_G(_sys)[YOU_HAVE_NOTHING].c_str(), nullptr, nullptr);
+		return 0;
+	} else {
+		c->_next = next;
+		c->_allFlag = 1 | LASTALL;
+	}
+	return 1;
+}
+
+int getInput(int *vb, int *no) {
+	if (_G(_currentCommand) && _G(_currentCommand)->_next) {
+		_G(_currentCommand) = _G(_currentCommand)->_next;
+	} else {
+		printPendingError();
+		if (_G(_currentCommand))
+			freeCommands();
+		_G(_charWords) = LineInput();
+
+		if (_G(_wordsInInput) == 0 || _G(_charWords) == nullptr)
+			return 0;
+
+		_G(_currentCommand) = commandFromStrings(0, nullptr);
+	}
+
+	if (_G(_currentCommand) == nullptr) {
+		printPendingError();
+		return 1;
+	}
+
+	/* We use NumWords + verb for our extra commands */
+	/* such as UNDO and TRANSCRIPT */
+	if (_G(_currentCommand)->_verb > _G(_gameHeader)->_numWords) {
+		if (!g_scott->performExtraCommand(0)) {
+			createErrorMessage(_G(_sys)[I_DONT_UNDERSTAND].c_str(), nullptr, nullptr);
+		}
+		return 1;
+		/* And NumWords + noun for our extra nouns */
+		/* such as ALL */
+	} else if (_G(_currentCommand)->_noun > _G(_gameHeader->_numWords)) {
+		_G(_currentCommand)->_noun -= _G(_gameHeader)->_numWords;
+		if (_G(_currentCommand)->_noun == ALL) {
+			if (_G(_currentCommand)->_verb != TAKE && _G(_currentCommand)->_verb != DROP) {
+				createErrorMessage(_G(_sys)[CANT_USE_ALL].c_str(), nullptr, nullptr);
+				return 1;
+			}
+			if (!createAllCommands(_G(_currentCommand)))
+				return 1;
+		} else if (_G(_currentCommand)->_noun == IT) {
+			_G(_currentCommand)->_noun = _G(_lastNoun);
+		}
+	}
+
+	*vb = _G(_currentCommand)->_verb;
+	*no = _G(_currentCommand)->_noun;
+
+	if (*no > 6) {
+		_G(_lastNoun) = *no;
+	}
+
+	return 0;
+}
+
+void freeCommands() {
+	while (_G(_currentCommand) && _G(_currentCommand)->_previous)
+		_G(_currentCommand) = _G(_currentCommand)->_previous;
+	while (_G(_currentCommand)) {
+		Command *temp = _G(_currentCommand);
+		_G(_currentCommand) = _G(_currentCommand)->_next;
+		delete temp;
+	}
+	_G(_currentCommand) = nullptr;
+	freeStrings();
+	if (_G(_firstErrorMessage))
+		delete[] _G(_firstErrorMessage);
+	_G(_firstErrorMessage) = nullptr;
+}
+
+glui32 *toUnicode(const char *string) {
+	if (string == nullptr)
+		return nullptr;
+	glui32 unicode[2048];
+	int i;
+	int dest = 0;
+	for (i = 0; string[i] != 0 && i < 2047; i++) {
+		char c = string[i];
+		if (c == '\n')
+			c = 10;
+		glui32 unichar = (glui32)c;
+		if (_G(_game) && (CURRENT_GAME == GREMLINS_GERMAN || CURRENT_GAME == GREMLINS_GERMAN_C64)) {
+			const char d = string[i + 1];
+			if (c == 'u' && d == 'e') { // ü
+				if (!(i > 2 && string[i - 1] == 'e')) {
+					unichar = 0xfc;
+					i++;
+				}
+			} else if (c == 'o' && d == 'e') {
+				unichar = 0xf6; // ö
+				i++;
+			} else if (c == 'a' && d == 'e') {
+				unichar = 0xe4; // ä
+				i++;
+			} else if (c == 's' && d == 's') {
+				if (string[i + 2] != 'c' && string[i - 2] != 'W' && !(string[i - 1] == 'a' && string[i - 2] == 'l') && string[i + 2] != '-' && string[i - 2] != 'b') {
+					unichar = 0xdf; // ß
+					i++;
+				}
+			} else if (c == 'U' && d == 'E') {
+				unichar = 0xdc; // Ü
+				i++;
+			}
+			if (c == '\"') {
+				unichar = 0x2019; // ’
+			}
+		} else if (_G(_game) && CURRENT_GAME == GREMLINS_SPANISH) {
+			switch (c) {
+			case '\x83':
+				unichar = 0xbf; // ¿
+				break;
+			case '\x80':
+				unichar = 0xa1; // ¡
+				break;
+			case '\x82':
+				unichar = 0xfc; // ü
+				break;
+			case '{':
+				unichar = 0xe1; // á
+				break;
+			case '}':
+				unichar = 0xed; // í
+				break;
+			case '|':
+				unichar = 0xf3; // ó
+				break;
+			case '~':
+				unichar = 0xf1; // ñ
+				break;
+			case '\x84':
+				unichar = 0xe9; // é
+				break;
+			case '\x85':
+				unichar = 0xfa; // ú
+				break;
+			}
+		} else if (_G(_game) && CURRENT_GAME == TI994A) {
+			switch (c) {
+			case '@':
+				unicode[dest++] = 0xa9;
+				unichar = ' ';
+				break;
+			case '}':
+				unichar = 0xfc;
+				break;
+			case 12:
+				unichar = 0xf6;
+				break;
+			case '{':
+				unichar = 0xe4;
+				break;
+			}
+		}
+		unicode[dest++] = unichar;
+	}
+	unicode[dest] = 0;
+	glui32 *result = new glui32[(dest + 1) * 4];
+	memcpy(result, unicode, (dest + 1) * 4);
+	return result;
+}
+
+char *fromUnicode(glui32 *unicodeString, int origLength) {
+	int sourcepos = 0;
+	int destpos = 0;
+
+	char dest[MAX_WORDLENGTH];
+	glui32 unichar = unicodeString[sourcepos];
+	while (unichar != 0 && destpos < MAX_WORDLENGTH && sourcepos < origLength) {
+		switch (unichar) {
+		case '.':
+			if (origLength == 1) {
+				dest[destpos++] = 'a';
+				dest[destpos++] = 'n';
+				dest[destpos++] = 'd';
+			} else {
+				dest[destpos] = (char)unichar;
+			}
+			break;
+		case 0xf6: // ö
+			dest[destpos++] = 'o';
+			dest[destpos] = 'e';
+			break;
+		case 0xe4: // ä
+			dest[destpos++] = 'a';
+			dest[destpos] = 'e';
+			break;
+		case 0xfc: // ü
+			dest[destpos] = 'u';
+			if (CURRENT_GAME == GREMLINS_GERMAN || CURRENT_GAME == GREMLINS_GERMAN_C64) {
+				destpos++;
+				dest[destpos] = 'e';
+			}
+			break;
+		case 0xdf: // ß
+			dest[destpos++] = 's';
+			dest[destpos] = 's';
+			break;
+		case 0xed: // í
+			dest[destpos] = 'i';
+			break;
+		case 0xe1: // á
+			dest[destpos] = 'a';
+			break;
+		case 0xf3: // ó
+			dest[destpos] = 'o';
+			break;
+		case 0xf1: // ñ
+			dest[destpos] = 'n';
+			break;
+		case 0xe9: // é
+			dest[destpos] = 'e';
+			break;
+		default:
+			dest[destpos] = (char)unichar;
+			break;
+		}
+		sourcepos++;
+		destpos++;
+		unichar = unicodeString[sourcepos];
+	}
+	if (destpos == 0)
+		return nullptr;
+	char *result = new char[destpos + 1];
+	memcpy(result, dest, destpos);
+
+	result[destpos] = 0;
+	return result;
+}
+
+int recheckForExtraCommand() {
+	const char *verbWord = _G(_charWords)[_G(_currentCommand)->_verbWordIndex];
+
+	int extraVerb = whichWord(verbWord, _G(_extraCommands), _G(_gameHeader)->_wordLength);
+	if (!extraVerb) {
+		return 0;
+	}
+	int ExtraNoun = 0;
+	if (_G(_currentCommand)->_noun) {
+		const char *nounWord = _G(_charWords)[_G(_currentCommand)->_nounWordIndex];
+		ExtraNoun = whichWord(nounWord, _G(_extraNouns), strlen(nounWord));
+	}
+	_G(_currentCommand)->_verb = _G(_extraCommandsKey)[extraVerb];
+	if (ExtraNoun)
+		_G(_currentCommand)->_noun = _G(_extraNounsKey)[ExtraNoun];
+
+	return g_scott->performExtraCommand(1);
+}
+
+int whichWord(const char *word, Common::StringArray list, int wordLength) {
+	int n = 1;
+	unsigned int ne = 1;
+	const char *tp;
+	while (ne < list.size()) {
+		tp = list[ne].c_str();
+		if (*tp == '*')
+			tp++;
+		else
+			n = ne;
+		if (scumm_strnicmp(word, tp, wordLength) == 0)
+			return (n);
+		ne++;
+	}
+	return (0);
+}
+
+} // End of namespace Scott
+} // End of namespace Glk
diff --git a/engines/glk/scott/command_parser.h b/engines/glk/scott/command_parser.h
new file mode 100644
index 00000000000..b336e95e840
--- /dev/null
+++ b/engines/glk/scott/command_parser.h
@@ -0,0 +1,78 @@
+/* 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 GLK_SCOTT_PARSER
+#define GLK_SCOTT_PARSER
+
+#include "common/str-array.h"
+#include "glk/glk_types.h"
+
+namespace Glk {
+namespace Scott {
+
+#define NUMBER_OF_DIRECTIONS 14
+#define NUMBER_OF_SKIPPABLE_WORDS 18
+#define NUMBER_OF_DELIMITERS 5
+#define NUMBER_OF_EXTRA_COMMANDS 20
+#define NUMBER_OF_EXTRA_NOUNS 16
+
+struct Command {
+	int _verb;
+	int _noun;
+	int _item;
+	int _verbWordIndex;
+	int _nounWordIndex;
+	int _allFlag;
+	struct Command *_previous;
+	struct Command *_next;
+};
+
+enum ExtraCommand : int {
+	NO_COMMAND,
+	RESTART,
+	SAVE,
+	RESTORE,
+	SCRIPT,
+	ON,
+	OFF,
+	UNDO,
+	RAM,
+	RAMSAVE,
+	RAMLOAD,
+	GAME,
+	COMMAND,
+	ALL,
+	IT,
+	EXCEPT
+};
+
+char **splitIntoWords(glui32 *string, int length);
+int getInput(int *vb, int *no);
+void freeCommands();
+glui32 *toUnicode(const char *string);
+char *fromUnicode(glui32 *unicodeString, int origLength);
+int recheckForExtraCommand();
+int whichWord(const char *word, Common::StringArray list, int wordLength);
+
+} // End of namespace Scott
+} // End of namespace Glk
+
+#endif


Commit: fb7180e37960fbe4fb365aa0b17795e25c10ad50
    https://github.com/scummvm/scummvm/commit/fb7180e37960fbe4fb365aa0b17795e25c10ad50
Author: Avijeet (am388488 at gmail.com)
Date: 2022-04-12T20:35:24-07:00

Commit Message:
GLK: SCOTT: Add support for .z80 game files.

Changed paths:
  A engines/glk/scott/decompress_z80.cpp
  A engines/glk/scott/decompress_z80.h


diff --git a/engines/glk/scott/decompress_z80.cpp b/engines/glk/scott/decompress_z80.cpp
new file mode 100644
index 00000000000..1586f15bb24
--- /dev/null
+++ b/engines/glk/scott/decompress_z80.cpp
@@ -0,0 +1,904 @@
+/* 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 "glk/scott/decompress_z80.h"
+#include "common/algorithm.h"
+#include "common/array.h"
+#include "common/textconsole.h"
+
+namespace Glk {
+namespace Scott {
+
+/* Sizes of some of the arrays in the snap structure */
+const int SNAPSHOT_RAM_PAGES = 16;
+const int SNAPSHOT_SLT_PAGES = 256;
+const int SNAPSHOT_ZXATASP_PAGES = 32;
+const int SNAPSHOT_ZXCF_PAGES = 64;
+const int SNAPSHOT_DOCK_EXROM_PAGES = 8;
+const int SNAPSHOT_JOYSTICKS = 7;
+const int SNAPSHOT_DIVIDE_PAGES = 4;
+
+void *libspectrumReallocN(void *ptr, size_t nmemb, size_t size) {
+	if (nmemb > SIZE_MAX / size)
+		error("Can't reallocate to required size");
+
+	return realloc(ptr, nmemb * size);
+}
+/* Ensure there is room for `requested' characters after the current
+position `ptr' in `buffer'. If not, renew() and update the
+pointers as necessary */
+void libspectrumMakeRoom(uint8_t **dest, size_t requested, uint8_t **ptr,
+						 size_t *allocated) {
+	size_t current_length = 0;
+
+	if (*allocated == 0) {
+
+		(*allocated) = requested;
+		*dest = new uint8_t[requested];
+
+	} else {
+		current_length = *ptr - *dest;
+
+		/* If there's already enough room here, just return */
+		if (current_length + requested <= (*allocated))
+			return;
+
+		/* Make the new size the maximum of the new needed size and the
+	 old allocated size * 2 */
+		(*allocated) = current_length + requested > 2 * (*allocated)
+						   ? current_length + requested
+						   : 2 * (*allocated);
+
+		*dest = static_cast<uint8_t *>(libspectrumReallocN(*dest, *allocated, sizeof(uint8_t)));
+	}
+
+	/* Update the secondary pointer to the block */
+	*ptr = *dest + current_length;
+}
+
+struct LibspectrumSnap {
+	LibspectrumSnap() : pages(SNAPSHOT_RAM_PAGES) {}
+
+	/* Which machine are we using here? */
+	int machine;
+
+	/* Registers and the like */
+	uint16_t pc;
+
+	Common::Array<uint8_t *> pages;
+};
+
+/* Error handling */
+
+/* The various errors which can occur */
+enum LibspectrumError {
+
+	LIBSPECTRUM_ERROR_NONE = 0,
+
+	LIBSPECTRUM_ERROR_WARNING,
+	LIBSPECTRUM_ERROR_MEMORY,
+	LIBSPECTRUM_ERROR_UNKNOWN,
+	LIBSPECTRUM_ERROR_CORRUPT,
+	LIBSPECTRUM_ERROR_SIGNATURE,
+	LIBSPECTRUM_ERROR_SLT,     /* .slt data found at end of a .z80 file */
+	LIBSPECTRUM_ERROR_INVALID, /* Invalid parameter supplied */
+
+	LIBSPECTRUM_ERROR_LOGIC = -1,
+};
+
+enum LibspectrumMachine {
+
+	LIBSPECTRUM_MACHINE_48,
+	LIBSPECTRUM_MACHINE_TC2048,
+	LIBSPECTRUM_MACHINE_128,
+	LIBSPECTRUM_MACHINE_PLUS2,
+	LIBSPECTRUM_MACHINE_PENT,
+	LIBSPECTRUM_MACHINE_PLUS2A,
+	LIBSPECTRUM_MACHINE_PLUS3,
+
+	/* Used by libspectrum_tape_guess_hardware if we can't work out what
+   hardware should be used */
+	LIBSPECTRUM_MACHINE_UNKNOWN,
+
+	LIBSPECTRUM_MACHINE_16,
+	LIBSPECTRUM_MACHINE_TC2068,
+
+	LIBSPECTRUM_MACHINE_SCORP,
+	LIBSPECTRUM_MACHINE_PLUS3E,
+	LIBSPECTRUM_MACHINE_SE,
+
+	LIBSPECTRUM_MACHINE_TS2068,
+	LIBSPECTRUM_MACHINE_PENT512,
+	LIBSPECTRUM_MACHINE_PENT1024,
+	LIBSPECTRUM_MACHINE_48_NTSC,
+
+	LIBSPECTRUM_MACHINE_128E,
+
+};
+
+enum LibspectrumMachineCapability {
+	LIBSPECTRUM_MACHINE_CAPABILITY_128_MEMORY = (1u << 0),
+	LIBSPECTRUM_MACHINE_CAPABILITY_PLUS3_MEMORY = (1u << 1),
+	LIBSPECTRUM_MACHINE_CAPABILITY_TIMEX_MEMORY = (1u << 2),
+	LIBSPECTRUM_MACHINE_CAPABILITY_SCORP_MEMORY = (1u << 3),
+	LIBSPECTRUM_MACHINE_CAPABILITY_SE_MEMORY = (1u << 4),
+	LIBSPECTRUM_MACHINE_CAPABILITY_PENT512_MEMORY = (1u << 5),
+	LIBSPECTRUM_MACHINE_CAPABILITY_PENT1024_MEMORY = (1u << 6),
+};
+
+/* Given a machine type, what features does it have? */
+int libspectrumMachineCapabilities(LibspectrumMachine type) {
+	int capabilities = 0;
+
+	/* 128K Spectrum-style 0x7ffd memory paging */
+	switch (type) {
+	case LIBSPECTRUM_MACHINE_128:
+	case LIBSPECTRUM_MACHINE_PLUS2:
+	case LIBSPECTRUM_MACHINE_PLUS2A:
+	case LIBSPECTRUM_MACHINE_PLUS3:
+	case LIBSPECTRUM_MACHINE_PLUS3E:
+	case LIBSPECTRUM_MACHINE_128E:
+	case LIBSPECTRUM_MACHINE_PENT:
+	case LIBSPECTRUM_MACHINE_PENT512:
+	case LIBSPECTRUM_MACHINE_PENT1024:
+	case LIBSPECTRUM_MACHINE_SCORP:
+		/* FIXME: SE needs to have this capability to be considered a 128k machine
+		 */
+	case LIBSPECTRUM_MACHINE_SE:
+		capabilities |= LIBSPECTRUM_MACHINE_CAPABILITY_128_MEMORY;
+		break;
+	default:
+		break;
+	}
+
+	/* +3 Spectrum-style 0x1ffd memory paging */
+	switch (type) {
+	case LIBSPECTRUM_MACHINE_PLUS2A:
+	case LIBSPECTRUM_MACHINE_PLUS3:
+	case LIBSPECTRUM_MACHINE_PLUS3E:
+	case LIBSPECTRUM_MACHINE_128E:
+		capabilities |= LIBSPECTRUM_MACHINE_CAPABILITY_PLUS3_MEMORY;
+		break;
+	default:
+		break;
+	}
+
+	/* T[CS]20[46]8-style 0x00fd memory paging */
+	switch (type) {
+	case LIBSPECTRUM_MACHINE_TC2048:
+	case LIBSPECTRUM_MACHINE_TC2068:
+	case LIBSPECTRUM_MACHINE_TS2068:
+		capabilities |= LIBSPECTRUM_MACHINE_CAPABILITY_TIMEX_MEMORY;
+		break;
+	default:
+		break;
+	}
+
+	/* Scorpion-style 0x1ffd memory paging */
+	switch (type) {
+	case LIBSPECTRUM_MACHINE_SCORP:
+		capabilities |= LIBSPECTRUM_MACHINE_CAPABILITY_SCORP_MEMORY;
+		break;
+	default:
+		break;
+	}
+
+	/* SE-style 0x7ffd and 0x00fd memory paging */
+	switch (type) {
+	case LIBSPECTRUM_MACHINE_SE:
+		capabilities |= LIBSPECTRUM_MACHINE_CAPABILITY_SE_MEMORY;
+		break;
+	default:
+		break;
+	}
+
+	/* Pentagon 512-style memory paging */
+	switch (type) {
+	case LIBSPECTRUM_MACHINE_PENT512:
+	case LIBSPECTRUM_MACHINE_PENT1024:
+		capabilities |= LIBSPECTRUM_MACHINE_CAPABILITY_PENT512_MEMORY;
+		break;
+	default:
+		break;
+	}
+
+	/* Pentagon 1024-style memory paging */
+	switch (type) {
+	case LIBSPECTRUM_MACHINE_PENT1024:
+		capabilities |= LIBSPECTRUM_MACHINE_CAPABILITY_PENT1024_MEMORY;
+		break;
+	default:
+		break;
+	}
+
+	return capabilities;
+}
+
+/* Length of the basic .z80 headers */
+static const int LIBSPECTRUM_Z80_HEADER_LENGTH = 30;
+
+/* Length of the v2 extensions */
+#define LIBSPECTRUM_Z80_V2_LENGTH 23
+
+/* Length of the v3 extensions */
+#define LIBSPECTRUM_Z80_V3_LENGTH 54
+
+/* Length of xzx's extensions */
+#define LIBSPECTRUM_Z80_V3X_LENGTH 55
+
+/* The constants used for each machine type */
+enum {
+
+	/* v2 constants */
+	Z80_MACHINE_48_V2 = 0,
+	Z80_MACHINE_48_IF1_V2 = 1,
+	Z80_MACHINE_48_SAMRAM_V2 = 2,
+	Z80_MACHINE_128_V2 = 3,
+	Z80_MACHINE_128_IF1_V2 = 4,
+
+	/* v3 constants */
+	Z80_MACHINE_48 = 0,
+	Z80_MACHINE_48_IF1 = 1,
+	Z80_MACHINE_48_SAMRAM = 2,
+	Z80_MACHINE_48_MGT = 3,
+	Z80_MACHINE_128 = 4,
+	Z80_MACHINE_128_IF1 = 5,
+	Z80_MACHINE_128_MGT = 6,
+
+	/* Extensions */
+	Z80_MACHINE_PLUS3 = 7,
+	Z80_MACHINE_PLUS3_XZX_ERROR = 8,
+	Z80_MACHINE_PENTAGON = 9,
+	Z80_MACHINE_SCORPION = 10,
+	Z80_MACHINE_PLUS2 = 12,
+	Z80_MACHINE_PLUS2A = 13,
+	Z80_MACHINE_TC2048 = 14,
+	Z80_MACHINE_TC2068 = 15,
+	Z80_MACHINE_TS2068 = 128,
+
+	/* The first extension ID; anything here or greater applies to both
+	 v2 and v3 files */
+	Z80_MACHINE_FIRST_EXTENSION = Z80_MACHINE_PLUS3,
+};
+
+static LibspectrumError readHeader(const uint8_t *buffer, LibspectrumSnap *snap, const uint8_t **data, int *version, int *compressed);
+
+static LibspectrumError getMachineType(LibspectrumSnap *snap, uint8_t type, uint8_t mgt_type, int version);
+
+static LibspectrumError getMachineTypeV2(LibspectrumSnap *snap, uint8_t type);
+
+static LibspectrumError getMachineTypeV3(LibspectrumSnap *snap, uint8_t type, uint8_t mgt_type);
+
+static LibspectrumError getMachineTypeExtension(LibspectrumSnap *snap, uint8_t type);
+
+static LibspectrumError readBlocks(const uint8_t *buffer, size_t buffer_length, LibspectrumSnap *snap, int version, int compressed);
+
+static LibspectrumError readBlock(const uint8_t *buffer, LibspectrumSnap *snap, const uint8_t **next_block, const uint8_t *end, int version, int compressed);
+
+static LibspectrumError readV1Block(const uint8_t *buffer, int is_compressed, uint8_t **uncompressed, const uint8_t **next_block, const uint8_t *end);
+
+static LibspectrumError readV2Block(const uint8_t *buffer, uint8_t **block, size_t *length, int *page, const uint8_t **next_block, const uint8_t *end);
+
+static void uncompressBlock(uint8_t **dest, size_t *dest_length, const uint8_t *src, size_t src_length);
+
+void libspectrumSnapSetMachine(LibspectrumSnap *snap, int val) {
+	snap->machine = val;
+}
+
+uint8_t *libspectrumSnapPages(LibspectrumSnap *snap, int page) {
+	return snap->pages[page];
+}
+
+void libspectrumSnapSetPages(LibspectrumSnap *snap, int page, uint8_t *buf) {
+	snap->pages[page] = buf;
+}
+
+void libspectrumPrintError(LibspectrumError error) {
+	switch (error) {
+	case LIBSPECTRUM_ERROR_WARNING:
+		warning("warning");
+		break;
+	case LIBSPECTRUM_ERROR_MEMORY:
+		warning("memory error");
+		break;
+	case LIBSPECTRUM_ERROR_UNKNOWN:
+		warning("unknown error");
+		break;
+	case LIBSPECTRUM_ERROR_CORRUPT:
+		warning("corruption error");
+		break;
+	case LIBSPECTRUM_ERROR_SIGNATURE:
+		warning("signature error");
+		break;
+	case LIBSPECTRUM_ERROR_SLT:
+		warning("SLT data in Z80 error");
+		break;
+	case LIBSPECTRUM_ERROR_INVALID:
+		warning("invalid parameter error");
+		break;
+	case LIBSPECTRUM_ERROR_LOGIC:
+		warning("logic error");
+		break;
+	default:
+		warning("unhandled error");
+		break;
+	}
+}
+
+int libspectrumSnapMachine(LibspectrumSnap *snap) {
+	return snap->machine;
+}
+
+/* Read an LSB dword from buffer */
+uint32_t libspectrumReadDword(const uint8_t **buffer) {
+	uint32_t value;
+
+	value = (*buffer)[0] + (*buffer)[1] * 0x100 + (*buffer)[2] * 0x10000 + (*buffer)[3] * 0x1000000;
+
+	(*buffer) += 4;
+
+	return value;
+}
+
+static LibspectrumError getMachineTypeExtension(LibspectrumSnap *snap, uint8_t type) {
+	switch (type) {
+	case Z80_MACHINE_PLUS3:
+	case Z80_MACHINE_PLUS3_XZX_ERROR:
+		libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_PLUS3);
+		break;
+	case Z80_MACHINE_PENTAGON:
+		libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_PENT);
+		break;
+	case Z80_MACHINE_SCORPION:
+		libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_SCORP);
+		break;
+	case Z80_MACHINE_PLUS2:
+		libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_PLUS2);
+		break;
+	case Z80_MACHINE_PLUS2A:
+		libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_PLUS2A);
+		break;
+	case Z80_MACHINE_TC2048:
+		libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_TC2048);
+		break;
+	case Z80_MACHINE_TC2068:
+		libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_TC2068);
+		break;
+	case Z80_MACHINE_TS2068:
+		libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_TS2068);
+		break;
+	default:
+		libspectrumPrintError(LIBSPECTRUM_ERROR_UNKNOWN);
+		warning("%s:get_machine_type: unknown extension machine type %d", __FILE__, type);
+		return LIBSPECTRUM_ERROR_UNKNOWN;
+	}
+
+	return LIBSPECTRUM_ERROR_NONE;
+}
+
+LibspectrumError internalZ80Read(LibspectrumSnap *snap, const uint8_t *buffer, size_t buffer_length);
+
+uint8_t *decompressZ80(uint8_t *raw_data, size_t length) {
+	LibspectrumSnap *snap = new LibspectrumSnap;
+	for (int i = 0; i < SNAPSHOT_RAM_PAGES; i++)
+		libspectrumSnapSetPages(snap, i, nullptr);
+
+	if (internalZ80Read(snap, raw_data, length) != LIBSPECTRUM_ERROR_NONE) {
+		return nullptr;
+	}
+
+	uint8_t *uncompressed = new uint8_t[0xC000];
+	if (uncompressed == nullptr)
+		return nullptr;
+	Common::copy(snap->pages[5], snap->pages[5] + 0x4000, uncompressed);
+	Common::copy(snap->pages[2], snap->pages[2] + 0x4000, uncompressed + 0x4000);
+	Common::copy(snap->pages[0], snap->pages[0] + 0x4000, uncompressed + 0x8000);
+
+	for (int i = 0; i < SNAPSHOT_RAM_PAGES; i++)
+		if (snap->pages[i] != nullptr)
+			delete snap->pages[i];
+	delete snap;
+
+	return uncompressed;
+}
+
+LibspectrumError internalZ80Read(LibspectrumSnap *snap, const uint8_t *buffer, size_t buffer_length) {
+	LibspectrumError error;
+	const uint8_t *data;
+	int version, compressed = 1;
+
+	error = readHeader(buffer, snap, &data, &version, &compressed);
+	if (error != LIBSPECTRUM_ERROR_NONE)
+		return error;
+
+	error = readBlocks(data, buffer_length - (data - buffer), snap, version,
+					   compressed);
+	if (error != LIBSPECTRUM_ERROR_NONE)
+		return error;
+
+	return LIBSPECTRUM_ERROR_NONE;
+}
+
+static LibspectrumError readHeader(const uint8_t *buffer, LibspectrumSnap *snap, const uint8_t **data, int *version, int *compressed) {
+	const uint8_t *header = buffer;
+	LibspectrumError error;
+
+	uint8_t header_6 = header[6];
+	uint8_t header_7 = header[7] * 0x100;
+	snap->pc = header_6 + header_7;
+
+	if (snap->pc == 0) { /* PC == 0x0000 => v2 or greater */
+
+		size_t extra_length;
+		const uint8_t *extra_header;
+
+		extra_length = header[LIBSPECTRUM_Z80_HEADER_LENGTH] + header[LIBSPECTRUM_Z80_HEADER_LENGTH + 1] * 0x100;
+
+		switch (extra_length) {
+		case LIBSPECTRUM_Z80_V2_LENGTH:
+			*version = 2;
+			break;
+		case LIBSPECTRUM_Z80_V3_LENGTH:
+		case LIBSPECTRUM_Z80_V3X_LENGTH:
+			*version = 3;
+			break;
+		default:
+			libspectrumPrintError(LIBSPECTRUM_ERROR_UNKNOWN);
+			warning("libspectrumReadZ80Header: unknown header length %d", (int)extra_length);
+			return LIBSPECTRUM_ERROR_UNKNOWN;
+		}
+
+		extra_header = buffer + LIBSPECTRUM_Z80_HEADER_LENGTH + 2;
+
+		snap->pc = extra_header[0] + extra_header[1] * 0x100;
+
+		error = getMachineType(snap, extra_header[2], extra_header[51], *version);
+		if (error)
+			return error;
+
+		if (extra_header[5] & 0x80) {
+
+			switch (libspectrumSnapMachine(snap)) {
+			case LIBSPECTRUM_MACHINE_48:
+				libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_16);
+				break;
+			case LIBSPECTRUM_MACHINE_128:
+				libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_PLUS2);
+				break;
+			case LIBSPECTRUM_MACHINE_PLUS3:
+				libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_PLUS2A);
+				break;
+			default:
+				break; /* Do nothing */
+			}
+		}
+
+		(*data) = buffer + LIBSPECTRUM_Z80_HEADER_LENGTH + 2 + extra_length;
+
+	} else { /* v1 .z80 file */
+
+		libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_48);
+		*version = 1;
+
+		/* Need to flag this for later */
+		*compressed = (header[12] & 0x20) ? 1 : 0;
+
+		(*data) = buffer + LIBSPECTRUM_Z80_HEADER_LENGTH;
+	}
+
+	return LIBSPECTRUM_ERROR_NONE;
+}
+
+static LibspectrumError getMachineType(LibspectrumSnap *snap, uint8_t type, uint8_t mgt_type, int version) {
+	LibspectrumError error;
+
+	if (type < Z80_MACHINE_FIRST_EXTENSION) {
+		switch (version) {
+		case 2:
+			error = getMachineTypeV2(snap, type);
+			if (error)
+				return error;
+			break;
+		case 3:
+			error = getMachineTypeV3(snap, type, mgt_type);
+			if (error)
+				return error;
+			break;
+		default:
+			libspectrumPrintError(LIBSPECTRUM_ERROR_LOGIC);
+			warning("%s:getMachineType: unknown version %d", __FILE__, version);
+			return LIBSPECTRUM_ERROR_LOGIC;
+		}
+
+	} else {
+		error = getMachineTypeExtension(snap, type);
+		if (error)
+			return error;
+	}
+
+	return LIBSPECTRUM_ERROR_NONE;
+}
+
+static LibspectrumError getMachineTypeV2(LibspectrumSnap *snap, uint8_t type) {
+	switch (type) {
+	case Z80_MACHINE_48_V2:
+	case Z80_MACHINE_48_SAMRAM_V2:
+		libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_48);
+		break;
+	case Z80_MACHINE_48_IF1_V2:
+		libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_48);
+		break;
+	case Z80_MACHINE_128_V2:
+		libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_128);
+		break;
+	case Z80_MACHINE_128_IF1_V2:
+		libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_128);
+		break;
+	default:
+		libspectrumPrintError(LIBSPECTRUM_ERROR_UNKNOWN);
+		warning("%s: getMachineType: unknown v2 machine type %d", __FILE__, type);
+		return LIBSPECTRUM_ERROR_UNKNOWN;
+	}
+
+	return LIBSPECTRUM_ERROR_NONE;
+}
+
+static LibspectrumError getMachineTypeV3(LibspectrumSnap *snap, uint8_t type, uint8_t mgt_type) {
+	switch (type) {
+	case Z80_MACHINE_48:
+	case Z80_MACHINE_48_SAMRAM:
+		libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_48);
+		break;
+	case Z80_MACHINE_48_IF1:
+		libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_48);
+		break;
+	case Z80_MACHINE_48_MGT:
+		libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_48);
+		break;
+	case Z80_MACHINE_128:
+		libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_128);
+		break;
+	case Z80_MACHINE_128_MGT:
+		libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_128);
+		break;
+	default:
+		libspectrumPrintError(LIBSPECTRUM_ERROR_UNKNOWN);
+		warning("%s:getMachineType: unknown v3 machine type %d", __FILE__, type);
+		return LIBSPECTRUM_ERROR_UNKNOWN;
+	}
+
+	return LIBSPECTRUM_ERROR_NONE;
+}
+
+/* Given a 48K memory dump `data', place it into the
+ appropriate bits of `snap' for a 48K machine */
+LibspectrumError libspectrumSplitTo48kPages(LibspectrumSnap *snap, const uint8_t *data) {
+	uint8_t *buffer[3];
+	size_t i;
+
+	/* If any of the three pages are already occupied, barf */
+	if (libspectrumSnapPages(snap, 5) || libspectrumSnapPages(snap, 2) || libspectrumSnapPages(snap, 0)) {
+		libspectrumPrintError(LIBSPECTRUM_ERROR_LOGIC);
+		warning("libspectrumSplitTo48kPages: RAM page already in use");
+		return LIBSPECTRUM_ERROR_LOGIC;
+	}
+
+	for (i = 0; i < 3; i++) {
+		buffer[i] = new uint8_t[0x4000];
+	}
+
+	libspectrumSnapSetPages(snap, 5, buffer[0]);
+	libspectrumSnapSetPages(snap, 2, buffer[1]);
+	libspectrumSnapSetPages(snap, 0, buffer[2]);
+
+	/* Finally, do the copies... */
+	Common::copy(&data[0x0000], &data[0x0000] + 0x4000, libspectrumSnapPages(snap, 5));
+	Common::copy(&data[0x4000], &data[0x4000] + 0x4000, libspectrumSnapPages(snap, 2));
+	Common::copy(&data[0x8000], &data[0x8000] + 0x4000, libspectrumSnapPages(snap, 0));
+
+	return LIBSPECTRUM_ERROR_NONE;
+}
+
+static LibspectrumError readBlocks(const uint8_t *buffer, size_t buffer_length, LibspectrumSnap *snap, int version, int compressed) {
+	const uint8_t *end, *next_block;
+
+	end = buffer + buffer_length;
+	next_block = buffer;
+
+	while (next_block < end) {
+		LibspectrumError error;
+		error = readBlock(next_block, snap, &next_block, end, version, compressed);
+		if (error != LIBSPECTRUM_ERROR_NONE)
+			return error;
+	}
+
+	return LIBSPECTRUM_ERROR_NONE;
+}
+
+static LibspectrumError readBlock(const uint8_t *buffer, LibspectrumSnap *snap, const uint8_t **next_block, const uint8_t *end, int version, int compressed) {
+	LibspectrumError error;
+	uint8_t *uncompressed;
+
+	int capabilities = libspectrumMachineCapabilities(static_cast<LibspectrumMachine>(libspectrumSnapMachine(snap)));
+
+	if (version == 1) {
+		error = readV1Block(buffer, compressed, &uncompressed, next_block, end);
+		if (error != LIBSPECTRUM_ERROR_NONE)
+			return error;
+
+		libspectrumSplitTo48kPages(snap, uncompressed);
+
+		delete uncompressed;
+
+	} else {
+
+		size_t length;
+		int page;
+
+		error = readV2Block(buffer, &uncompressed, &length, &page, next_block, end);
+		if (error != LIBSPECTRUM_ERROR_NONE)
+			return error;
+
+		if (page <= 0 || page > 18) {
+			libspectrumPrintError(LIBSPECTRUM_ERROR_UNKNOWN);
+			warning("readBlock: unknown page %d", page);
+			delete uncompressed;
+			return LIBSPECTRUM_ERROR_UNKNOWN;
+		}
+
+		/* If it's a ROM page, just throw it away */
+		if (page < 3) {
+			delete uncompressed;
+			return LIBSPECTRUM_ERROR_NONE;
+		}
+
+		/* Page 11 is the Multiface ROM unless we're emulating something
+	   Scorpion-like */
+		if (page == 11 && !(capabilities & LIBSPECTRUM_MACHINE_CAPABILITY_SCORP_MEMORY)) {
+			delete uncompressed;
+			return LIBSPECTRUM_ERROR_NONE;
+		}
+
+		/* Deal with 48K snaps -- first, throw away page 3, as it's a ROM.
+	   Then remap the numbers slightly */
+		if (!(capabilities & LIBSPECTRUM_MACHINE_CAPABILITY_128_MEMORY)) {
+			switch (page) {
+			case 3:
+				delete uncompressed;
+				return LIBSPECTRUM_ERROR_NONE;
+			case 4:
+				page = 5;
+				break;
+			case 5:
+				page = 3;
+				break;
+			}
+		}
+
+		/* Now map onto RAM page numbers */
+		page -= 3;
+
+		if (libspectrumSnapPages(snap, page) == nullptr) {
+			libspectrumSnapSetPages(snap, page, uncompressed);
+		} else {
+			delete uncompressed;
+			libspectrumPrintError(LIBSPECTRUM_ERROR_UNKNOWN);
+			warning("readBlock: page %d duplicated", page);
+			return LIBSPECTRUM_ERROR_CORRUPT;
+		}
+	}
+
+	return LIBSPECTRUM_ERROR_NONE;
+}
+
+static LibspectrumError readV1Block(const uint8_t *buffer, int is_compressed, uint8_t **uncompressed, const uint8_t **next_block, const uint8_t *end) {
+	if (is_compressed) {
+		const uint8_t *ptr;
+		int state;
+		size_t uncompressed_length = 0;
+
+		state = 0;
+		ptr = buffer;
+
+		while (state != 4) {
+			if (ptr == end) {
+				libspectrumPrintError(LIBSPECTRUM_ERROR_CORRUPT);
+				warning("readV1Block: end marker not found");
+				return LIBSPECTRUM_ERROR_CORRUPT;
+			}
+
+			switch (state) {
+			case 0:
+				switch (*ptr++) {
+				case 0x00:
+					state = 1;
+					break;
+				default:
+					state = 0;
+					break;
+				}
+				break;
+			case 1:
+				switch (*ptr++) {
+				case 0x00:
+					state = 1;
+					break;
+				case 0xed:
+					state = 2;
+					break;
+				default:
+					state = 0;
+					break;
+				}
+				break;
+			case 2:
+				switch (*ptr++) {
+				case 0x00:
+					state = 1;
+					break;
+				case 0xed:
+					state = 3;
+					break;
+				default:
+					state = 0;
+					break;
+				}
+				break;
+			case 3:
+				switch (*ptr++) {
+				case 0x00:
+					state = 4;
+					break;
+				default:
+					state = 0;
+					break;
+				}
+				break;
+			default:
+				libspectrumPrintError(LIBSPECTRUM_ERROR_LOGIC);
+				warning("readV1Block: unknown state %d", state);
+				return LIBSPECTRUM_ERROR_LOGIC;
+			}
+		}
+
+		/* Length passed here is reduced by 4 to remove the end marker */
+		uncompressBlock(uncompressed, &uncompressed_length, buffer, (ptr - buffer - 4));
+
+		/* Uncompressed data must be exactly 48Kb long */
+		if (uncompressed_length != 0xc000) {
+			delete *uncompressed;
+			libspectrumPrintError(LIBSPECTRUM_ERROR_CORRUPT);
+			warning("readV1Block: data does not uncompress to 48Kb");
+			return LIBSPECTRUM_ERROR_CORRUPT;
+		}
+
+		*next_block = ptr;
+
+	} else { /* Snap isn't compressed */
+		/* Check we've got enough bytes to read */
+		if (end - *next_block < 0xc000) {
+			libspectrumPrintError(LIBSPECTRUM_ERROR_CORRUPT);
+			warning("readV1Block: not enough data in buffer");
+			return LIBSPECTRUM_ERROR_CORRUPT;
+		}
+
+		*uncompressed = new uint8_t[0xC000];
+		Common::copy(buffer, buffer + 0xC000, *uncompressed);
+		*next_block = buffer + 0xc000;
+	}
+
+	return LIBSPECTRUM_ERROR_NONE;
+}
+
+/* The signature used to designate the .slt extensions */
+static uint8_t slt_signature[] = "\0\0\0SLT";
+static size_t slt_signature_length = 6;
+
+static LibspectrumError readV2Block(const uint8_t *buffer, uint8_t **block, size_t *length, int *page, const uint8_t **next_block, const uint8_t *end) {
+	size_t length2;
+	length2 = buffer[0] + buffer[1] * 0x100;
+	(*page) = buffer[2];
+
+	if (length2 == 0 && *page == 0) {
+		if (buffer + 8 < end && !memcmp(buffer, slt_signature, slt_signature_length)) {
+			/* Ah, we have what looks like SLT data... */
+			*next_block = buffer + 6;
+			return LIBSPECTRUM_ERROR_SLT;
+		}
+	}
+
+	/* A length of 0xffff => 16384 bytes of uncompressed data */
+	if (length2 != 0xffff) {
+		/* Check we're not going to run over the end of the buffer */
+		if (buffer + 3 + length2 > end) {
+			libspectrumPrintError(LIBSPECTRUM_ERROR_CORRUPT);
+			warning("readV2Block: not enough data in buffer");
+			return LIBSPECTRUM_ERROR_CORRUPT;
+		}
+
+		*length = 0;
+		uncompressBlock(block, length, buffer + 3, length2);
+		*next_block = buffer + 3 + length2;
+
+	} else { /* Uncompressed block */
+		/* Check we're not going to run over the end of the buffer */
+		if (buffer + 3 + 0x4000 > end) {
+			libspectrumPrintError(LIBSPECTRUM_ERROR_CORRUPT);
+			warning("readV2Block: not enough data in buffer");
+			return LIBSPECTRUM_ERROR_CORRUPT;
+		}
+
+		*block = new uint8_t[0x4000];
+		Common::copy(buffer + 3, buffer + 3 + 0x4000, *block);
+
+		*length = 0x4000;
+		*next_block = buffer + 3 + 0x4000;
+	}
+
+	return LIBSPECTRUM_ERROR_NONE;
+}
+
+static void uncompressBlock(uint8_t **dest, size_t *dest_length, const uint8_t *src, size_t src_length) {
+	const uint8_t *in_ptr;
+	uint8_t *out_ptr;
+
+	/* Allocate memory for dest if requested */
+	if (*dest_length == 0) {
+		*dest_length = src_length / 2;
+		*dest = new uint8_t[*dest_length];
+	}
+
+	in_ptr = src;
+	out_ptr = *dest;
+
+	while (in_ptr < src + src_length) {
+		/* If we're pointing at the last byte, just copy it across and exit */
+		if (in_ptr == src + src_length - 1) {
+			libspectrumMakeRoom(dest, 1, &out_ptr, dest_length);
+			*out_ptr++ = *in_ptr++;
+			continue;
+		}
+
+		/* If we're pointing at two successive 0xed bytes, that's a run. If not, just copy the byte across */
+		if (*in_ptr == 0xed && *(in_ptr + 1) == 0xed) {
+			size_t run_length;
+			uint8_t repeated;
+
+			in_ptr += 2;
+			run_length = *in_ptr++;
+			repeated = *in_ptr++;
+
+			libspectrumMakeRoom(dest, run_length, &out_ptr, dest_length);
+
+			while (run_length--) {
+				*out_ptr++ = repeated;
+			}
+		} else {
+			libspectrumMakeRoom(dest, 1, &out_ptr, dest_length);
+			*out_ptr++ = *in_ptr++;
+		}
+	}
+
+	*dest_length = out_ptr - *dest;
+}
+
+} // End of namespace Scott
+} // End of namespace Glk
diff --git a/engines/glk/scott/decompress_z80.h b/engines/glk/scott/decompress_z80.h
new file mode 100644
index 00000000000..4aa8e3c88ea
--- /dev/null
+++ b/engines/glk/scott/decompress_z80.h
@@ -0,0 +1,36 @@
+/* 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 GLK_SCOTT_DECOMPRESSZ80
+#define GLK_SCOTT_DECOMPRESSZ80
+
+#include <cstdint>
+
+namespace Glk {
+namespace Scott {
+
+// Will return nullptr on error or 0xc000 (49152) bytes of uncompressed raw data on success
+uint8_t *decompressZ80(uint8_t *rawData, size_t length);
+
+} // End of namespace Scott
+} // End of namespace Glk
+
+#endif


Commit: 6d6d4fd4345d6c91f8258216091e4065defa49c3
    https://github.com/scummvm/scummvm/commit/6d6d4fd4345d6c91f8258216091e4065defa49c3
Author: Avijeet (am388488 at gmail.com)
Date: 2022-04-12T20:35:24-07:00

Commit Message:
GLK: SCOTT: Support games with compressed text data.

Changed paths:
  A engines/glk/scott/decompress_text.cpp
  A engines/glk/scott/decompress_text.h


diff --git a/engines/glk/scott/decompress_text.cpp b/engines/glk/scott/decompress_text.cpp
new file mode 100644
index 00000000000..a972ac09e9d
--- /dev/null
+++ b/engines/glk/scott/decompress_text.cpp
@@ -0,0 +1,111 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 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 "glk/scott/decompress_text.h"
+#include "common/str.h"
+
+namespace Glk {
+namespace Scott {
+
+int rotateLeftWithCarry(uint8_t *byte, int lastCarry) {
+	int carry = ((*byte & 0x80) > 0);
+	*byte = *byte << 1;
+	if (lastCarry)
+		*byte = *byte | 0x01;
+	return carry;
+}
+
+int decompressOne(uint8_t *bytes) {
+	uint8_t result = 0;
+	int carry;
+	for (int i = 0; i < 5; i++) {
+		carry = 0;
+		for (int j = 0; j < 5; j++) {
+			carry = rotateLeftWithCarry(bytes + 4 - j, carry);
+		}
+		rotateLeftWithCarry(&result, carry);
+	}
+	return result;
+}
+
+char *decompressText(uint8_t *source, int stringIndex) {
+	// Lookup table
+	Common::String alphabet = " abcdefghijklmnopqrstuvwxyz'\x01,.\x00";
+
+	int pos, c, uppercase, i, j;
+	uint8_t decompressed[256];
+	uint8_t buffer[5];
+	int idx = 0;
+
+	// Find the start of the compressed message
+	for (i = 0; i < stringIndex; i++) {
+		pos = *source;
+		pos = pos & 0x7F;
+		source += pos;
+	};
+
+	uppercase = ((*source & 0x40) == 0); // Test bit 6
+
+	source++;
+	do {
+		// Get five compressed bytes
+		for (i = 0; i < 5; i++) {
+			buffer[i] = *source++;
+		}
+		for (j = 0; j < 8; j++) {
+			// Decompress one character:
+			int next = decompressOne(buffer);
+
+			c = alphabet[next];
+
+			if (c == 0x01) {
+				uppercase = 1;
+				c = ' ';
+			}
+
+			if (c >= 'a' && uppercase) {
+				c = toupper(c);
+				uppercase = 0;
+			}
+			decompressed[idx++] = c;
+
+			if (idx > 255)
+				return nullptr;
+
+			if (idx == 255)
+				c = 0; // We've gone too far, return
+
+			if (c == 0) {
+				char *result = new char[idx];
+				memcpy(result, decompressed, idx);
+				return result;
+			} else if (c == '.' || c == ',') {
+				if (c == '.')
+					uppercase = 1;
+				decompressed[idx++] = ' ';
+			}
+		}
+	} while (idx < 0xff); // Chosen arbitrarily, might be too small
+	return nullptr;
+}
+
+} // End of namespace Scott
+} // End of namespace Glk
diff --git a/engines/glk/scott/decompress_text.h b/engines/glk/scott/decompress_text.h
new file mode 100644
index 00000000000..d90342f02c8
--- /dev/null
+++ b/engines/glk/scott/decompress_text.h
@@ -0,0 +1,35 @@
+/* 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 GLK_SCOTT_DECOMPRESSTEXT
+#define GLK_SCOTT_DECOMPRESSTEXT
+
+#include <stdint.h>
+
+namespace Glk {
+namespace Scott {
+
+char *decompressText(uint8_t *source, int stringIndex);
+
+} // End of namespace Scott
+} // End of namespace Glk
+
+#endif


Commit: abecb9a38804acb3700f30ddf1c12d32b8473d29
    https://github.com/scummvm/scummvm/commit/abecb9a38804acb3700f30ddf1c12d32b8473d29
Author: Avijeet (am388488 at gmail.com)
Date: 2022-04-12T20:35:24-07:00

Commit Message:
GLK: SCOTT: Add game detection and loading for ZX Spectrum games.

Changed paths:
  A engines/glk/scott/detect_game.cpp
  A engines/glk/scott/detect_game.h


diff --git a/engines/glk/scott/detect_game.cpp b/engines/glk/scott/detect_game.cpp
new file mode 100644
index 00000000000..42159e878e0
--- /dev/null
+++ b/engines/glk/scott/detect_game.cpp
@@ -0,0 +1,1260 @@
+/* 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 "glk/scott/command_parser.h"
+#include "glk/scott/detect_game.h"
+#include "glk/scott/decompress_text.h"
+#include "glk/scott/decompress_z80.h"
+#include "glk/scott/detection.h"
+#include "glk/scott/detection_tables.h"
+#include "glk/scott/game_info.h"
+#include "glk/scott/globals.h"
+#include "glk/scott/hulk.h"
+#include "glk/scott/line_drawing.h"
+#include "glk/scott/saga_draw.h"
+#include "glk/scott/scott.h"
+#include "common/str.h"
+
+namespace Glk {
+namespace Scott {
+
+struct DictionaryKey {
+	DictionaryType _dict;
+	const char *_signature;
+};
+
+DictionaryKey g_dictKeys[] = {
+	{FOUR_LETTER_UNCOMPRESSED, "AUTO\0GO\0"},
+	{THREE_LETTER_UNCOMPRESSED, "AUT\0GO\0"},
+	{FIVE_LETTER_UNCOMPRESSED, "AUTO\0\0GO"},
+	{FOUR_LETTER_COMPRESSED, "aUTOgO\0"},
+	{GERMAN, "\xc7"
+			 "EHENSTEIGE"},
+	{FIVE_LETTER_COMPRESSED, "gEHENSTEIGE"}, // Gremlins C64
+	{SPANISH, "ANDAENTRAVAN"},
+	{FIVE_LETTER_UNCOMPRESSED, "*CROSS*RUN\0\0"}, // Claymorgue
+	{ITALIAN, "AUTO\0VAI\0\0*ENTR"}
+};
+
+void readHeader(uint8_t *ptr) {
+	int i, value;
+	for (i = 0; i < 24; i++) {
+		value = *ptr + 256 * *(ptr + 1);
+		_G(_header)[i] = value;
+		ptr += 2;
+	}
+}
+
+int sanityCheckHeader(void) {
+	int16_t v = _G(_gameHeader)->_numItems;
+	if (v < 10 || v > 500)
+		return 0;
+	v = _G(_gameHeader)->_numActions;
+	if (v < 100 || v > 500)
+		return 0;
+	v = _G(_gameHeader)->_numWords; // word pairs
+	if (v < 50 || v > 190)
+		return 0;
+	v = _G(_gameHeader)->_numRooms; // Number of rooms
+	if (v < 10 || v > 100)
+		return 0;
+
+	return 1;
+}
+
+uint8_t *readDictionary(GameInfo info, uint8_t **pointer, int loud) {
+	uint8_t *ptr = *pointer;
+	char *dictword = new char[info._wordLength + 2];
+	char c = 0;
+	int wordnum = 0;
+	int charindex = 0;
+
+	int nw = info._numberOfWords;
+
+	int nv = info._numberOfVerbs;
+	int nn = info._numberOfNouns;
+
+	for (int i = 0; i <= MAX(nn, nw) - nv; i++) {
+		_G(_verbs)[nv + i] = ".\0";
+	}
+
+	for (int i = 0; i <= MAX(nn, nw) - nn; i++) {
+		_G(_nouns)[nn + i] = ".\0";
+	}
+
+	do {
+		for (int i = 0; i < info._wordLength; i++) {
+			c = *(ptr++);
+
+			if (info._dictionary == FOUR_LETTER_COMPRESSED || info._dictionary == FIVE_LETTER_COMPRESSED) {
+				if (charindex == 0) {
+					if (c >= 'a') {
+						c = toupper(c);
+					} else if (c != '.' && c != 0) {
+						dictword[charindex++] = '*';
+					}
+				}
+				dictword[charindex++] = c;
+			} else if (info._subType & LOCALIZED) {
+				if (charindex == 0) {
+					if (c & 0x80) {
+						c = c & 0x7f;
+					} else if (c != '.') {
+						dictword[charindex++] = '*';
+					}
+				}
+				dictword[charindex++] = c;
+			} else {
+				if (c == 0) {
+					if (charindex == 0) {
+						c = *(ptr++);
+					}
+				}
+				if (c != ' ' && charindex > 0 && dictword[charindex - 1] == ' ') {
+					i--;
+					charindex--;
+				}
+				if (c == '*') {
+					if (charindex != 0)
+						charindex = 0;
+					i = -1;
+				}
+				dictword[charindex++] = c;
+			}
+		}
+		dictword[charindex] = 0;
+		if (wordnum < nv) {
+			_G(_verbs)[wordnum] = Common::String(dictword, charindex + 1);
+			if (loud)
+				debug("Verb %d: \"%s\"\n", wordnum, _G(_verbs)[wordnum].c_str());
+		} else {
+			_G(_nouns)[wordnum - nv] = Common::String(dictword, charindex + 1);
+			if (loud)
+				debug("Noun %d: \"%s\"\n", wordnum - nv, _G(_nouns)[wordnum - nv].c_str());
+		}
+		wordnum++;
+
+		if (c != 0 && !isascii(c))
+			return ptr;
+
+		charindex = 0;
+	} while (wordnum <= nv + nn);
+
+	return ptr;
+}
+
+int parseHeader(int *h, HeaderType type, int *ni, int *na, int *nw, int *nr, int *mc, int *pr, int *tr, int *wl, int *lt, int *mn, int *trm) {
+	switch (type) {
+	case NO_HEADER:
+		return 0;
+	case EARLY:
+		*ni = h[1];
+		*na = h[2];
+		*nw = h[3];
+		*nr = h[4];
+		*mc = h[5];
+		*pr = h[6];
+		*tr = h[7];
+		*wl = h[8];
+		*lt = h[9];
+		*mn = h[10];
+		*trm = h[11];
+		break;
+	case LATE:
+		*ni = h[1];
+		*na = h[2];
+		*nw = h[3];
+		*nr = h[4];
+		*mc = h[5];
+		*wl = h[6];
+		*mn = h[7];
+		*pr = 1;
+		*tr = 0;
+		*lt = -1;
+		*trm = 0;
+		break;
+	case HULK_HEADER:
+		*ni = h[3];
+		*na = h[2];
+		*nw = h[1];
+		*nr = h[5];
+		*mc = h[6];
+		*pr = h[7];
+		*tr = h[8];
+		*wl = h[0];
+		*lt = h[9];
+		*mn = h[4];
+		*trm = h[10];
+		break;
+	case ROBIN_C64_HEADER:
+		*ni = h[1];
+		*na = h[2];
+		*nw = h[6];
+		*nr = h[4];
+		*mc = h[5];
+		*pr = 1;
+		*tr = 0;
+		*wl = h[7];
+		*lt = -1;
+		*mn = h[3];
+		*trm = 0;
+		break;
+	case GREMLINS_C64_HEADER:
+		*ni = h[1];
+		*na = h[2];
+		*nw = h[5];
+		*nr = h[3];
+		*mc = h[6];
+		*pr = h[8];
+		*tr = 0;
+		*wl = h[7];
+		*lt = -1;
+		*mn = 98;
+		*trm = 0;
+		break;
+	case SUPERGRAN_C64_HEADER:
+		*ni = h[3];
+		*na = h[1];
+		*nw = h[2];
+		*nr = h[4];
+		*mc = h[8];
+		*pr = 1;
+		*tr = 0;
+		*wl = h[6];
+		*lt = -1;
+		*mn = h[5];
+		*trm = 0;
+		break;
+	case SEAS_OF_BLOOD_C64_HEADER:
+		*ni = h[0];
+		*na = h[1];
+		*nw = 134;
+		*nr = h[3];
+		*mc = h[4];
+		*pr = 1;
+		*tr = 0;
+		*wl = h[6];
+		*lt = -1;
+		*mn = h[2];
+		*trm = 0;
+		break;
+	case MYSTERIOUS_C64_HEADER:
+		*ni = h[1];
+		*na = h[2];
+		*nw = h[3];
+		*nr = h[4];
+		*mc = h[5] & 0xff;
+		*pr = h[5] >> 8;
+		*tr = h[6];
+		*wl = h[7];
+		*lt = h[8];
+		*mn = h[9];
+		*trm = 0;
+		break;
+	case ARROW_OF_DEATH_PT_2_C64_HEADER:
+		*ni = h[3];
+		*na = h[1];
+		*nw = h[2];
+		*nr = h[4];
+		*mc = h[5] & 0xff;
+		*pr = h[5] >> 8;
+		*tr = h[6];
+		*wl = h[7];
+		*lt = h[8];
+		*mn = h[9];
+		*trm = 0;
+		break;
+	case INDIANS_C64_HEADER:
+		*ni = h[1];
+		*na = h[2];
+		*nw = h[3];
+		*nr = h[4];
+		*mc = h[5] & 0xff;
+		*pr = h[5] >> 8;
+		*tr = h[6] & 0xff;
+		*wl = h[6] >> 8;
+		*lt = h[7] >> 8;
+		*mn = h[8] >> 8;
+		*trm = 0;
+		break;
+	default:
+		warning("Unhandled header type!\n");
+		return 0;
+	}
+	return 1;
+}
+
+void printHeaderInfo(int *h, int ni, int na, int nw, int nr, int mc, int pr, int tr, int wl, int lt, int mn, int trm) {
+	uint16_t value;
+	for (int i = 0; i < 13; i++) {
+		value = h[i];
+		debug("b $%X %d: ", 0x494d + 0x3FE5 + i * 2, i);
+		debug("\t%d\n", value);
+	}
+
+	debug("Number of items =\t%d\n", ni);
+	debug("Number of actions =\t%d\n", na);
+	debug("Number of words =\t%d\n", nw);
+	debug("Number of rooms =\t%d\n", nr);
+	debug("Max carried items =\t%d\n", mc);
+	debug("Word length =\t%d\n", wl);
+	debug("Number of messages =\t%d\n", mn);
+	debug("Player start location: %d\n", pr);
+	debug("Treasure room: %d\n", tr);
+	debug("Lightsource time left: %d\n", lt);
+	debug("Number of treasures: %d\n", tr);
+}
+
+void loadVectorData(GameInfo info, uint8_t *ptr) {
+	int offset;
+
+	if (info._startOfImageData == FOLLOWS)
+		ptr++;
+	else if (seekIfNeeded(info._startOfImageData, &offset, &ptr) == 0)
+		return;
+
+	_G(_lineImages).resize(info._numberOfRooms);
+	int ct = 0;
+	LineImage *lp = &_G(_lineImages)[0];
+	uint8_t byte = *(ptr++);
+	do {
+		_G(_rooms)[ct]._image = 0;
+		if (byte == 0xff) {
+			lp->_bgColour = *(ptr++);
+			lp->_data = ptr;
+		} else {
+			error("Error! Image data does not start with 0xff!\n");
+		}
+		do {
+			byte = *(ptr++);
+			if (ptr > _G(_entireFile) && static_cast<size_t>(ptr - _G(_entireFile)) >= _G(_fileLength)) {
+				error("Error! Image data for image %d cut off!\n", ct);
+				if (_G(_gameHeader)->_numRooms - ct > 1)
+					g_scott->display(_G(_bottomWindow), "[This copy has %d broken or missing pictures. These have been patched out.]\n\n", _G(_gameHeader)->_numRooms - ct);
+				if (lp->_data >= ptr)
+					lp->_size = 0;
+				else
+					lp->_size = ptr - lp->_data - 1;
+				for (int i = ct + 2; i < _G(_gameHeader)->_numRooms; i++)
+					_G(_rooms)[i]._image = 255;
+				return;
+			}
+		} while (byte != 0xff);
+
+		lp->_size = ptr - lp->_data;
+		lp++;
+		ct++;
+	} while (ct < info._numberOfRooms);
+}
+
+GameIDType detectGame(Common::SeekableReadStream *f) {
+
+	for (int i = 0; i < NUMBER_OF_DIRECTIONS; i++)
+		_G(_directions)[i] = _G(_englishDirections)[i];
+	for (int i = 0; i < NUMBER_OF_SKIPPABLE_WORDS; i++)
+		_G(_skipList)[i] = _G(_englishSkipList)[i];
+	for (int i = 0; i < NUMBER_OF_DELIMITERS; i++)
+		_G(_delimiterList)[i] = _G(_englishDelimiterList)[i];
+	for (int i = 0; i < NUMBER_OF_EXTRA_NOUNS; i++)
+		_G(_extraNouns)[i] = _G(_englishExtraNouns)[i];
+
+	_G(_fileLength) = f->size();
+
+	_G(_game) = new GameInfo;
+
+	Common::String md5 = g_vm->getGameMD5();
+	const GlkDetectionEntry *p = SCOTT_GAMES;
+
+	while (p->_md5) {
+		if (md5.equalsC(p->_md5)) {
+			if (!scumm_stricmp(p->_extra, "")) {
+				CURRENT_GAME = SCOTTFREE;
+			}
+			if (!scumm_stricmp(p->_extra, "ZXSpectrum")) {
+				_G(_entireFile) = new uint8_t[_G(_fileLength)];
+				size_t result = f->read(_G(_entireFile), _G(_fileLength));
+				if (result != _G(_fileLength))
+					g_scott->fatal("File empty or read error!");
+
+				// ZXSpectrum Detection
+				uint8_t *uncompressed = decompressZ80(_G(_entireFile), _G(_fileLength));
+				if (uncompressed != nullptr) {
+					delete[] _G(_entireFile);
+					_G(_entireFile) = uncompressed;
+					_G(_fileLength) = 0xc000;
+				}
+
+				int offset;
+				DictionaryType dict_type = getId(&offset);
+				if (dict_type == NOT_A_GAME)
+					return UNKNOWN_GAME;
+				for (int i = 0; i < NUMGAMES; i++) {
+					if (g_games[i]._dictionary == dict_type) {
+						if (tryLoading(g_games[i], offset, 0)) {
+							delete _G(_game);
+							_G(_game) = &g_games[i];
+							break;
+						}
+					}
+				}
+			}
+		}
+		// TODO
+		// TI99/4A Detection
+
+		// TODO
+		// C64 Detection
+
+		++p;
+	}
+
+	if (CURRENT_GAME == SCOTTFREE || CURRENT_GAME == TI994A)
+		return CURRENT_GAME;
+
+	/* Copy ZX Spectrum style system messages as base */
+	for (int i = 6; i < MAX_SYSMESS && g_sysDictZX[i] != nullptr; i++) {
+		_G(_sys)[i] = g_sysDictZX[i];
+	}
+
+	switch (CURRENT_GAME) {
+	case ROBIN_OF_SHERWOOD:
+		// TODO
+		break;
+	case ROBIN_OF_SHERWOOD_C64:
+		// TODO
+		break;
+	case SEAS_OF_BLOOD:
+		// TODO
+		break;
+	case SEAS_OF_BLOOD_C64:
+		// TODO
+		break;
+	case CLAYMORGUE:
+		// TODO
+		break;
+	case SECRET_MISSION:
+	case SECRET_MISSION_C64:
+		// TODO
+		break;
+	case ADVENTURELAND:
+		// TODO
+		break;
+	case ADVENTURELAND_C64:
+		// TODO
+		break;
+	case CLAYMORGUE_C64:
+		// TODO
+		break;
+	case GREMLINS_GERMAN_C64:
+		// TODO
+		break;
+	case SPIDERMAN_C64:
+		// TODO
+		break;
+	case SUPERGRAN_C64:
+		// TODO
+		break;
+	case SAVAGE_ISLAND_C64:
+	case SAVAGE_ISLAND2_C64:
+		// TODO
+		break;
+	case SAVAGE_ISLAND:
+	case SAVAGE_ISLAND2:
+	case GREMLINS_GERMAN:
+	case GREMLINS:
+	case SUPERGRAN:
+		// TODO
+		break;
+	case GREMLINS_SPANISH:
+		// TODO
+		break;
+	case HULK_C64:
+	case HULK:
+		for (int i = 0; i < 6; i++) {
+			_G(_sys)[i] = g_sysDictZX[i];
+		}
+		break;
+	default:
+		if (!(_G(_game)->_subType & C64)) {
+			if (_G(_game)->_subType & MYSTERIOUS) {
+				for (int i = PLAY_AGAIN; i <= YOU_HAVENT_GOT_IT; i++)
+					_G(_sys)[i] = _G(_systemMessages)[2 - PLAY_AGAIN + i];
+				for (int i = YOU_DONT_SEE_IT; i <= WHAT_NOW; i++)
+					_G(_sys)[i] = _G(_systemMessages)[15 - YOU_DONT_SEE_IT + i];
+				for (int i = LIGHT_HAS_RUN_OUT; i <= RESUME_A_SAVED_GAME; i++)
+					_G(_sys)[i] = _G(_systemMessages)[31 - LIGHT_HAS_RUN_OUT + i];
+				_G(_sys)[ITEM_DELIMITER] = " - ";
+				_G(_sys)[MESSAGE_DELIMITER] = "\n";
+				_G(_sys)[YOU_SEE] = "\nThings I can see:\n";
+				break;
+			} else {
+				for (int i = PLAY_AGAIN; i <= RESUME_A_SAVED_GAME; i++)
+					_G(_sys)[i] = _G(_systemMessages)[2 - PLAY_AGAIN + i];
+			}
+		}
+		break;
+	}
+
+	switch (CURRENT_GAME) {
+	case GREMLINS_GERMAN:
+		// TODO
+		break;
+	case GREMLINS_GERMAN_C64:
+		// TODO
+		break;
+	case PERSEUS_ITALIAN:
+		// TODO
+		break;
+	default:
+		break;
+	}
+
+	/* If it is a C64 or a Mysterious Adventures game, we have setup the graphics already */
+	if (!(_G(_game)->_subType & (C64 | MYSTERIOUS)) && _G(_game)->_numberOfPictures > 0) {
+		sagaSetup(0);
+	}
+
+	return CURRENT_GAME;
+}
+
+uint8_t *seekToPos(uint8_t *buf, size_t offset) {
+	if (offset > _G(_fileLength))
+		return 0;
+	return buf + offset;
+}
+
+int seekIfNeeded(int expectedStart, int *offset, uint8_t **ptr) {
+	if (expectedStart != FOLLOWS) {
+		*offset = expectedStart + _G(_fileBaselineOffset);
+		*ptr = seekToPos(_G(_entireFile), *offset);
+		if (*ptr == 0)
+			return 0;
+	}
+	return 1;
+}
+
+int tryLoadingOld(GameInfo info, int dictStart) {
+	int ni, na, nw, nr, mc, pr, tr, wl, lt, mn, trm;
+	int ct;
+
+	Action *ap;
+	Room *rp;
+	Item *ip;
+	/* Load the header */
+
+	uint8_t *ptr = _G(_entireFile);
+	_G(_fileBaselineOffset) = dictStart - info._startOfDictionary;
+	int offset = info._startOfHeader + _G(_fileBaselineOffset);
+
+	ptr = seekToPos(_G(_entireFile), offset);
+	if (ptr == 0)
+		return 0;
+
+	readHeader(ptr);
+
+	if (!parseHeader(_G(_header), info._headerStyle, &ni, &na, &nw, &nr, &mc, &pr, &tr, &wl, &lt, &mn, &trm))
+		return 0;
+
+	if (ni != info._numberOfItems || na != info._numberOfActions || nw != info._numberOfWords || nr != info._numberOfRooms || mc != info._maxCarried) {
+		return 0;
+	}
+
+	_G(_gameHeader)->_numItems = ni;
+	_G(_gameHeader)->_numActions = na;
+	_G(_gameHeader)->_numWords = nw;
+	_G(_gameHeader)->_wordLength = wl;
+	_G(_gameHeader)->_numRooms = nr;
+	_G(_gameHeader)->_maxCarry = mc;
+	_G(_gameHeader)->_playerRoom = pr;
+	_G(_gameHeader)->_treasures = tr;
+	_G(_gameHeader)->_lightTime = lt;
+	_G(_lightRefill) = lt;
+	_G(_gameHeader)->_numMessages = mn;
+	_G(_gameHeader)->_treasureRoom = trm;
+	_G(_items).resize(ni + 1);
+	_G(_actions).resize(na + 1);
+	_G(_verbs).resize(nw + 2);
+	_G(_nouns).resize(nw + 2);
+	_G(_rooms).resize(nr + 1);
+	_G(_messages).resize(mn + 1);
+
+	// actions
+	if (seekIfNeeded(info._startOfActions, &offset, &ptr) == 0)
+		return 0;
+
+	ct = 0;
+
+	uint16_t value, cond, comm;
+
+	while (ct < na + 1) {
+		ap = &_G(_actions)[ct];
+		value = *(ptr++);
+		value += *(ptr++) * 0x100; /* verb/noun */
+		ap->_vocab = value;
+
+		cond = 5;
+		comm = 2;
+
+		for (int j = 0; j < 5; j++) {
+			if (j < cond) {
+				value = *(ptr++);
+				value += *(ptr++) * 0x100;
+			} else
+				value = 0;
+			ap->_condition[j] = value;
+		}
+		for (int j = 0; j < 2; j++) {
+			if (j < comm) {
+				value = *(ptr++);
+				value += *(ptr++) * 0x100;
+			} else
+				value = 0;
+			ap->_action[j] = value;
+		}
+		ct++;
+	}
+
+	// room connections
+	if (seekIfNeeded(info._startOfRoomConnections, &offset, &ptr) == 0)
+		return 0;
+
+	ct = 0;
+
+	while (ct < nr + 1) {
+		rp = &_G(_rooms)[ct];
+		for (int j = 0; j < 6; j++) {
+			rp->_exits[j] = *(ptr++);
+		}
+		ct++;
+	}
+
+	// item locations
+	if (seekIfNeeded(info._startOfItemLocations, &offset, &ptr) == 0)
+		return 0;
+
+	ct = 0;
+	while (ct < ni + 1) {
+		ip = &_G(_items)[ct];
+		ip->_location = *(ptr++);
+		ip->_initialLoc = ip->_location;
+		ct++;
+	}
+
+	// dictionary
+	if (seekIfNeeded(info._startOfDictionary, &offset, &ptr) == 0)
+		return 0;
+
+	ptr = readDictionary(info, &ptr, 0);
+
+	// rooms
+	if (seekIfNeeded(info._startOfRoomDescriptions, &offset, &ptr) == 0)
+		return 0;
+
+	if (info._startOfRoomDescriptions == FOLLOWS)
+		ptr++;
+
+	ct = 0;
+
+	char text[256];
+	char c = 0;
+	int charindex = 0;
+
+	do {
+		c = *(ptr++);
+		text[charindex] = c;
+		rp = &_G(_rooms)[ct];
+		if (c == 0) {
+			rp->_text = text;
+			if (info._numberOfPictures > 0)
+				rp->_image = ct - 1;
+			else
+				rp->_image = 255;
+			ct++;
+			charindex = 0;
+		} else {
+			charindex++;
+		}
+		if (c != 0 && !isascii(c))
+			return 0;
+	} while (ct < nr + 1);
+
+	// messages
+	if (seekIfNeeded(info._startOfMessages, &offset, &ptr) == 0)
+		return 0;
+
+	ct = 0;
+	charindex = 0;
+
+	while (ct < mn + 1) {
+		c = *(ptr++);
+		text[charindex] = c;
+		if (c == 0) {
+			_G(_messages)[ct] = text;
+			ct++;
+			charindex = 0;
+		} else {
+			charindex++;
+		}
+	}
+
+	// items
+	if (seekIfNeeded(info._startOfItemDescriptions, &offset, &ptr) == 0)
+		return 0;
+
+	ct = 0;
+	charindex = 0;
+
+	do {
+		ip = &_G(_items)[ct];
+		c = *(ptr++);
+		text[charindex] = c;
+		if (c == 0) {
+			ip->_text = text;
+			const char *p = strchr(ip->_text.c_str(), '/');
+			/* Some games use // to mean no auto get/drop word! */
+			if (p) {
+				ip->_autoGet = Common::String(p);
+				if (!(ip->_autoGet == "//") && !(ip->_autoGet == "/*")) {
+					ip->_text = Common::String(ip->_text.c_str(), p);
+					ip->_autoGet.deleteChar(0);
+					const char *t = strchr(ip->_autoGet.c_str(), '/');
+					if (t) {
+						ip->_autoGet = Common::String(ip->_autoGet.c_str(), t);
+					}
+				}
+			}
+			ct++;
+			charindex = 0;
+		} else {
+			charindex++;
+		}
+	} while (ct < ni + 1);
+
+	// line images
+	if (info._numberOfPictures > 0) {
+		loadVectorData(info, ptr);
+	}
+
+	// system messages
+	ct = 0;
+	if (seekIfNeeded(info._startOfSystemMessages, &offset, &ptr) == 0)
+		return 1;
+
+	charindex = 0;
+
+	do {
+		c = *(ptr++);
+		text[charindex] = c;
+		if (c == 0 || c == 0x0d) {
+			if (charindex > 0) {
+				if (c == 0x0d)
+					charindex++;
+				text[charindex] = '\0';
+				_G(_systemMessages)[ct] = Common::String(text, charindex + 1);
+				ct++;
+				charindex = 0;
+			}
+		} else {
+			charindex++;
+		}
+
+		if (c != 0 && c != 0x0d && c != '\x83' && c != '\xc9' && !isascii(c))
+			break;
+	} while (ct < 40);
+
+	charindex = 0;
+
+	if (seekIfNeeded(info._startOfDirections, &offset, &ptr) == 0)
+		return 0;
+
+	ct = 0;
+	do {
+		c = *(ptr++);
+		text[charindex] = c;
+		if (c == 0 || c == 0x0d) {
+			if (charindex > 0) {
+				if (c == 0x0d)
+					charindex++;
+				text[charindex] = '\0';
+				_G(_sys)[ct] = Common::String(text, charindex + 1);
+				ct++;
+				charindex = 0;
+			}
+		} else {
+			charindex++;
+		}
+
+		if (c != 0 && c != 0x0d && !isascii(c))
+			break;
+	} while (ct < 6);
+
+	return 1;
+}
+
+int tryLoading(GameInfo info, int dictStart, int loud) {
+	// The Hulk does everything differently so it gets its own function
+	if (info._gameID == HULK || info._gameID == HULK_C64)
+		return tryLoadingHulk(info, dictStart);
+
+	if (info._type == OLD_STYLE)
+		return tryLoadingOld(info, dictStart);
+
+	int ni, na, nw, nr, mc, pr, tr, wl, lt, mn, trm;
+	int ct;
+
+	Action *ap;
+	Room *rp;
+	Item *ip;
+
+	/* Load the header */
+	uint8_t *ptr = _G(_entireFile);
+
+	if (loud) {
+		debug("dict_start:%x\n", dictStart);
+		debug(" info._start_of_dictionary:%x\n", info._startOfDictionary);
+	}
+	_G(_fileBaselineOffset) = dictStart - info._startOfDictionary;
+
+	if (loud) {
+		debug("_fileBaselineOffset:%x (%d)\n", _G(_fileBaselineOffset), _G(_fileBaselineOffset));
+	}
+
+	int offset = info._startOfHeader + _G(_fileBaselineOffset);
+
+	ptr = seekToPos(_G(_entireFile), offset);
+	if (ptr == 0)
+		return 0;
+
+	readHeader(ptr);
+
+	if (!parseHeader(_G(_header), info._headerStyle, &ni, &na, &nw, &nr, &mc, &pr, &tr, &wl, &lt, &mn, &trm))
+		return 0;
+
+	if (loud)
+		printHeaderInfo(_G(_header), ni, na, nw, nr, mc, pr, tr, wl, lt, mn, trm);
+
+	_G(_gameHeader)->_numItems = ni;
+	_G(_gameHeader)->_numActions = na;
+	_G(_gameHeader)->_numWords = nw;
+	_G(_gameHeader)->_wordLength = wl;
+	_G(_gameHeader)->_numRooms = nr;
+	_G(_gameHeader)->_maxCarry = mc;
+	_G(_gameHeader)->_playerRoom = pr;
+	_G(_gameHeader)->_treasures = tr;
+	_G(_gameHeader)->_lightTime = lt;
+	_G(_lightRefill) = lt;
+	_G(_gameHeader)->_numMessages = mn;
+	_G(_gameHeader)->_treasureRoom = trm;
+
+	if (sanityCheckHeader() == 0) {
+		return 0;
+	}
+
+	if (loud) {
+		debug("Found a valid header at position 0x%x\n", offset);
+		debug("Expected: 0x%x\n", info._startOfHeader + _G(_fileBaselineOffset));
+	}
+
+	if (ni != info._numberOfItems || na != info._numberOfActions || nw != info._numberOfWords || nr != info._numberOfRooms || mc != info._maxCarried) {
+		if (loud) {
+			debug("Non-matching header\n");
+		}
+		return 0;
+	}
+
+	_G(_items).resize(ni + 1);
+	_G(_actions).resize(na + 1);
+	_G(_verbs).resize(nw + 2);
+	_G(_nouns).resize(nw + 2);
+	_G(_rooms).resize(nr + 1);
+	_G(_messages).resize(mn + 1);
+
+	int compressed = (info._dictionary == FOUR_LETTER_COMPRESSED);
+
+	// room images
+	if (info._startOfRoomImageList != 0) {
+		if (seekIfNeeded(info._startOfRoomImageList, &offset, &ptr) == 0)
+			return 0;
+
+		for (ct = 0; ct <= _G(_gameHeader)->_numRooms; ct++) {
+			rp = &_G(_rooms)[ct];
+			rp->_image = *(ptr++);
+		}
+	}
+
+	// item flags
+	if (info._startOfItemFlags != 0) {
+
+		if (seekIfNeeded(info._startOfItemFlags, &offset, &ptr) == 0)
+			return 0;
+
+		for (ct = 0; ct <= _G(_gameHeader)->_numItems; ct++) {
+			ip = &_G(_items)[ct];
+			ip->_flag = *(ptr++);
+		}
+	}
+
+	// item images
+	if (info._startOfItemImageList != 0) {
+
+		if (seekIfNeeded(info._startOfItemImageList, &offset, &ptr) == 0)
+			return 0;
+
+		for (ct = 0; ct <= _G(_gameHeader)->_numItems; ct++) {
+			ip = &_G(_items)[ct];
+			ip->_image = *(ptr++);
+		}
+		if (loud)
+			debug("Offset after reading item images: %d\n", static_cast<int>(ptr - _G(_entireFile) - _G(_fileBaselineOffset)));
+	}
+
+	// actions
+	if (seekIfNeeded(info._startOfActions, &offset, &ptr) == 0)
+		return 0;
+
+	ct = 0;
+
+	uint16_t value, cond, comm;
+
+	while (ct < na + 1) {
+		ap = &_G(_actions)[ct];
+		value = *(ptr++);
+		value += *(ptr++) * 0x100; /* verb/noun */
+		ap->_vocab = value;
+
+		if (info._actionsStyle == COMPRESSED) {
+			value = *(ptr++); /* count of actions/conditions */
+			cond = value & 0x1f;
+			if (cond > 5) {
+				debug("Condition error at action %d!\n", ct);
+				cond = 5;
+			}
+			comm = (value & 0xe0) >> 5;
+			if (comm > 2) {
+				debug("Command error at action %d!\n", ct);
+				comm = 2;
+			}
+		} else {
+			cond = 5;
+			comm = 2;
+		}
+		for (int j = 0; j < 5; j++) {
+			if (j < cond) {
+				value = *(ptr++);
+				value += *(ptr++) * 0x100;
+			} else
+				value = 0;
+			ap->_condition[j] = value;
+		}
+		for (int j = 0; j < 2; j++) {
+			if (j < comm) {
+				value = *(ptr++);
+				value += *(ptr++) * 0x100;
+			} else
+				value = 0;
+			ap->_action[j] = value;
+		}
+
+		ct++;
+	}
+	if (loud)
+		debug("Offset after reading actions: %d\n", static_cast<int>(ptr - _G(_entireFile) - _G(_fileBaselineOffset)));
+
+	// dictionary
+	if (seekIfNeeded(info._startOfDictionary, &offset, &ptr) == 0)
+		return 0;
+
+	ptr = readDictionary(info, &ptr, loud);
+
+	if (loud)
+		debug("Offset after reading dictionary: %d\n", static_cast<int>(ptr - _G(_entireFile) - _G(_fileBaselineOffset)));
+
+	// rooms
+	if (info._startOfRoomDescriptions != 0) {
+		if (seekIfNeeded(info._startOfRoomDescriptions, &offset, &ptr) == 0)
+			return 0;
+
+		ct = 0;
+
+		char text[256];
+		char c = 0;
+		int charindex = 0;
+
+		if (!compressed) {
+			do {
+				rp = &_G(_rooms)[ct];
+				c = *(ptr++);
+				text[charindex] = c;
+				if (c == 0) {
+					rp->_text = text;
+					if (loud)
+						debug("Room %d: %s\n", ct, rp->_text.c_str());
+					ct++;
+					charindex = 0;
+				} else {
+					charindex++;
+				}
+				if (c != 0 && !isascii(c))
+					return 0;
+			} while (ct < nr + 1);
+		} else {
+			do {
+				rp = &_G(_rooms)[ct];
+				rp->_text = decompressText(ptr, ct);
+				if (rp->_text == nullptr)
+					return 0;
+				rp->_text.replace(0, 1, Common::String(tolower(rp->_text[0])));
+				ct++;
+			} while (ct < nr);
+		}
+	}
+
+	// room connections
+	if (seekIfNeeded(info._startOfRoomConnections, &offset, &ptr) == 0)
+		return 0;
+
+	ct = 0;
+
+	while (ct < nr + 1) {
+		rp = &_G(_rooms)[ct];
+		for (int j = 0; j < 6; j++) {
+			rp->_exits[j] = *(ptr++);
+		}
+
+		ct++;
+	}
+
+	// messages
+	if (seekIfNeeded(info._startOfMessages, &offset, &ptr) == 0)
+		return 0;
+
+	ct = 0;
+	char text[256];
+	char c = 0;
+	int charindex = 0;
+
+	if (compressed) {
+		while (ct < mn + 1) {
+			_G(_messages)[ct] = decompressText(ptr, ct);
+			if (loud)
+				debug("Message %d: \"%s\"\n", ct, _G(_messages)[ct].c_str());
+			if (_G(_messages)[ct] == nullptr)
+				return 0;
+			ct++;
+		}
+	} else {
+		while (ct < mn + 1) {
+			c = *(ptr++);
+			text[charindex] = c;
+			if (c == 0) {
+				_G(_messages)[ct] = text;
+				if (loud)
+					debug("Message %d: \"%s\"\n", ct, _G(_messages)[ct].c_str());
+				ct++;
+				charindex = 0;
+			} else {
+				charindex++;
+			}
+		}
+	}
+
+	// items
+	if (seekIfNeeded(info._startOfItemDescriptions, &offset, &ptr) == 0)
+		return 0;
+
+	ct = 0;
+	charindex = 0;
+
+	if (compressed) {
+		do {
+			ip = &_G(_items)[ct];
+			ip->_text = decompressText(ptr, ct);
+			ip->_autoGet = "";
+			if (ip->_text != "" && ip->_text[0] != '.') {
+				if (loud)
+					debug("Item %d: %s\n", ct, ip->_text.c_str());
+				const char *p = strchr(ip->_text.c_str(), '.');
+				if (p) {
+					ip->_autoGet = Common::String(p);
+					ip->_autoGet.deleteChar(0);
+					ip->_autoGet.deleteChar(0);
+					const char *t = strchr(ip->_autoGet.c_str(), '.');
+					if (t)
+						ip->_autoGet = Common::String(ip->_autoGet.c_str(), t);
+					for (int i = 1; i < _G(_gameHeader)->_wordLength; i++)
+						ip->_autoGet.replace(i, 1, Common::String(toupper(ip->_text[i])));
+				}
+			}
+			ct++;
+		} while (ct < ni + 1);
+	} else {
+		do {
+			ip = &_G(_items)[ct];
+			c = *(ptr++);
+			text[charindex] = c;
+			if (c == 0) {
+				ip->_text = text;
+				if (loud)
+					debug("Item %d: %s\n", ct, ip->_text.c_str());
+				const char *p = strchr(ip->_text.c_str(), '/');
+				/* Some games use // to mean no auto get/drop word! */
+				if (p) {
+					ip->_autoGet = Common::String(p);
+					if (!(ip->_autoGet == "//") && !(ip->_autoGet == "/*")) {
+						ip->_text = Common::String(ip->_text.c_str(), p);
+						ip->_autoGet.deleteChar(0);
+						const char *t = strchr(ip->_autoGet.c_str(), '/');
+						if (t) {
+							ip->_autoGet = Common::String(ip->_autoGet.c_str(), t);
+						}
+					}
+				}
+				ct++;
+				charindex = 0;
+			} else {
+				charindex++;
+			}
+		} while (ct < ni + 1);
+	}
+
+	// item locations
+	if (seekIfNeeded(info._startOfItemLocations, &offset, &ptr) == 0)
+		return 0;
+
+	ct = 0;
+	while (ct < ni + 1) {
+		ip = &_G(_items)[ct];
+		ip->_location = *(ptr++);
+		ip->_initialLoc = ip->_location;
+		ct++;
+	}
+
+	// vector images
+	if (info._numberOfPictures > 0 && info._pictureFormatVersion == 99) {
+		loadVectorData(info, ptr);
+	}
+
+	// system messages
+	if (seekIfNeeded(info._startOfSystemMessages, &offset, &ptr) == 0)
+		return 1;
+jumpSysMess:
+	ptr = seekToPos(_G(_entireFile), offset);
+	ct = 0;
+	charindex = 0;
+
+	do {
+		c = *(ptr++);
+		if ((ptr > _G(_entireFile) && static_cast<size_t>(ptr - _G(_entireFile)) > _G(_fileLength)) || ptr < _G(_entireFile)) {
+			debug("Read out of bounds!\n");
+			return 0;
+		}
+		if (charindex > 255)
+			charindex = 0;
+		text[charindex] = c;
+		if (c == 0 || c == 0x0d) {
+			if (charindex > 0) {
+				if (c == 0x0d)
+					charindex++;
+				if (ct == 0 && (info._subType & (C64 | ENGLISH)) == (C64 | ENGLISH) && memcmp(text, "NORTH", 5) != 0) {
+					offset--;
+					goto jumpSysMess;
+				}
+				text[charindex] = '\0';
+				_G(_systemMessages)[ct] = Common::String(text, charindex + 1);
+				if (loud)
+					debug("system_messages %d: \"%s\"\n", ct, _G(_systemMessages)[ct].c_str());
+				ct++;
+				charindex = 0;
+			}
+		} else {
+			charindex++;
+		}
+	} while (ct < 45);
+
+	if (loud)
+		debug("Offset after reading system messages: %d\n", static_cast<int>(ptr - _G(_entireFile)));
+
+	if ((info._subType & (C64 | ENGLISH)) == (C64 | ENGLISH)) {
+		return 1;
+	}
+
+	if (seekIfNeeded(info._startOfDirections, &offset, &ptr) == 0)
+		return 0;
+
+	charindex = 0;
+
+	ct = 0;
+	do {
+		c = *(ptr++);
+		text[charindex] = c;
+		if (c == 0 || c == 0x0d) {
+			if (charindex > 0) {
+				if (c == 0x0d)
+					charindex++;
+				text[charindex] = '\0';
+				_G(_sys)[ct] = Common::String(text, charindex + 1);
+				ct++;
+				charindex = 0;
+			}
+		} else {
+			charindex++;
+		}
+
+		if (c != 0 && c != 0x0d && !isascii(c))
+			break;
+	} while (ct < 6);
+
+	return 1;
+}
+
+DictionaryType getId(int *offset) {
+	for (int i = 0; i < 9; i++) {
+		*offset = findCode(g_dictKeys[i]._signature, 0);
+		if (*offset != -1) {
+			if (i == 4 || i == 5) // GERMAN
+				*offset -= 5;
+			else if (i == 6) // SPANISH
+				*offset -= 8;
+			else if (i == 7) // Claymorgue
+				*offset -= 11;
+			return g_dictKeys[i]._dict;
+		}
+	}
+
+	return NOT_A_GAME;
+}
+
+int findCode(const char *x, int base) {
+	unsigned const char *p = _G(_entireFile) + base;
+	int len = strlen(x);
+	if (len < 7)
+		len = 7;
+	while (p < _G(_entireFile) + _G(_fileLength) - len) {
+		if (memcmp(p, x, len) == 0) {
+			return p - _G(_entireFile);
+		}
+		p++;
+	}
+	return -1;
+}
+
+} // End of namespace Scott
+} // End of namespace Glk
diff --git a/engines/glk/scott/detect_game.h b/engines/glk/scott/detect_game.h
new file mode 100644
index 00000000000..cbc0643f358
--- /dev/null
+++ b/engines/glk/scott/detect_game.h
@@ -0,0 +1,44 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 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 GLK_SCOTT_DETECTGAME
+#define GLK_SCOTT_DETECTGAME
+
+#include "common/stream.h"
+#include "glk/scott/definitions.h"
+#include <stdint.h>
+
+namespace Glk {
+namespace Scott {
+
+void readHeader(uint8_t *ptr);
+int parseHeader(int *h, HeaderType type, int *ni, int *na, int *nw, int *nr, int *mc, int *pr, int *tr, int *wl, int *lt, int *mn, int *trm);
+GameIDType detectGame(Common::SeekableReadStream *f);
+uint8_t *seekToPos(uint8_t *buf, size_t offset);
+int seekIfNeeded(int expectedStart, int *offset, uint8_t **ptr);
+int tryLoading(GameInfo info, int dictStart, int loud);
+DictionaryType getId(int *offset);
+int findCode(const char *x, int base);
+
+} // End of namespace Scott
+} // End of namespace Glk
+
+#endif


Commit: 4b31312480697854956b77fb55b0bce937e1da51
    https://github.com/scummvm/scummvm/commit/4b31312480697854956b77fb55b0bce937e1da51
Author: Avijeet (am388488 at gmail.com)
Date: 2022-04-12T20:35:24-07:00

Commit Message:
GLK: SCOTT: Add support for different drawing methods.

Changed paths:
  A engines/glk/scott/line_drawing.cpp
  A engines/glk/scott/line_drawing.h
  A engines/glk/scott/saga_draw.cpp
  A engines/glk/scott/saga_draw.h


diff --git a/engines/glk/scott/line_drawing.cpp b/engines/glk/scott/line_drawing.cpp
new file mode 100644
index 00000000000..dcae2152790
--- /dev/null
+++ b/engines/glk/scott/line_drawing.cpp
@@ -0,0 +1,235 @@
+/* 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 "glk/scott/line_drawing.h"
+#include "glk/scott/globals.h"
+#include "glk/scott/ring_buffer.h"
+#include "glk/scott/saga_draw.h"
+#include "glk/scott/scott.h"
+
+namespace Glk {
+namespace Scott {
+
+void scottLinegraphicsPlotClip(int x, int y, int colour) {
+	/*
+	 * Clip the plot if the value is outside the context.  Otherwise, plot the
+	 * pixel as colour1 if it is currently colour2.
+	 */
+	if (x >= 0 && x <= _G(_scottGraphicsWidth) && y >= 0 && y < _G(_scottGraphicsHeight)) {
+		_G(_pictureBitmap)[y * 255 + x] = colour;
+		PixelToDraw *toDraw = new PixelToDraw;
+		toDraw->_x = x;
+		toDraw->_y = y;
+		toDraw->_colour = colour;
+		_G(_pixelsToDraw)[_G(_totalDrawInstructions)++] = toDraw;
+	}
+}
+
+void scottLinegraphicsDrawLine(int x1, int y1, int x2, int y2, int colour) {
+	int x, y, dx, dy, incx, incy, balance;
+
+	/* Normalize the line into deltas and increments. */
+	if (x2 >= x1) {
+		dx = x2 - x1;
+		incx = 1;
+	} else {
+		dx = x1 - x2;
+		incx = -1;
+	}
+
+	if (y2 >= y1) {
+		dy = y2 - y1;
+		incy = 1;
+	} else {
+		dy = y1 - y2;
+		incy = -1;
+	}
+
+	/* Start at x1,y1. */
+	x = x1;
+	y = y1;
+
+	/* Decide on a direction to progress in. */
+	if (dx >= dy) {
+		dy <<= 1;
+		balance = dy - dx;
+		dx <<= 1;
+
+		/* Loop until we reach the end point of the line. */
+		while (x != x2) {
+			scottLinegraphicsPlotClip(x, y, colour);
+			if (balance >= 0) {
+				y += incy;
+				balance -= dx;
+			}
+			balance += dy;
+			x += incx;
+		}
+		scottLinegraphicsPlotClip(x, y, colour);
+	} else {
+		dx <<= 1;
+		balance = dx - dy;
+		dy <<= 1;
+
+		/* Loop until we reach the end point of the line. */
+		while (y != y2) {
+			scottLinegraphicsPlotClip(x, y, colour);
+			if (balance >= 0) {
+				x += incx;
+				balance -= dy;
+			}
+			balance += dx;
+			y += incy;
+		}
+		scottLinegraphicsPlotClip(x, y, colour);
+	}
+}
+
+void freePixels() {
+	for (int i = 0; i < _G(_totalDrawInstructions); i++)
+		if (_G(_pixelsToDraw)[i] != nullptr)
+			delete _G(_pixelsToDraw)[i];
+	delete[] _G(_pixelsToDraw);
+}
+
+int linegraphicsGetPixel(int x, int y) {
+	return _G(_pictureBitmap)[y * 255 + x];
+}
+
+void diamondFill(int x, int y, int colour) {
+	uint8_t buffer[2048];
+	cbuf_handle_t ringbuf = circularBufInit(buffer, 2048);
+	circularBufPut(ringbuf, x, y);
+	while (!circularBufEmpty(ringbuf)) {
+		circularBufGet(ringbuf, &x, &y);
+		if (x >= 0 && x < _G(_scottGraphicsWidth) && y >= 0 &&
+			y < _G(_scottGraphicsHeight) &&
+			linegraphicsGetPixel(x, y) == _G(_bgColour)) {
+			scottLinegraphicsPlotClip(x, y, colour);
+			circularBufPut(ringbuf, x, y + 1);
+			circularBufPut(ringbuf, x, y - 1);
+			circularBufPut(ringbuf, x + 1, y);
+			circularBufPut(ringbuf, x - 1, y);
+		}
+	}
+}
+
+void drawVectorPicture(int image) {
+	if (image < 0) {
+		return;
+	}
+
+	if (_G(_vectorImageShown) == image) {
+		if (_G(_vectorState) == SHOWING_VECTOR_IMAGE) {
+			return;
+		} else {
+			if (_G(_gliSlowDraw))
+				g_scott->glk_request_timer_events(20);
+			drawSomeVectorPixels(1);
+			return;
+		}
+	}
+
+	g_scott->glk_request_timer_events(0);
+	_G(_vectorImageShown) = image;
+	if (_G(_pixelsToDraw) != nullptr)
+		freePixels();
+	_G(_pixelsToDraw) = new PixelToDraw *[255 * 97];
+	_G(_totalDrawInstructions) = 0;
+	_G(_currentDrawInstruction) = 0;
+
+	if (_G(_palChosen) == NO_PALETTE) {
+		_G(_palChosen) = _G(_game)->_palette;
+		definePalette();
+	}
+	_G(_pictureBitmap) = new uint8_t[255 * 97];
+	_G(_bgColour) = _G(_lineImages)[image]._bgColour;
+	memset(_G(_pictureBitmap), _G(_bgColour), 255 * 97);
+	if (_G(_bgColour) == 0)
+		_G(_lineColour) = 7;
+	else
+		_G(_lineColour) = 0;
+	int x = 0, y = 0, y2 = 0;
+	int arg1, arg2, arg3;
+	uint8_t *p = _G(_lineImages)[image]._data;
+	uint8_t opcode = 0;
+	while (((p < _G(_lineImages)[image]._data) || static_cast<size_t>(p - _G(_lineImages)[image]._data) < _G(_lineImages)[image]._size) && opcode != 0xff) {
+		if (p > _G(_entireFile) + _G(_fileLength)) {
+			error("Out of range! Opcode: %x. Image: %d. LineImages[%d].size: %zu\n", opcode, image, image, _G(_lineImages)[image]._size);
+			break;
+		}
+		opcode = *(p++);
+		switch (opcode) {
+		case 0xc0:
+			y = 190 - *(p++);
+			x = *(p++);
+			break;
+		case 0xc1:
+			arg1 = *(p++);
+			arg2 = *(p++);
+			arg3 = *(p++);
+			diamondFill(arg3, 190 - arg2, arg1);
+			break;
+		case 0xff:
+			break;
+		default:
+			arg1 = *(p++);
+			y2 = 190 - opcode;
+			scottLinegraphicsDrawLine(x, y, arg1, y2, _G(_lineColour));
+			x = arg1;
+			y = y2;
+			break;
+		}
+	}
+	if (_G(_pictureBitmap) != nullptr) {
+		delete[] _G(_pictureBitmap);
+		_G(_pictureBitmap) = nullptr;
+	}
+	if (_G(_gliSlowDraw))
+		g_scott->glk_request_timer_events(20);
+	else
+		drawSomeVectorPixels(1);
+}
+
+void drawSomeVectorPixels(int fromStart) {
+	_G(_vectorState) = DRAWING_VECTOR_IMAGE;
+	int i = _G(_currentDrawInstruction);
+	if (fromStart)
+		i = 0;
+	if (i == 0)
+		rectFill(0, 0, _G(_scottGraphicsWidth), _G(_scottGraphicsHeight), remap(_G(_bgColour)));
+	for (; i < _G(_totalDrawInstructions) && (!_G(_gliSlowDraw) || i < _G(_currentDrawInstruction) + 50); i++) {
+		PixelToDraw toDraw = *_G(_pixelsToDraw)[i];
+		putPixel(toDraw._x, toDraw._y, remap(toDraw._colour));
+	}
+	_G(_currentDrawInstruction) = i;
+	if (_G(_currentDrawInstruction) >= _G(_totalDrawInstructions)) {
+		g_scott->glk_request_timer_events(0);
+		_G(_vectorState) = SHOWING_VECTOR_IMAGE;
+	}
+}
+
+int drawingVector() {
+	return (_G(_totalDrawInstructions) > _G(_currentDrawInstruction));
+}
+
+} // End of namespace Scott
+} // End of namespace Glk
diff --git a/engines/glk/scott/line_drawing.h b/engines/glk/scott/line_drawing.h
new file mode 100644
index 00000000000..7e87abd79b9
--- /dev/null
+++ b/engines/glk/scott/line_drawing.h
@@ -0,0 +1,56 @@
+/* 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 GLK_SCOTT_LINEDRAWING
+#define GLK_SCOTT_LINEDRAWING
+
+#include <stdint.h>
+
+namespace Glk {
+namespace Scott {
+
+struct LineImage {
+	uint8_t *_data;
+	int _bgColour;
+	size_t _size;
+};
+
+struct PixelToDraw {
+	uint8_t _x = 0;
+	uint8_t _y = 0;
+	uint8_t _colour = 0;
+};
+
+void drawVectorPicture(int image);
+void drawSomeVectorPixels(int fromStart);
+int drawingVector();
+void freePixels();
+
+enum VectorStateType : int {
+	NO_VECTOR_IMAGE,
+	DRAWING_VECTOR_IMAGE,
+	SHOWING_VECTOR_IMAGE
+};
+
+} // End of namespace Scott
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/scott/saga_draw.cpp b/engines/glk/scott/saga_draw.cpp
new file mode 100644
index 00000000000..79db99b7a10
--- /dev/null
+++ b/engines/glk/scott/saga_draw.cpp
@@ -0,0 +1,655 @@
+/* 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 "glk/scott/saga_draw.h"
+#include "glk/scott/definitions.h"
+#include "glk/scott/detect_game.h"
+#include "glk/scott/globals.h"
+#include "glk/scott/scott.h"
+
+namespace Glk {
+namespace Scott {
+
+#define INVALID_COLOR 16
+
+void rot90(uint8_t character[]) {
+	int32_t i, j;
+	uint8_t work2[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+
+	for (i = 0; i < 8; i++)
+		for (j = 0; j < 8; j++)
+			if ((character[j] & (1 << i)) != 0)
+				work2[7 - i] += 1 << j;
+
+	for (i = 0; i < 8; i++)
+		character[i] = work2[i];
+}
+
+void rot180(uint8_t character[]) {
+	int32_t i, j;
+	uint8_t work2[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+
+	for (i = 0; i < 8; i++)
+		for (j = 0; j < 8; j++)
+			if ((character[i] & (1 << j)) != 0)
+				work2[7 - i] += 1 << (7 - j);
+
+	for (i = 0; i < 8; i++)
+		character[i] = work2[i];
+}
+
+void rot270(uint8_t character[]) {
+	int32_t i, j;
+	uint8_t work2[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+
+	for (i = 0; i < 8; i++)
+		for (j = 0; j < 8; j++)
+			if ((character[j] & (1 << i)) != 0)
+				work2[i] += 1 << (7 - j);
+
+	for (i = 0; i < 8; i++)
+		character[i] = work2[i];
+}
+
+void flip(uint8_t character[]) {
+	int32_t i, j;
+	uint8_t work2[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+
+	for (i = 0; i < 8; i++)
+		for (j = 0; j < 8; j++)
+			if ((character[i] & (1 << j)) != 0)
+				work2[i] += 1 << (7 - j);
+	for (i = 0; i < 8; i++)
+		character[i] = work2[i];
+}
+
+void background(int32_t x, int32_t y, int32_t color) {
+	/* Draw the background */
+	rectFill(x * 8, y * 8, 8, 8, color);
+}
+
+void plotsprite(int32_t character, int32_t x, int32_t y, int32_t fg, int32_t bg) {
+	int32_t i, j;
+	background(x, y, bg);
+	for (i = 0; i < 8; i++) {
+		for (j = 0; j < 8; j++)
+			if ((_G(_screenchars)[character][i] & (1 << j)) != 0)
+				putPixel(x * 8 + j, y * 8 + i, fg);
+	}
+}
+
+void transform(int32_t character, int32_t flipMode, int32_t ptr) {
+	uint8_t work[8];
+	int32_t i;
+
+#ifdef DRAWDEBUG
+	debug("Plotting char: %d with flip: %02x (%s) at %d: %d,%d\n",
+		  character, flip_mode, flipdescription[(flip_mode & 48) >> 4],
+		  ptr, ptr % 0x20, ptr / 0x20);
+#endif
+
+	// first copy the character into work
+	for (i = 0; i < 8; i++)
+		work[i] = _G(_sprite)[character][i];
+
+	// Now flip it
+	if ((flipMode & 0x30) == 0x10) {
+		rot90(work);
+	}
+	if ((flipMode & 0x30) == 0x20) {
+		rot180(work);
+	}
+	if ((flipMode & 0x30) == 0x30) {
+		rot270(work);
+	}
+	if ((flipMode & 0x40) != 0) {
+		flip(work);
+	}
+	flip(work);
+
+	// Now mask it onto the previous character
+	for (i = 0; i < 8; i++) {
+		if ((flipMode & 0x0c) == 12)
+			_G(_screenchars)[ptr][i] ^= work[i];
+		else if ((flipMode & 0x0c) == 8)
+			_G(_screenchars)[ptr][i] &= work[i];
+		else if ((flipMode & 0x0c) == 4)
+			_G(_screenchars)[ptr][i] |= work[i];
+		else
+			_G(_screenchars)[ptr][i] = work[i];
+	}
+}
+
+uint8_t *drawSagaPictureFromData(uint8_t *dataptr, int xSize, int ySize, int xOff, int yOff) {
+	int32_t offset = 0, cont = 0;
+	int32_t i, x, y, mask_mode;
+	uint8_t data, data2, old = 0;
+	int32_t ink[0x22][14], paper[0x22][14];
+
+	//   uint8_t *origptr = dataptr;
+	int version = _G(_game)->_pictureFormatVersion;
+
+	offset = 0;
+	int32_t character = 0;
+	int32_t count;
+	do {
+		count = 1;
+
+		/* first handle mode */
+		data = *dataptr++;
+		if (data < 0x80) {
+			if (character > 127 && version > 2) {
+				data += 128;
+			}
+			character = data;
+#ifdef DRAWDEBUG
+			debug("******* SOLO CHARACTER: %04x\n", character);
+#endif
+			transform(character, 0, offset);
+			offset++;
+			if (offset > 767)
+				break;
+		} else {
+			// first check for a count
+			if ((data & 2) == 2) {
+				count = (*dataptr++) + 1;
+			}
+
+			// Next get character and plot (count) times
+			character = *dataptr++;
+
+			// Plot the initial character
+			if ((data & 1) == 1 && character < 128)
+				character += 128;
+
+			for (i = 0; i < count; i++)
+				transform(character, (data & 0x0c) ? (data & 0xf3) : data, offset + i);
+
+			// Now check for overlays
+			if ((data & 0xc) != 0) {
+				// We have overlays so grab each member of the stream and work out what
+				// to do with it
+
+				mask_mode = (data & 0xc);
+				data2 = *dataptr++;
+				old = data;
+				do {
+					cont = 0;
+					if (data2 < 0x80) {
+						if (version == 4 && (old & 1) == 1)
+							data2 += 128;
+#ifdef DRAWDEBUG
+						debug("Plotting %d directly (overlay) at %d\n", data2, offset);
+#endif
+						for (i = 0; i < count; i++)
+							transform(data2, old & 0x0c, offset + i);
+					} else {
+						character = *dataptr++;
+						if ((data2 & 1) == 1)
+							character += 128;
+#ifdef DRAWDEBUG
+						debug("Plotting %d with flip %02x (%s) at %d %d\n", character, (data2 | mask_mode), flipdescription[((data2 | mask_mode) & 48) >> 4],
+							  offset, count);
+#endif
+						for (i = 0; i < count; i++)
+							transform(character, (data2 & 0xf3) | mask_mode, offset + i);
+						if ((data2 & 0x0c) != 0) {
+							mask_mode = (data2 & 0x0c);
+							old = data2;
+							cont = 1;
+							data2 = *dataptr++;
+						}
+					}
+				} while (cont != 0);
+			}
+			offset += count;
+		}
+	} while (offset < xSize * ySize);
+
+	y = 0;
+	x = 0;
+
+	uint8_t colour = 0;
+	// Note version 3 count is inverse it is repeat previous colour
+	// Whilst version0-2 count is repeat next character
+	while (y < ySize) {
+		if (dataptr > _G(_entireFile) && static_cast<size_t>(dataptr - _G(_entireFile)) > _G(_fileLength))
+			return dataptr - 1;
+		data = *dataptr++;
+		if ((data & 0x80)) {
+			count = (data & 0x7f) + 1;
+			if (version >= 3) {
+				count--;
+			} else {
+				colour = *dataptr++;
+			}
+		} else {
+			count = 1;
+			colour = data;
+		}
+		while (count > 0) {
+			// Now split up depending on which version we're using
+
+			// For version 3+
+
+			if (_G(_drawToBuffer))
+				_G(_buffer)[(yOff + y) * 32 + (xOff + x)][8] = colour;
+			else {
+				if (version > 2) {
+					if (x > 33)
+						return nullptr;
+					// ink is 0-2, screen is 3-5, 6 in bright flag
+					ink[x][y] = colour & 0x07;
+					paper[x][y] = (colour & 0x38) >> 3;
+
+					if ((colour & 0x40) == 0x40) {
+						paper[x][y] += 8;
+						ink[x][y] += 8;
+					}
+				} else {
+					if (x > 33)
+						return nullptr;
+					paper[x][y] = colour & 0x07;
+					ink[x][y] = ((colour & 0x70) >> 4);
+
+					if ((colour & 0x08) == 0x08 || version < 2) {
+						paper[x][y] += 8;
+						ink[x][y] += 8;
+					}
+				}
+			}
+
+			x++;
+			if (x == xSize) {
+				x = 0;
+				y++;
+			}
+			count--;
+		}
+	}
+	offset = 0;
+	int32_t xoff2;
+	for (y = 0; y < ySize; y++)
+		for (x = 0; x < xSize; x++) {
+			xoff2 = xOff;
+			if (version > 0 && version < 3)
+				xoff2 = xOff - 4;
+
+			if (_G(_drawToBuffer)) {
+				for (i = 0; i < 8; i++)
+					_G(_buffer)[(y + yOff) * 32 + x + xoff2][i] = _G(_screenchars)[offset][i];
+			} else {
+				plotsprite(offset, x + xoff2, y + yOff, remap(ink[x][y]), remap(paper[x][y]));
+			}
+
+#ifdef DRAWDEBUG
+			debug("(gfx#:plotting %d,%d:paper=%s,ink=%s)\n", x + xoff2, y + yoff,
+				  colortext(remap(paper[x][y])), colortext(remap(ink[x][y])));
+#endif
+			offset++;
+			if (offset > 766)
+				break;
+		}
+	return dataptr;
+}
+
+void drawSagaPictureNumber(int pictureNumber) {
+	int numgraphics = _G(_game)->_numberOfPictures;
+	if (pictureNumber >= numgraphics) {
+		error("Invalid image number % d !Last image: % d\n ", pictureNumber, numgraphics - 1);
+		return;
+	}
+
+	Image img = _G(_images)[pictureNumber];
+
+	if (img._imageData == nullptr)
+		return;
+
+	drawSagaPictureFromData(img._imageData, img._width, img._height, img._xOff, img._yOff);
+}
+
+void sagaSetup(size_t imgOffset) {
+	int32_t i, y;
+
+	Common::Array<uint16_t> imageOffsets(_G(_game)->_numberOfPictures);
+
+	if (_G(_palChosen) == NO_PALETTE) {
+		_G(_palChosen) = _G(_game)->_palette;
+	}
+
+	if (_G(_palChosen) == NO_PALETTE) {
+		error("unknown palette\n");
+	}
+
+	definePalette();
+
+	int version = _G(_game)->_pictureFormatVersion;
+
+	int32_t CHAR_START = _G(_game)->_startOfCharacters + _G(_fileBaselineOffset);
+	int32_t OFFSET_TABLE_START = _G(_game)->_startOfImageData + _G(_fileBaselineOffset);
+
+	if (_G(_game)->_startOfImageData == FOLLOWS) {
+		OFFSET_TABLE_START = CHAR_START + 0x800;
+	}
+
+	int32_t DATA_OFFSET = _G(_game)->_imageAddressOffset + _G(_fileBaselineOffset);
+	if (imgOffset)
+		DATA_OFFSET = imgOffset;
+	uint8_t *pos;
+	int numgraphics = _G(_game)->_numberOfPictures;
+
+	pos = seekToPos(_G(_entireFile), CHAR_START);
+
+#ifdef DRAWDEBUG
+	debug("Grabbing Character details\n");
+	debug("Character Offset: %04x\n", CHAR_START - _G(_fileBaselineOffset));
+#endif
+	for (i = 0; i < 256; i++) {
+		for (y = 0; y < 8; y++) {
+			_G(_sprite)[i][y] = *(pos++);
+		}
+	}
+
+	_G(_images).resize(numgraphics);
+	Image *img = &_G(_images)[0];
+
+	pos = seekToPos(_G(_entireFile), OFFSET_TABLE_START);
+	pos = seekToPos(_G(_entireFile), OFFSET_TABLE_START);
+
+	for (i = 0; i < numgraphics; i++) {
+		if (_G(_game)->_pictureFormatVersion == 0) {
+			uint16_t address;
+
+			if (i < 11) {
+				address = _G(_game)->_startOfImageData + (i * 2);
+			} else if (i < 28) {
+				address = _G(_hulkItemImageOffsets) + (i - 10) * 2;
+			} else if (i < 34) {
+				address = _G(_hulkLookImageOffsets) + (i - 28) * 2;
+			} else {
+				address = _G(_hulkSpecialImageOffsets) + (i - 34) * 2;
+			}
+
+			address += _G(_fileBaselineOffset);
+			address = _G(_entireFile)[address] + _G(_entireFile)[address + 1] * 0x100;
+
+			imageOffsets[i] = address + _G(_hulkImageOffset);
+		} else {
+			imageOffsets[i] = *(pos++);
+			imageOffsets[i] += *(pos++) * 0x100;
+		}
+	}
+
+	for (int picture_number = 0; picture_number < numgraphics; picture_number++) {
+		pos = seekToPos(_G(_entireFile), imageOffsets[picture_number] + DATA_OFFSET);
+		if (pos == 0)
+			return;
+
+		img->_width = *(pos++);
+		if (img->_width > 32)
+			img->_width = 32;
+
+		img->_height = *(pos++);
+		if (img->_height > 12)
+			img->_height = 12;
+
+		if (version > 0) {
+			img->_xOff = *(pos++);
+			if (img->_xOff > 32)
+				img->_xOff = 4;
+			img->_yOff = *(pos++);
+			if (img->_yOff > 12)
+				img->_yOff = 0;
+		} else {
+			if (picture_number > 9 && picture_number < 28) {
+				img->_xOff = _G(_entireFile)[_G(_hulkCoordinates) + picture_number - 10 + _G(_fileBaselineOffset)];
+				img->_yOff = _G(_entireFile)[_G(_hulkCoordinates) + 18 + picture_number - 10 + _G(_fileBaselineOffset)];
+			} else {
+				img->_xOff = img->_yOff = 0;
+			}
+		}
+
+		img->_imageData = pos;
+
+		img++;
+	}
+}
+
+void putPixel(glsi32 x, glsi32 y, int32_t color) {
+	int yOffset = 0;
+
+	glui32 glk_color = ((_G(_pal)[color][0] << 16)) | ((_G(_pal)[color][1] << 8)) | (_G(_pal)[color][2]);
+
+	g_scott->glk_window_fill_rect(_G(_graphics), glk_color, x * _G(_pixelSize) + _G(_xOffset),
+							   y * _G(_pixelSize) + yOffset, _G(_pixelSize), _G(_pixelSize));
+}
+
+void rectFill(int32_t x, int32_t y, int32_t width, int32_t height, int32_t color) {
+	int yOffset = 0;
+
+	int bufferpos = (y / 8) * 32 + (x / 8);
+	if (bufferpos >= 0xD80)
+		return;
+	_G(_buffer)[bufferpos][8] = _G(_buffer)[bufferpos][8] | (color << 3);
+
+	glui32 glk_color = ((_G(_pal)[color][0] << 16)) | ((_G(_pal)[color][1] << 8)) | (_G(_pal)[color][2]);
+
+	g_scott->glk_window_fill_rect(_G(_graphics), glk_color, x * _G(_pixelSize) + _G(_xOffset),
+							   y * _G(_pixelSize) + yOffset, width * _G(_pixelSize), height * _G(_pixelSize));
+}
+
+void setColor(int32_t index, RGB *colour) {
+	_G(_pal)[index][0] = (*colour)[0];
+	_G(_pal)[index][1] = (*colour)[1];
+	_G(_pal)[index][2] = (*colour)[2];
+}
+
+void definePalette() {
+	/* set up the palette */
+	if (_G(_palChosen) == VGA) {
+		RGB black = {0, 0, 0};
+		RGB blue = {0, 0, 255};
+		RGB red = {255, 0, 0};
+		RGB magenta = {255, 0, 255};
+		RGB green = {0, 255, 0};
+		RGB cyan = {0, 255, 255};
+		RGB yellow = {255, 255, 0};
+		RGB white = {255, 255, 255};
+		RGB brblack = {0, 0, 0};
+		RGB brblue = {0, 0, 255};
+		RGB brred = {255, 0, 0};
+		RGB brmagenta = {255, 0, 255};
+		RGB brgreen = {0, 255, 0};
+		RGB brcyan = {0, 255, 255};
+		RGB bryellow = {255, 255, 0};
+		RGB brwhite = {255, 255, 255};
+
+		setColor(0, &black);
+		setColor(1, &blue);
+		setColor(2, &red);
+		setColor(3, &magenta);
+		setColor(4, &green);
+		setColor(5, &cyan);
+		setColor(6, &yellow);
+		setColor(7, &white);
+		setColor(8, &brblack);
+		setColor(9, &brblue);
+		setColor(10, &brred);
+		setColor(11, &brmagenta);
+		setColor(12, &brgreen);
+		setColor(13, &brcyan);
+		setColor(14, &bryellow);
+		setColor(15, &brwhite);
+	} else if (_G(_palChosen) == ZX) {
+		/* corrected Sinclair ZX palette (pretty dull though) */
+		RGB black = {0, 0, 0};
+		RGB blue = {0, 0, 154};
+		RGB red = {154, 0, 0};
+		RGB magenta = {154, 0, 154};
+		RGB green = {0, 154, 0};
+		RGB cyan = {0, 154, 154};
+		RGB yellow = {154, 154, 0};
+		RGB white = {154, 154, 154};
+		RGB brblack = {0, 0, 0};
+		RGB brblue = {0, 0, 170};
+		RGB brred = {186, 0, 0};
+		RGB brmagenta = {206, 0, 206};
+		RGB brgreen = {0, 206, 0};
+		RGB brcyan = {0, 223, 223};
+		RGB bryellow = {239, 239, 0};
+		RGB brwhite = {255, 255, 255};
+
+		setColor(0, &black);
+		setColor(1, &blue);
+		setColor(2, &red);
+		setColor(3, &magenta);
+		setColor(4, &green);
+		setColor(5, &cyan);
+		setColor(6, &yellow);
+		setColor(7, &white);
+		setColor(8, &brblack);
+		setColor(9, &brblue);
+		setColor(10, &brred);
+		setColor(11, &brmagenta);
+		setColor(12, &brgreen);
+		setColor(13, &brcyan);
+		setColor(14, &bryellow);
+		setColor(15, &brwhite);
+
+		_G(_whiteColour) = 15;
+		_G(_blueColour) = 9;
+		_G(_diceColour) = 0xff0000;
+	} else if (_G(_palChosen) == ZXOPT) {
+		/* optimized but not realistic Sinclair ZX palette (SPIN emu) */
+		RGB black = {0, 0, 0};
+		RGB blue = {0, 0, 202};
+		RGB red = {202, 0, 0};
+		RGB magenta = {202, 0, 202};
+		RGB green = {0, 202, 0};
+		RGB cyan = {0, 202, 202};
+		RGB yellow = {202, 202, 0};
+		RGB white = {202, 202, 202};
+		/*
+	 old David Lodge palette:
+
+	 RGB black = { 0, 0, 0 };
+	 RGB blue = { 0, 0, 214 };
+	 RGB red = { 214, 0, 0 };
+	 RGB magenta = { 214, 0, 214 };
+	 RGB green = { 0, 214, 0 };
+	 RGB cyan = { 0, 214, 214 };
+	 RGB yellow = { 214, 214, 0 };
+	 RGB white = { 214, 214, 214 };
+	 */
+		RGB brblack = {0, 0, 0};
+		RGB brblue = {0, 0, 255};
+		RGB brred = {255, 0, 20};
+		RGB brmagenta = {255, 0, 255};
+		RGB brgreen = {0, 255, 0};
+		RGB brcyan = {0, 255, 255};
+		RGB bryellow = {255, 255, 0};
+		RGB brwhite = {255, 255, 255};
+
+		setColor(0, &black);
+		setColor(1, &blue);
+		setColor(2, &red);
+		setColor(3, &magenta);
+		setColor(4, &green);
+		setColor(5, &cyan);
+		setColor(6, &yellow);
+		setColor(7, &white);
+		setColor(8, &brblack);
+		setColor(9, &brblue);
+		setColor(10, &brred);
+		setColor(11, &brmagenta);
+		setColor(12, &brgreen);
+		setColor(13, &brcyan);
+		setColor(14, &bryellow);
+		setColor(15, &brwhite);
+
+		_G(_whiteColour) = 15;
+		_G(_blueColour) = 9;
+		_G(_diceColour) = 0xff0000;
+
+	} else if ((_G(_palChosen) == C64A) || (_G(_palChosen) == C64B)) {
+		/* and now: C64 palette (pepto/VICE) */
+		RGB black = {0, 0, 0};
+		RGB white = {255, 255, 255};
+		RGB red = {191, 97, 72};
+		RGB cyan = {153, 230, 249};
+		RGB purple = {177, 89, 185};
+		RGB green = {121, 213, 112};
+		RGB blue = {95, 72, 233};
+		RGB yellow = {247, 255, 108};
+		RGB orange = {186, 134, 32};
+		RGB brown = {116, 105, 0};
+		RGB lred = {180, 105, 164};
+		RGB dgrey = {69, 69, 69};
+		RGB grey = {167, 167, 167};
+		RGB lgreen = {154, 210, 134};
+		RGB lblue = {162, 143, 255};
+		RGB lgrey = {150, 150, 150};
+
+		setColor(0, &black);
+		setColor(1, &white);
+		setColor(2, &red);
+		setColor(3, &cyan);
+		setColor(4, &purple);
+		setColor(5, &green);
+		setColor(6, &blue);
+		setColor(7, &yellow);
+		setColor(8, &orange);
+		setColor(9, &brown);
+		setColor(10, &lred);
+		setColor(11, &dgrey);
+		setColor(12, &grey);
+		setColor(13, &lgreen);
+		setColor(14, &lblue);
+		setColor(15, &lgrey);
+
+		_G(_whiteColour) = 1;
+		_G(_blueColour) = 6;
+		_G(_diceColour) = 0x5f48e9;
+	}
+}
+
+int32_t remap(int32_t color) {
+	int32_t mapcol;
+
+	if ((_G(_palChosen) == ZX) || (_G(_palChosen) == ZXOPT)) {
+		/* nothing to remap here; shows that the gfx were created on a ZX */
+		mapcol = (((color >= 0) && (color <= 15)) ? color : INVALID_COLOR);
+	} else if (_G(_palChosen) == C64A) {
+		/* remap A determined from Golden Baton, applies to S1/S3/S13 too (8col) */
+		int32_t c64remap[] = {0, 6, 2, 4, 5, 3, 7, 1, 8, 1, 1, 1, 7, 12, 8, 7};
+		mapcol = (((color >= 0) && (color <= 15)) ? c64remap[color] : INVALID_COLOR);
+	} else if (_G(_palChosen) == C64B) {
+		/* remap B determined from Spiderman (16col) */
+		int32_t c64remap[] = {0, 6, 9, 4, 5, 14, 8, 12, 0, 6, 2, 4, 5, 3, 7, 1};
+		mapcol = (((color >= 0) && (color <= 15)) ? c64remap[color] : INVALID_COLOR);
+	} else
+		mapcol = (((color >= 0) && (color <= 15)) ? color : INVALID_COLOR);
+
+	return (mapcol);
+}
+
+} // End of namespace Scott
+} // End of namespace Glk
diff --git a/engines/glk/scott/saga_draw.h b/engines/glk/scott/saga_draw.h
new file mode 100644
index 00000000000..3d8ade31903
--- /dev/null
+++ b/engines/glk/scott/saga_draw.h
@@ -0,0 +1,56 @@
+/* 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 GLK_SCOTT_SAGADRAW
+#define GLK_SCOTT_SAGADRAW
+
+#include "glk/glk_types.h"
+#include <stdint.h>
+
+namespace Glk {
+namespace Scott {
+
+struct Image {
+	uint8_t *_imageData;
+	uint8_t _xOff;
+	uint8_t _yOff;
+	uint8_t _width;
+	uint8_t _height;
+};
+
+typedef uint8_t RGB[3];
+typedef RGB PALETTE[16];
+
+uint8_t *drawSagaPictureFromData(uint8_t *dataptr, int xSize, int ySize, int xOff, int yOff);
+void drawSagaPictureNumber(int pictureNumber);
+
+void sagaSetup(size_t imgOffset);
+
+void putPixel(glsi32 x, glsi32 y, int32_t color);
+void rectFill(int32_t x, int32_t y, int32_t width, int32_t height, int32_t color);
+void definePalette();
+
+int32_t remap(int32_t color);
+
+} // End of namespace Scott
+} // End of namespace Glk
+
+#endif


Commit: 5a4bda94a97bc530b089bb51e6e6000a89bea83c
    https://github.com/scummvm/scummvm/commit/5a4bda94a97bc530b089bb51e6e6000a89bea83c
Author: Avijeet (am388488 at gmail.com)
Date: 2022-04-12T20:35:24-07:00

Commit Message:
GLK: SCOTT: Add ring buffer for drawing methods.

Changed paths:
  A engines/glk/scott/ring_buffer.cpp
  A engines/glk/scott/ring_buffer.h


diff --git a/engines/glk/scott/ring_buffer.cpp b/engines/glk/scott/ring_buffer.cpp
new file mode 100644
index 00000000000..de2aa18ae8b
--- /dev/null
+++ b/engines/glk/scott/ring_buffer.cpp
@@ -0,0 +1,134 @@
+/* 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 "glk/scott/ring_buffer.h"
+
+namespace Glk {
+namespace Scott {
+
+// The hidden definition of our circular buffer structure
+struct CircularBuf {
+	uint8_t *_buffer;
+	size_t _head;
+	size_t _tail;
+	size_t _max; // of the buffer
+	bool _full;
+};
+
+// Return a pointer to a struct instance
+cbuf_handle_t circularBufInit(uint8_t *buffer, size_t size) {
+	cbuf_handle_t cbuf = new CircularBuf;
+	cbuf->_buffer = buffer;
+	cbuf->_max = size;
+	circularBufReset(cbuf);
+
+	return cbuf;
+}
+
+void circularBufReset(cbuf_handle_t me) {
+	me->_head = 0;
+	me->_tail = 0;
+	me->_full = false;
+}
+
+void circularBufFree(cbuf_handle_t me) {
+	delete me;
+}
+
+bool circularBufFull(cbuf_handle_t me) {
+	return me->_full;
+}
+
+bool circularBufEmpty(cbuf_handle_t me) {
+	return (!me->_full && (me->_head == me->_tail));
+}
+
+size_t circularBufCapacity(cbuf_handle_t me) {
+	return me->_max;
+}
+
+size_t circularBufSize(cbuf_handle_t me) {
+	size_t size = me->_max;
+
+	if (!me->_full) {
+		if (me->_head >= me->_tail) {
+			size = (me->_head - me->_tail);
+		} else {
+			size = (me->_max + me->_head - me->_tail);
+		}
+	}
+
+	return size;
+}
+
+static void advancePointer(cbuf_handle_t me) {
+	if (me->_full) {
+		if (++(me->_tail) == me->_max) {
+			me->_tail = 0;
+		}
+	}
+
+	if (++(me->_head) == me->_max) {
+		me->_head = 0;
+	}
+	me->_full = (me->_head == me->_tail);
+}
+
+static void retreatPointer(cbuf_handle_t me) {
+
+	me->_full = false;
+	if (++(me->_tail) == me->_max) {
+		me->_tail = 0;
+	}
+}
+
+int circularBufPut(cbuf_handle_t me, uint8_t x, uint8_t y) {
+	int r = -1;
+
+	if (!circularBufFull(me)) {
+		me->_buffer[me->_head] = x;
+		advancePointer(me);
+		if (!circularBufFull(me)) {
+			me->_buffer[me->_head] = y;
+			advancePointer(me);
+			r = 0;
+		}
+	}
+	return r;
+}
+
+int circularBufGet(cbuf_handle_t me, int *x, int *y) {
+	int r = -1;
+
+	if (!circularBufEmpty(me)) {
+		*x = me->_buffer[me->_tail];
+		retreatPointer(me);
+		if (!circularBufEmpty(me)) {
+			*y = me->_buffer[me->_tail];
+			retreatPointer(me);
+			r = 0;
+		}
+	}
+	return r;
+}
+
+} // End of namespace Scott
+} // End of namespace Glk
diff --git a/engines/glk/scott/ring_buffer.h b/engines/glk/scott/ring_buffer.h
new file mode 100644
index 00000000000..1a0a3597565
--- /dev/null
+++ b/engines/glk/scott/ring_buffer.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 GLK_SCOTT_RINGBUFFER
+#define GLK_SCOTT_RINGBUFFER
+
+#include <stdint.h>
+
+namespace Glk {
+namespace Scott {
+
+// Opaque circular buffer structure
+struct CircularBuf;
+// Handle type, the way users interact with the API
+typedef CircularBuf *cbuf_handle_t;
+
+/// Pass in a storage buffer and size
+/// Returns a circular buffer handle
+cbuf_handle_t circularBufInit(uint8_t *buffer, size_t size);
+
+/// Free a circular buffer structure.
+/// Does not free data buffer; owner is responsible for that
+void circularBufFree(cbuf_handle_t me);
+
+/// Reset the circular buffer to empty, head == tail
+void circularBufReset(cbuf_handle_t me);
+
+/// Rejects new data if the buffer is full
+/// Returns 0 on success, -1 if buffer is full
+int circularBufPut(cbuf_handle_t me, uint8_t x, uint8_t y);
+
+/// Retrieve a value from the buffer
+/// Returns 0 on success, -1 if the buffer is empty
+int circularBufGet(cbuf_handle_t me, int *x, int *y);
+
+/// Returns true if the buffer is empty
+bool circularBufEmpty(cbuf_handle_t me);
+
+/// Returns true if the buffer is full
+bool circularBufFull(cbuf_handle_t me);
+
+/// Returns the maximum capacity of the buffer
+size_t circularBufCapacity(cbuf_handle_t me);
+
+/// Returns the current number of elements in the buffer
+size_t circularBufSize(cbuf_handle_t me);
+
+} // End of namespace Scott
+} // End of namespace Glk
+
+#endif


Commit: f9521be168884218a5fce85f581586b2693b824f
    https://github.com/scummvm/scummvm/commit/f9521be168884218a5fce85f581586b2693b824f
Author: Avijeet (am388488 at gmail.com)
Date: 2022-04-12T20:35:24-07:00

Commit Message:
GLK: SCOTT: Game specific info to load game data.

Changed paths:
  A engines/glk/scott/game_info.cpp
  A engines/glk/scott/game_info.h


diff --git a/engines/glk/scott/game_info.cpp b/engines/glk/scott/game_info.cpp
new file mode 100644
index 00000000000..b44f7ec4d34
--- /dev/null
+++ b/engines/glk/scott/game_info.cpp
@@ -0,0 +1,2613 @@
+/* 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 "glk/scott/definitions.h"
+#include "common/array.h"
+#include "common/str.h"
+#include "common/str-array.h"
+
+namespace Glk {
+namespace Scott {
+
+Common::Array<GameInfo> g_games = {
+	GameInfo("Pirate Adventure",
+		 PIRATE,
+		 OLD_STYLE,                 // type
+		 ENGLISH,                   // subtype
+		 THREE_LETTER_UNCOMPRESSED, // dictionary type
+
+		 66,                        // Number of items
+		 177,                       // Number of actions
+		 79,                        // Number of words
+		 26,                        // Number of rooms
+		 6,                         // Max carried items
+		 3,                         // Word length
+		 99,                        // Number of messages
+		 80,                        // number_of_verbs
+		 79,                        // number_of_nouns;
+		 0x2451,                    // header
+		 EARLY,                     // header style
+		 0,                         // no room images
+		 0,                         // no item flags
+		 0,                         // no item images
+		 0x3a50,                    // actions
+
+		 UNCOMPRESSED,
+		 0x469a,     // dictionary
+		 FOLLOWS,    // start_of_room_descriptions;
+		 FOLLOWS,    // start_of_room_connections;
+		 FOLLOWS,    // start_of_messages;
+		 FOLLOWS,    // start_of_item_descriptions;
+		 FOLLOWS,    // start_of_item_locations;
+
+		 0x2539,     // start_of_system_messages
+		 0x28de,     // start of directions
+		 0,          // start_of_characters;
+		 0,          // start_of_image_data;
+		 0,          // image_address_offset
+		 0,          // number_of_pictures;
+		 NO_PALETTE, // palette;
+		 0,
+		 0),
+	GameInfo("Voodoo Castle",
+		VOODOO,
+		OLD_STYLE,                 // type
+		ENGLISH,                   // subtype
+		THREE_LETTER_UNCOMPRESSED, // dictionary type
+
+		65,  // Number of items
+		189, // Number of actions
+		89,  // Number of words
+		25,  // Number of rooms
+		9,   // Max carried items
+		3,   // Word length
+		99,  // Number of messages
+
+		90, // number_of_verbs
+		89, // number_of_nouns;
+
+		0x2451, // header
+		EARLY,  // header style
+
+		0, // no room images
+		0, // no item flags
+		0, // no item images
+
+		0x3a50, // actions
+		UNCOMPRESSED,
+		0x4752,  // dictionary
+		FOLLOWS, // start_of_room_descriptions;
+		FOLLOWS, // start_of_room_connections;
+		FOLLOWS, // start_of_messages;
+		FOLLOWS, // start_of_item_descriptions;
+		FOLLOWS, // start_of_item_locations;
+
+		0x2539, // start_of_system_messages
+		0x28de, // start of directions
+
+		0,          // start_of_characters;
+		0,          // start_of_image_data;
+		0,          // image_address_offset
+		0,          // number_of_pictures;
+		NO_PALETTE, // palette;
+		0,          // picture_format_version;
+		0),
+	GameInfo("Strange Odyssey",
+		STRANGE,
+		OLD_STYLE,                // type
+		ENGLISH,                  // subtype
+		FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+		55,  // Number of items
+		223, // Number of actions
+		79,  // Number of words
+		35,  // Number of rooms
+		6,   // Max carried items
+		4,   // Word length
+		94,  // Number of messages
+
+		80, // number_of_verbs
+		79, // number_of_nouns;
+
+		0x2451, // header
+		EARLY,  // header style
+
+		0, // no room images
+		0, // no item flags
+		0, // no item images
+
+		0x3a50, // actions
+		UNCOMPRESSED,
+		0x499a,  // dictionary
+		FOLLOWS, // start_of_room_descriptions;
+		FOLLOWS, // start_of_room_connections;
+		FOLLOWS, // start_of_messages;
+		FOLLOWS, // start_of_item_descriptions;
+		FOLLOWS, // start_of_item_locations;
+
+		0x2539, // start_of_system_messages
+		0x28de, // start of directions
+
+		0,          // start_of_characters;
+		0,          // start_of_image_data;
+		0,          // image_address_offset
+		0,          // number_of_pictures;
+		NO_PALETTE, // palette;
+		0,          // picture_format_version;
+		0),
+	GameInfo("Buckaroo Banzai",
+		BANZAI,
+		OLD_STYLE,                // type
+		ENGLISH,                  // subtype
+		FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+		60,  // Number of items
+		266, // Number of actions
+		110, // Number of words
+		35,  // Number of rooms
+		5,   // Max carried items
+		4,   // Word length
+		95,  // Number of messages
+
+		111, // number_of_verbs
+		110, // number_of_nouns;
+
+		0x2451, // header
+		EARLY,  // header style
+
+		0, // no room images
+		0, // no item flags
+		0, // no item images
+
+		0x3a50, // actions
+		UNCOMPRESSED,
+		0x4c54,  // dictionary
+		FOLLOWS, // start_of_room_descriptions;
+		FOLLOWS, // start_of_room_connections;
+		FOLLOWS, // start_of_messages;
+		FOLLOWS, // start_of_item_descriptions;
+		FOLLOWS, // start_of_item_locations;
+
+		0x2539, // start_of_system_messages
+		0x28de, // start of directions
+
+		0,          // start_of_characters;
+		0,          // start_of_image_data;
+		0,          // image_address_offset
+		0,          // number_of_pictures;
+		NO_PALETTE, // palette;
+		0,          // picture_format_version;
+		0),
+	GameInfo("The Golden Baton",
+		BATON,
+		OLD_STYLE,                // type
+		MYSTERIOUS,               // subtype
+		FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+		48,  // Number of items
+		171, // Number of actions
+		76,  // Number of words
+		31,  // Number of rooms
+		5,   // Max carried items
+		4,   // Word length
+		82,  // Number of messages
+
+		77, // number_of_verbs
+		76, // number_of_nouns;
+
+		0x2349, // header
+		EARLY,  // header style
+
+		0, // no room images
+		0, // no item flags
+		0, // no item images
+
+		0x3b56, // actions
+		UNCOMPRESSED,
+		0x473a,  // dictionary
+		FOLLOWS, // start_of_room_descriptions;
+		FOLLOWS, // start_of_room_connections;
+		FOLLOWS, // start_of_messages;
+		FOLLOWS, // start_of_item_descriptions;
+		FOLLOWS, // start_of_item_locations;
+
+		0x2450, // start_of_system_messages
+		0x277e, // start of directions
+
+		0,       // start_of_characters;
+		FOLLOWS, // start_of_image_data;
+		0,       // image_address_offset
+		30,      // number_of_pictures;
+		ZXOPT,   // palette
+		99,      // picture_format_version;
+		0),
+	GameInfo("The Golden Baton C64",
+		BATON_C64,
+		NO_TYPE,                                          // type
+		static_cast<Subtype>(MYSTERIOUS | ENGLISH | C64), // subtype
+		FOUR_LETTER_UNCOMPRESSED,                         // dictionary type
+
+		48,  // Number of items
+		166, // Number of actions
+		78,  // Number of words
+		31,  // Number of rooms
+		6,   // Max carried items
+		4,   // Word length
+		99,  // Number of messages
+
+		80, // number_of_verbs
+		79, // number_of_nouns;
+
+		0x1dd9,                // header
+		MYSTERIOUS_C64_HEADER, // header style
+
+		0, // no room images
+		0, // no item flags
+		0, // no item images
+
+		0x1df1, // actions
+		UNCOMPRESSED,
+		0x2861,  // dictionary
+		FOLLOWS, // start_of_room_descriptions;
+		FOLLOWS, // start_of_room_connections;
+		FOLLOWS, // start_of_messages;
+		FOLLOWS, // start_of_item_descriptions;
+		FOLLOWS, // start_of_item_locations;
+
+		0x408, // start_of_system_messages
+		0x408, // start of directions
+
+		0,      // start_of_characters;
+		0x38f1, // start_of_image_data;
+		0,      // image_address_offset
+		30,     // number_of_pictures;
+		C64A,   // palette
+		99,     // picture_format_version;
+		0),
+	GameInfo("The Time Machine",
+		TIME_MACHINE,
+		OLD_STYLE,                // type
+		MYSTERIOUS,               // subtype
+		FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+		62,  // Number of items
+		164, // Number of actions
+		87,  // Number of words
+		44,  // Number of rooms
+		6,   // Max carried items
+		4,   // Word length
+		82,  // Number of messages
+
+		88, // number_of_verbs
+		87, // number_of_nouns;
+
+		0x2351, // header
+		EARLY,  // header style
+
+		0, // no room images
+		0, // no item flags
+		0, // no item images
+
+		0x3b81, // actions
+		UNCOMPRESSED,
+		0x475f,  // dictionary
+		FOLLOWS, // start_of_room_descriptions;
+		FOLLOWS, // start_of_room_connections;
+		FOLLOWS, // start_of_messages;
+		FOLLOWS, // start_of_item_descriptions;
+		FOLLOWS, // start_of_item_locations;
+
+		0x24c0, // start_of_system_messages
+		0x2780, // start of directions
+
+		0, // start_of_characters;
+		FOLLOWS,
+		0,     // image_address_offset
+		43,    // number_of_pictures;
+		ZXOPT, // palette
+		99,    // picture_format_version;
+		0),
+	GameInfo("The Time Machine C64",
+		TIME_MACHINE_C64,
+		NO_TYPE,                                          // type
+		static_cast<Subtype>(MYSTERIOUS | ENGLISH | C64), // subtype
+		FOUR_LETTER_UNCOMPRESSED,                         // dictionary type
+
+		62,  // Number of items
+		161, // Number of actions
+		85,  // Number of words
+		44,  // Number of rooms
+		6,   // Max carried items
+		4,   // Word length
+		73,  // Number of messages
+
+		87, // number_of_verbs
+		85, // number_of_nouns;
+
+		0x1dd9,                // header
+		MYSTERIOUS_C64_HEADER, // header style
+
+		0, // no room images
+		0, // no item flags
+		0, // no item images
+
+		0x1df1, // actions
+		UNCOMPRESSED,
+		0x2811,  // dictionary
+		0x2b6d,  // start_of_room_descriptions;
+		FOLLOWS, // start_of_room_connections;
+		0x2f0f,  // start_of_messages;
+		FOLLOWS, // start_of_item_descriptions;
+		0x37ce,  // start_of_item_locations;
+
+		0x408, // start_of_system_messages
+		0x408, // start of directions
+
+		0,      // start_of_characters;
+		0x3872, // start_of_image_data;
+		0,      // image_address_offset
+		43,     // number_of_pictures;
+		C64A,   // palette
+		99,     // picture_format_version;
+		0),
+	GameInfo("Arrow of Death part 1",
+		ARROW1,
+		OLD_STYLE,                // type
+		MYSTERIOUS,               // subtype
+		FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+		64,  // Number of items
+		150, // Number of actions
+		90,  // Number of words
+		52,  // Number of rooms
+		5,   // Max carried items
+		4,   // Word length
+		82,  // Number of messages
+
+		91, // number_of_verbs
+		83, // number_of_nouns;
+
+		0x2351, // header
+		EARLY,  // header style
+
+		0, // no room images
+		0, // no item flags
+		0, // no item images
+
+		0x3b81, // actions
+		UNCOMPRESSED,
+		0x46b3,  // dictionary
+		0x4a41,  // start_of_room_descriptions;
+		FOLLOWS, // start_of_room_connections;
+		FOLLOWS, // start_of_messages;
+		FOLLOWS, // start_of_item_descriptions;
+		FOLLOWS, // start_of_item_locations;
+
+		0x24c0, // start_of_system_messages
+		0x2780, // start of directions
+
+		0,       // start_of_characters;
+		FOLLOWS, // start_of_image_data;
+		0,       // image_address_offset
+		51,      // number_of_pictures;
+		ZXOPT,   // palette
+		99,      // picture_format_version;
+		0),
+	GameInfo("Arrow of Death part 1 C64",
+		ARROW1_C64,
+		NO_TYPE,                                          // type
+		static_cast<Subtype>(MYSTERIOUS | ENGLISH | C64), // subtype
+		FOUR_LETTER_UNCOMPRESSED,                         // dictionary type
+
+		64,  // Number of items
+		150, // Number of actions
+		90,  // Number of words
+		52,  // Number of rooms
+		5,   // Max carried items
+		4,   // Word length
+		82,  // Number of messages
+
+		91, // number_of_verbs
+		82, // number_of_nouns;
+
+		0x1dd9,                // header
+		MYSTERIOUS_C64_HEADER, // header style
+
+		0, // no room images
+		0, // no item flags
+		0, // no item images
+
+		0x1df1, // actions
+		UNCOMPRESSED,
+		0x2761,  // dictionary
+		FOLLOWS, // start_of_room_descriptions;
+		FOLLOWS, // start_of_room_connections;
+		FOLLOWS, // start_of_messages;
+		FOLLOWS, // start_of_item_descriptions;
+		FOLLOWS, // start_of_item_locations;
+
+		0x0408, // start_of_system_messages
+		0x0408, // start of directions
+
+		0,      // start_of_characters;
+		0x38e2, // start_of_image_data;
+		0,      // image_address_offset
+		51,     // number_of_pictures;
+		C64A,   // palette
+		99,     // picture_format_version;
+		0),
+	GameInfo("Arrow of Death part 2",
+		ARROW2,
+		OLD_STYLE,                // type
+		MYSTERIOUS,               // subtype
+		FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+		91,  // Number of items
+		190, // Number of actions
+		83,  // Number of words
+		65,  // Number of rooms
+		9,   // Max carried items
+		4,   // Word length
+		82,  // Number of messages
+
+		84, // number_of_verbs
+		83, // number_of_nouns;
+
+		0x2351, // header
+		EARLY,  // header style
+
+		0, // no room images
+		0, // no item flags
+		0, // no item images
+
+		0x3b81, // actions
+		UNCOMPRESSED,
+		0x49b7,  // dictionary
+		FOLLOWS, // start_of_room_descriptions;
+		FOLLOWS, // start_of_room_connections;
+		FOLLOWS, // start_of_messages;
+		FOLLOWS, // start_of_item_descriptions;
+		FOLLOWS, // start_of_item_locations;
+
+		0x24c0, // start_of_system_messages
+		0x2780, // start of directions
+
+		0,       // start_of_characters;
+		FOLLOWS, // start_of_image_data;
+		0,       // image_address_offset
+		64,      // number_of_pictures;
+		ZXOPT,   // palette
+		99,      // picture_format_version;
+		0),
+	GameInfo("Arrow of Death part 2 C64",
+		ARROW2_C64,
+		NO_TYPE,                                          // type
+		static_cast<Subtype>(MYSTERIOUS | ENGLISH | C64), // subtype
+		FOUR_LETTER_UNCOMPRESSED,                         // dictionary type
+
+		90,  // Number of items
+		176, // Number of actions
+		82,  // Number of words
+		65,  // Number of rooms
+		9,   // Max carried items
+		4,   // Word length
+		82,  // Number of messages
+
+		81, // number_of_verbs
+		82, // number_of_nouns;
+
+		0x1dd9,                         // header
+		ARROW_OF_DEATH_PT_2_C64_HEADER, // header style
+
+		0, // no room images
+		0, // no item flags
+		0, // no item images
+
+		0x1df1, // actions
+		UNCOMPRESSED,
+		0x2901,  // dictionary
+		FOLLOWS, // start_of_room_descriptions;
+		FOLLOWS, // start_of_room_connections;
+		FOLLOWS, // start_of_messages;
+		FOLLOWS, // start_of_item_descriptions;
+		FOLLOWS, // start_of_item_locations;
+
+		0x408, // start_of_system_messages
+		0x408, // start of directions
+
+		0,      // start_of_characters;
+		0x3cac, // start_of_image_data;
+		0,      // image_address_offset
+		64,     // number_of_pictures;
+		C64A,   // palette
+		99,     // picture_format_version;
+		0),
+	GameInfo("Escape from Pulsar 7",
+		PULSAR7,
+		OLD_STYLE,                // type
+		MYSTERIOUS,               // subtype
+		FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+		90,  // Number of items
+		220, // Number of actions
+		145, // Number of words
+		45,  // Number of rooms
+		6,   // Max carried items
+		4,   // Word length
+		82,  // Number of messages
+
+		146, // number_of_verbs
+		145, // number_of_nouns;
+
+		0x2351, // header
+		EARLY,  // header style
+
+		0, // no room images
+		0, // no item flags
+		0, // no item images
+
+		0x3b81, // actions
+		UNCOMPRESSED,
+		0x4b1d,  // dictionary
+		FOLLOWS, // start_of_room_descriptions;
+		FOLLOWS, // start_of_room_connections;
+		FOLLOWS, // start_of_messages;
+		FOLLOWS, // start_of_item_descriptions;
+		FOLLOWS, // start_of_item_locations;
+
+		0x24c0, // start_of_system_messages
+		0x2780, // start of directions
+
+		0,       // start_of_characters;
+		FOLLOWS, // start_of_image_data;
+		0,       // image_address_offset
+		44,      // number_of_pictures;
+		ZXOPT,   // palette
+		99,      // picture_format_version;
+		0),
+	GameInfo("Escape from Pulsar 7 C64",
+		PULSAR7_C64,
+		NO_TYPE,                                          // type
+		static_cast<Subtype>(MYSTERIOUS | ENGLISH | C64), // subtype
+		FOUR_LETTER_UNCOMPRESSED,                         // dictionary type
+
+		88,  // Number of items
+		195, // Number of actions
+		145, // Number of words
+		45,  // Number of rooms
+		6,   // Max carried items
+		4,   // Word length
+		82,  // Number of messages
+
+		146, // number_of_verbs
+		102, // number_of_nouns;
+
+		0x1dd9, // header
+		EARLY,  // header style
+
+		0, // no room images
+		0, // no item flags
+		0, // no item images
+
+		0x1da3, // actions
+		UNCOMPRESSED,
+		0x29e3,  // dictionary
+		FOLLOWS, // start_of_room_descriptions;
+		FOLLOWS, // start_of_room_connections;
+		FOLLOWS, // start_of_messages;
+		FOLLOWS, // start_of_item_descriptions;
+		FOLLOWS, // start_of_item_locations;
+
+		0x408, // start_of_system_messages
+		0x408, // start of directions
+
+		0,      // start_of_characters;
+		0x3bf4, // start_of_image_data;
+		0,      // image_address_offset
+		44,     // number_of_pictures;
+		C64A,   // palette
+		99,     // picture_format_version;
+		0),
+	GameInfo("Circus",
+		CIRCUS,
+		OLD_STYLE,                // type
+		MYSTERIOUS,               // subtype
+		FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+		65,  // Number of items
+		165, // Number of actions
+		97,  // Number of words
+		36,  // Number of rooms
+		6,   // Max carried items
+		4,   // Word length
+		82,  // Number of messages
+
+		98, // number_of_verbs
+		97, // number_of_nouns;
+
+		0x2349, // header
+		EARLY,  // header style
+
+		0, // no room images
+		0, // no item flags
+		0, // no item images
+
+		0x3b56, // actions
+		UNCOMPRESSED,
+		0x471a,  // dictionary
+		FOLLOWS, // start_of_room_descriptions;
+		FOLLOWS, // start_of_room_connections;
+		FOLLOWS, // start_of_messages;
+		FOLLOWS, // start_of_item_descriptions;
+		FOLLOWS, // start_of_item_locations;
+
+		0x24c0, // start_of_system_messages
+		0x277E, // start of directions
+
+		0,       // start_of_characters;
+		FOLLOWS, // start_of_image_data;
+		0,       // image_address_offset
+		35,      // number_of_pictures;
+		ZXOPT,   // palette
+		99,      // picture_format_version;
+		0),
+	GameInfo("Circus C64",
+		CIRCUS_C64,
+		NO_TYPE,                                          // type
+		static_cast<Subtype>(MYSTERIOUS | ENGLISH | C64), // subtype
+		FOUR_LETTER_UNCOMPRESSED,                         // dictionary type
+
+		65,  // Number of items
+		165, // Number of actions
+		97,  // Number of words
+		36,  // Number of rooms
+		6,   // Max carried items
+		4,   // Word length
+		72,  // Number of messages
+
+		98, // number_of_verbs
+		96, // number_of_nouns;
+
+		0x1dd9,                // header
+		MYSTERIOUS_C64_HEADER, // header style
+
+		0, // no room images
+		0, // no item flags
+		0, // no item images
+
+		0x1df1, // actions
+		UNCOMPRESSED,
+		0x2851,  // dictionary
+		FOLLOWS, // start_of_room_descriptions;
+		FOLLOWS, // start_of_room_connections;
+		FOLLOWS, // start_of_messages;
+		FOLLOWS, // start_of_item_descriptions;
+		FOLLOWS, // start_of_item_locations;
+
+		0x408, // start_of_system_messages
+		0x408, // start of directions
+
+		0,      // start_of_characters;
+		0x3914, // start_of_image_data;
+		0,      // image_address_offset
+		35,     // number_of_pictures;
+		C64A,   // palette
+		99,     // picture_format_version;
+		0),
+	GameInfo("Feasibility Experiment",
+		FEASIBILITY,
+		OLD_STYLE,                // type
+		MYSTERIOUS,               // subtype
+		FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+		65,  // Number of items
+		164, // Number of actions
+		82,  // Number of words
+		59,  // Number of rooms
+		5,   // Max carried items
+		4,   // Word length
+		82,  // Number of messages
+
+		83, // number_of_verbs
+		82, // number_of_nouns;
+
+		0x2351, // header
+		EARLY,  // header style
+
+		0, // no room images
+		0, // no item flags
+		0, // no item images
+
+		0x3b81, // actions
+		UNCOMPRESSED,
+		0x47bf,  // dictionary
+		FOLLOWS, // start_of_room_descriptions;
+		FOLLOWS, // start_of_room_connections;
+		FOLLOWS, // start_of_messages;
+		FOLLOWS, // start_of_item_descriptions;
+		FOLLOWS, // start_of_item_locations;
+
+		0x24c0, // start_of_system_messages
+		0x2780, // start of directions
+
+		0,       // start_of_characters;
+		FOLLOWS, // start_of_image_data;
+		0,       // image_address_offset
+		58,      // number_of_pictures;
+		ZXOPT,   // palette
+		99,      // picture_format_version;
+		0),
+	GameInfo("Feasibility Experiment C64",
+		FEASIBILITY_C64,
+		NO_TYPE,                                          // type
+		static_cast<Subtype>(MYSTERIOUS | ENGLISH | C64), // subtype
+		FOUR_LETTER_UNCOMPRESSED,                         // dictionary type
+
+		65,  // Number of items
+		156, // Number of actions
+		79,  // Number of words
+		59,  // Number of rooms
+		6,   // Max carried items
+		4,   // Word length
+		82,  // Number of messages
+
+		56, // number_of_verbs
+		80, // number_of_nouns;
+
+		0x1dd9, // header
+		EARLY,  // header style
+
+		0, // no room images
+		0, // no item flags
+		0, // no item images
+
+		0x1df3, // actions
+		UNCOMPRESSED,
+		0x27c3,  // dictionary
+		FOLLOWS, // start_of_room_descriptions;
+		FOLLOWS, // start_of_room_connections;
+		FOLLOWS, // start_of_messages;
+		FOLLOWS, // start_of_item_descriptions;
+		FOLLOWS, // start_of_item_locations;
+
+		0x408, // start_of_system_messages
+		0x408, // start of directions
+
+		0,      // start_of_characters;
+		0x3876, // start_of_image_data;
+		0,      // image_address_offset
+		58,     // number_of_pictures;
+		C64A,   // palette
+		99,     // picture_format_version;
+		0),
+	GameInfo("The Wizard of Akyrz",
+		AKYRZ,
+		OLD_STYLE,                // type
+		MYSTERIOUS,               // subtype
+		FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+		49,  // Number of items
+		201, // Number of actions
+		85,  // Number of words
+		40,  // Number of rooms
+		6,   // Max carried items
+		4,   // Word length
+		82,  // Number of messages
+
+		86, // number_of_verbs
+		85, // number_of_nouns;
+
+		0x2351, // header
+		EARLY,  // header style
+
+		0, // no room images
+		0, // no item flags
+		0, // no item images
+
+		0x3b81, // actions
+		UNCOMPRESSED,
+		0x497d,  // dictionary
+		FOLLOWS, // start_of_room_descriptions;
+		FOLLOWS, // start_of_room_connections;
+		FOLLOWS, // start_of_messages;
+		FOLLOWS, // start_of_item_descriptions;
+		FOLLOWS, // start_of_item_locations;
+
+		0x24c0, // start_of_system_messages
+		0x2780, // start of directions
+
+		0,       // start_of_characters;
+		FOLLOWS, // start_of_image_data;
+		0,       // image_address_offset
+		39,      // number_of_pictures;
+		ZXOPT,   // palette
+		99,      // picture_format_version;
+		0),
+	GameInfo("The Wizard of Akyrz C64",
+		AKYRZ_C64,
+		NO_TYPE,                                          // type
+		static_cast<Subtype>(MYSTERIOUS | ENGLISH | C64), // subtype
+		FOUR_LETTER_UNCOMPRESSED,                         // dictionary type
+
+		49,  // Number of items
+		199, // Number of actions
+		85,  // Number of words
+		40,  // Number of rooms
+		6,   // Max carried items
+		4,   // Word length
+		82,  // Number of messages
+
+		67, // number_of_verbs
+		85, // number_of_nouns;
+
+		0x1dd9,                // header
+		MYSTERIOUS_C64_HEADER, // header style
+
+		0, // no room images
+		0, // no item flags
+		0, // no item images
+
+		0x1df1, // actions
+		UNCOMPRESSED,
+		0x2a71,  // dictionary
+		FOLLOWS, // start_of_room_descriptions;
+		FOLLOWS, // start_of_room_connections;
+		FOLLOWS, // start_of_messages;
+		FOLLOWS, // start_of_item_descriptions;
+		FOLLOWS, // start_of_item_locations;
+
+		0x408, // start_of_system_messages
+		0x408, // start of directions
+
+		0,      // start_of_characters;
+		0x3bce, // start_of_image_data;
+		0,      // image_address_offset
+		39,     // number_of_pictures;
+		C64A,   // palette
+		99,     // picture_format_version;
+		0),
+	GameInfo("Perseus and Andromeda",
+		PERSEUS,
+		OLD_STYLE,                // type
+		MYSTERIOUS,               // subtype
+		FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+		60,  // Number of items
+		178, // Number of actions
+		130, // Number of words
+		40,  // Number of rooms
+		6,   // Max carried items
+		4,   // Word length
+		82,  // Number of messages
+
+		131, // number_of_verbs
+		130, // number_of_nouns;
+
+		0x2351, // header
+		EARLY,  // header style
+
+		0, // no room images
+		0, // no item flags
+		0, // no item images
+
+		0x3b81, // actions
+		UNCOMPRESSED,
+		0x4823,  // dictionary
+		FOLLOWS, // start_of_room_descriptions;
+		FOLLOWS, // start_of_room_connections;
+		FOLLOWS, // start_of_messages;
+		FOLLOWS, // start_of_item_descriptions;
+		FOLLOWS, // start_of_item_locations;
+
+		0x24c0, // start_of_system_messages
+		0x2780, // start of directions
+
+		0,       // start_of_characters;
+		FOLLOWS, // start_of_image_data;
+		0,       // image_address_offset
+		39,      // number_of_pictures;
+		ZXOPT,   // palette
+		99,      // picture_format_version;
+		0),
+	GameInfo("Perseus and Andromeda C64",
+		PERSEUS_C64,
+		NO_TYPE,                                          // type
+		static_cast<Subtype>(MYSTERIOUS | ENGLISH | C64), // subtype
+		FOUR_LETTER_UNCOMPRESSED,                         // dictionary type
+
+		59,  // Number of items
+		165, // Number of actions
+		130, // Number of words
+		40,  // Number of rooms
+		6,   // Max carried items
+		4,   // Word length
+		82,  // Number of messages
+
+		131, // number_of_verbs
+		82,  // number_of_nouns;
+
+		0x1dd9, // header
+		EARLY,  // header style
+
+		0, // no room images
+		0, // no item flags
+		0, // no item images
+
+		0x1df3, // actions
+		UNCOMPRESSED,
+		0x2853,  // dictionary
+		FOLLOWS, // start_of_room_descriptions;
+		FOLLOWS, // start_of_room_connections;
+		FOLLOWS, // start_of_messages;
+		FOLLOWS, // start_of_item_descriptions;
+		FOLLOWS, // start_of_item_locations;
+
+		0x408, // start_of_system_messages
+		0x408, // start of directions
+
+		0,      // start_of_characters;
+		0x3d91, // start_of_image_data;
+		0,      // image_address_offset
+		39,     // number_of_pictures;
+		C64A,   // palette
+		99,     // picture_format_version;
+		0),
+	GameInfo("Perseus and Andromeda C64",
+		PERSEUS_C64,
+		NO_TYPE,                                          // type
+		static_cast<Subtype>(MYSTERIOUS | ENGLISH | C64), // subtype
+		FOUR_LETTER_UNCOMPRESSED,                         // dictionary type
+
+		59,  // Number of items
+		165, // Number of actions
+		130, // Number of words
+		40,  // Number of rooms
+		6,   // Max carried items
+		4,   // Word length
+		82,  // Number of messages
+
+		131, // number_of_verbs
+		82,  // number_of_nouns;
+
+		0x1dd9, // header
+		EARLY,  // header style
+
+		0, // no room images
+		0, // no item flags
+		0, // no item images
+
+		0x1df3, // actions
+		UNCOMPRESSED,
+		0x2853,  // dictionary
+		FOLLOWS, // start_of_room_descriptions;
+		FOLLOWS, // start_of_room_connections;
+		FOLLOWS, // start_of_messages;
+		FOLLOWS, // start_of_item_descriptions;
+		FOLLOWS, // start_of_item_locations;
+
+		0x408, // start_of_system_messages
+		0x408, // start of directions
+
+		0,      // start_of_characters;
+		0x3d91, // start_of_image_data;
+		0,      // image_address_offset
+		39,     // number_of_pictures;
+		C64A,   // palette
+		99,     // picture_format_version;
+		0),
+	GameInfo("Perseus and Andromeda C64",
+		PERSEUS_C64,
+		NO_TYPE,                                          // type
+		static_cast<Subtype>(MYSTERIOUS | ENGLISH | C64), // subtype
+		FOUR_LETTER_UNCOMPRESSED,                         // dictionary type
+
+		59,  // Number of items
+		165, // Number of actions
+		130, // Number of words
+		40,  // Number of rooms
+		6,   // Max carried items
+		4,   // Word length
+		82,  // Number of messages
+
+		131, // number_of_verbs
+		82,  // number_of_nouns;
+
+		0x1dd9, // header
+		EARLY,  // header style
+
+		0, // no room images
+		0, // no item flags
+		0, // no item images
+
+		0x1df3, // actions
+		UNCOMPRESSED,
+		0x2853,  // dictionary
+		FOLLOWS, // start_of_room_descriptions;
+		FOLLOWS, // start_of_room_connections;
+		FOLLOWS, // start_of_messages;
+		FOLLOWS, // start_of_item_descriptions;
+		FOLLOWS, // start_of_item_locations;
+
+		0x408, // start_of_system_messages
+		0x408, // start of directions
+
+		0,      // start_of_characters;
+		0x3d91, // start_of_image_data;
+		0,      // image_address_offset
+		39,     // number_of_pictures;
+		C64A,   // palette
+		99,     // picture_format_version;
+		0),
+	GameInfo("Perseus and Andromeda Italian",
+		PERSEUS_ITALIAN,
+		OLD_STYLE,  // type
+		MYSTERIOUS, // subtype
+		ITALIAN,    // dictionary type
+
+		60,  // Number of items
+		178, // Number of actions
+		130, // Number of words
+		40,  // Number of rooms
+		6,   // Max carried items
+		4,   // Word length
+		82,  // Number of messages
+
+		131, // number_of_verbs
+		83,  // number_of_nouns;
+
+		0x045d, // header
+		EARLY,  // header style
+
+		0, // no room images
+		0, // no item flags
+		0, // no item images
+
+		0x1c8d, // actions
+		UNCOMPRESSED,
+		0x292f,  // dictionary
+		0x2e4d,  // start_of_room_descriptions;
+		FOLLOWS, // start_of_room_connections;
+		FOLLOWS, // start_of_messages;
+		FOLLOWS, // start_of_item_descriptions;
+		FOLLOWS, // start_of_item_locations;
+
+		0x5d5, // start_of_system_messages
+		0x892, // start of directions
+
+		0,      // start_of_characters;
+		0x3faa, // start_of_image_data;
+		0,      // image_address_offset
+		40,     // number_of_pictures;
+		ZXOPT,  // palette
+		99,     // picture_format_version;
+		0),
+	GameInfo("Ten Little Indians",
+		INDIANS,
+		OLD_STYLE,                // type
+		MYSTERIOUS,               // subtype
+		FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+		73,  // Number of items
+		161, // Number of actions
+		85,  // Number of words
+		63,  // Number of rooms
+		5,   // Max carried items
+		4,   // Word length
+		82,  // Number of messages
+
+		86, // number_of_verbs
+		85, // number_of_nouns;
+
+		0x2351, // header
+		EARLY,  // header style
+
+		0, // no room images
+		0, // no item flags
+		0, // no item images
+
+		0x3b81, // actions
+		UNCOMPRESSED,
+		0x47b7,  // dictionary
+		FOLLOWS, // start_of_room_descriptions;
+		FOLLOWS, // start_of_room_connections;
+		FOLLOWS, // start_of_messages;
+		FOLLOWS, // start_of_item_descriptions;
+		FOLLOWS, // start_of_item_locations;
+
+		0x24c0, // start_of_system_messages
+		0x2780, // start of directions
+
+		0,       // start_of_characters;
+		FOLLOWS, // start_of_image_data;
+		0,       // image_address_offset
+		62,      // number_of_pictures;
+		ZXOPT,   // palette
+		99,      // picture_format_version;
+		0),
+	GameInfo("Ten Little Indians C64",
+		INDIANS_C64,
+		NO_TYPE,                                          // type
+		static_cast<Subtype>(MYSTERIOUS | ENGLISH | C64), // subtype
+		FOUR_LETTER_UNCOMPRESSED,                         // dictionary type
+
+		73,  // Number of items
+		161, // Number of actions
+		82,  // Number of words
+		63,  // Number of rooms
+		5,   // Max carried items
+		4,   // Word length
+		82,  // Number of messages
+
+		64, // number_of_verbs
+		82, // number_of_nouns;
+
+		0x1dd9,             // header
+		INDIANS_C64_HEADER, // header style
+
+		0, // no room images
+		0, // no item flags
+		0, // no item images
+
+		0x1df0, // actions
+		UNCOMPRESSED,
+		0x2810,  // dictionary
+		FOLLOWS, // start_of_room_descriptions;
+		FOLLOWS, // start_of_room_connections;
+		FOLLOWS, // start_of_messages;
+		FOLLOWS, // start_of_item_descriptions;
+		FOLLOWS, // start_of_item_locations;
+
+		0x408, // start_of_system_messages
+		0x408, // start of directions
+
+		0,      // start_of_characters;
+		0x3a46, // start_of_image_data;
+		0,      // image_address_offset
+		62,     // number_of_pictures;
+		C64A,   // palette
+		99,     // picture_format_version;
+		0),
+	GameInfo("Waxworks",
+		WAXWORKS,
+		OLD_STYLE,                // type
+		MYSTERIOUS,               // subtype
+		FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+		57,  // Number of items
+		189, // Number of actions
+		106, // Number of words
+		41,  // Number of rooms
+		6,   // Max carried items
+		4,   // Word length
+		82,  // Number of messages
+
+		107, // number_of_verbs
+		106, // number_of_nouns;
+
+		0x2351, // header
+		EARLY,  // header style
+
+		0, // no room images
+		0, // no item flags
+		0, // no item images
+
+		0x3b81, // actions
+		UNCOMPRESSED,
+		0x48d3,  // dictionary
+		FOLLOWS, // start_of_room_descriptions;
+		FOLLOWS, // start_of_room_connections;
+		FOLLOWS, // start_of_messages;
+		FOLLOWS, // start_of_item_descriptions;
+		FOLLOWS, // start_of_item_locations;
+
+		0x24c0, // start_of_system_messages
+		0x2780, // start of directions
+
+		0,       // start_of_characters;
+		FOLLOWS, // start_of_image_data;
+		0,       // image_address_offset
+		40,      // number_of_pictures;
+		ZXOPT,   // palette
+		99,      // picture_format_version;
+		0),
+	GameInfo("Waxworks C64",
+		WAXWORKS_C64,
+		NO_TYPE,                                          // type
+		static_cast<Subtype>(MYSTERIOUS | ENGLISH | C64), // subtype
+		FOUR_LETTER_UNCOMPRESSED,                         // dictionary type
+
+		57,  // Number of items
+		189, // Number of actions
+		105, // Number of words
+		41,  // Number of rooms
+		6,   // Max carried items
+		4,   // Word length
+		82,  // Number of messages
+
+		91,  // number_of_verbs
+		105, // number_of_nouns;
+
+		0x1dd9, // header
+		EARLY,  // header style
+
+		0, // no room images
+		0, // no item flags
+		0, // no item images
+
+		0x1df3, // actions
+		UNCOMPRESSED,
+		0x29d3,  // dictionary
+		FOLLOWS, // start_of_room_descriptions;
+		FOLLOWS, // start_of_room_connections;
+		FOLLOWS, // start_of_messages;
+		FOLLOWS, // start_of_item_descriptions;
+		FOLLOWS, // start_of_item_locations;
+
+		0x408, // start_of_system_messages
+		0x408, // start of directions
+
+		0,      // start_of_characters;
+		0x3f31, // start_of_image_data;
+		0,      // image_address_offset
+		40,     // number_of_pictures;
+		C64A,   // palette
+		99,     // picture_format_version;
+		0),
+	GameInfo("Questprobe 1: The Hulk",
+		HULK,
+		NO_TYPE,                  // type
+		ENGLISH,                  // subtype
+		FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+		54,  // Number of items
+		261, // Number of actions
+		128, // Number of words
+		20,  // Number of rooms
+		10,  // Max carried items
+		4,   // Word length
+		99,  // Number of messages
+
+		128, // number_of_verbs
+		129, // number_of_nouns;
+
+		0x4bf4,      // header
+		HULK_HEADER, // header style
+
+		0x270c, // room images
+		0,      // item flags
+		0x26c8, // item images
+
+		0x6087, // actions
+		HULK_ACTIONS,
+		0x4cc4,  // dictionary
+		0x51cd,  // start_of_room_descriptions;
+		0x7111,  // start_of_room_connections;
+		0x575e,  // start_of_messages;
+		FOLLOWS, // start_of_item_descriptions;
+		0x5f3d,  // start_of_item_locations;
+
+		0x2553, // start_of_system_messages
+		0x28f7, // start of directions
+
+		0x281b, // start_of_characters;
+		0x2782, // start_of_image_data
+		0,      // image_address_offset
+		43,     // number_of_pictures;
+		ZXOPT,  // palette
+		0,      // picture_format_version;
+		0),
+	GameInfo("Questprobe 1: The Hulk C64", HULK_C64,
+		NO_TYPE,                  // type
+		ENGLISH,                  // subtype
+		FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+		54,  // Number of items
+		261, // Number of actions
+		128, // Number of words
+		20,  // Number of rooms
+		10,  // Max carried items
+		4,   // Word length
+		99,  // Number of messages
+
+		128, // number_of_verbs
+		129, // number_of_nouns;
+
+		0x7867,      // header
+		HULK_HEADER, // header style
+
+		0x2280, // room images
+		0,      // item flags
+		0x22ba, // item images
+
+		0x8c47, // actions
+		HULK_ACTIONS,
+		0x7884,  // dictionary
+		0x7d8d,  // start_of_room_descriptions;
+		0x9CD1,  // start_of_room_connections;
+		0x831E,  // start_of_messages;
+		FOLLOWS, // start_of_item_descriptions;
+		0x8AFD,  // start_of_item_locations;
+
+		// These are spread out all over the code
+		0,      // start_of_system_messages
+		0xae25, // start of directions
+
+		0x0d01, // start_of_characters;
+		0x2701, // start_of_image_data
+		0,      // image_address_offset
+		43,     // number_of_pictures;
+		C64B,   // palette
+		0,      // picture_format_version;
+		0),
+	GameInfo("Adventureland",
+		ADVENTURELAND,
+		NO_TYPE,                   // type
+		ENGLISH,                   // subtype
+		THREE_LETTER_UNCOMPRESSED, // dictionary type
+
+		65,  // Number of items
+		181, // Number of actions
+		69,  // Number of words
+		33,  // Number of rooms
+		6,   // Max carried items
+		3,   // Word length
+		75,  // Number of messages
+
+		70, // number_of_verbs
+		69, // number_of_nouns;
+
+		0x2473, // header
+		EARLY,  // header style
+
+		0x3ebe,  // room images
+		FOLLOWS, // item flags
+		0x3f1f,  // item images
+		0x3f5e,  // actions
+		UNCOMPRESSED,
+		0x4c10,  // dictionary
+		0x4e40,  // start_of_room_descriptions;
+		0x4abe,  // start_of_room_connections;
+		0x52c3,  // start_of_messages;
+		FOLLOWS, // start_of_item_descriptions;
+		0x4b8a,  // start_of_item_locations;
+
+		0x24eb, // start_of_system_messages
+		0x285e, // start of directions
+
+		0x631e,  // start_of_characters;
+		FOLLOWS, // start_of_image_data
+		-0x3fe5, // image_address_offset
+		41,      // number_of_pictures;
+		ZXOPT,   // palette
+		1,       // picture_format_version;
+		0),
+	GameInfo("Adventureland C64",
+		ADVENTURELAND_C64,
+		NO_TYPE,                             // type
+		static_cast<Subtype>(ENGLISH | C64), // subtype
+		THREE_LETTER_UNCOMPRESSED,           // dictionary type
+
+		62,  // Number of items
+		170, // Number of actions
+		69,  // Number of words
+		33,  // Number of rooms
+		6,   // Max carried items
+		3,   // Word length
+		75,  // Number of messages
+
+		70, // number_of_verbs
+		69, // number_of_nouns;
+
+		0x4146, // header
+		EARLY,  // header style
+
+		0x6364, // room images
+		0x85a7, // item flags
+		0x63c5, // item images
+		0x4160, // actions
+		UNCOMPRESSED,
+		0x4c10,  // dictionary
+		0x4e40,  // start_of_room_descriptions;
+		FOLLOWS, // start_of_room_connections;
+		FOLLOWS, // start_of_messages;
+		FOLLOWS, // start_of_item_descriptions;
+		0x6404,  // start_of_item_locations;
+
+		0x188, // start_of_system_messages
+		0x188, // start of directions
+
+		0x6482,  // start_of_characters;
+		0x6c82,  // start_of_image_data
+		-0x1102, // image_address_offset
+		41,      // number_of_pictures;
+		C64B,    // palette
+		1,       // picture_format_version;
+		0),
+	GameInfo("Secret Mission",
+		SECRET_MISSION,
+		SECRET_MISSION_VARIANT,    // type
+		ENGLISH,                   // subtype
+		THREE_LETTER_UNCOMPRESSED, // dictionary type
+
+		53,  // Number of items
+		164, // Number of actions
+		64,  // Number of words
+		23,  // Number of rooms
+		7,   // Max carried items
+		3,   // Word length
+		81,  // Number of messages
+
+		65, // number_of_verbs
+		65, // number_of_nouns;
+
+		0x2473,  // header
+		EARLY,   // header style
+		0x3f26,  // room images
+		FOLLOWS, // item flags
+		0x3f70,  // item images
+		0x3fa2,  // actions
+		UNCOMPRESSED,
+		0x4af0,  // dictionary
+		0x4cf8,  // start_of_room_descriptions;
+		0x49f2,  // start_of_room_connections;
+		0x4edf,  // start_of_messages;
+		FOLLOWS, // start_of_item_descriptions;
+		0x4a82,  // start_of_item_locations;
+
+		0x24eb, // start_of_system_messages
+		0x285e, // start of directions
+
+		0x625d,  // start_of_characters;
+		FOLLOWS, // start_of_image_data
+		-0x3fe5, // image_address_offset
+		44,      // number_of_pictures;
+		ZXOPT,   // palette
+		1,       // picture_format_version;
+		0),
+	GameInfo("Secret Mission C64",
+		SECRET_MISSION_C64,
+		SECRET_MISSION_VARIANT,              // type
+		static_cast<Subtype>(ENGLISH | C64), // subtype
+		THREE_LETTER_UNCOMPRESSED,           // dictionary type
+
+		49,  // Number of items
+		154, // Number of actions
+		62,  // Number of words
+		23,  // Number of rooms
+		7,   // Max carried items
+		3,   // Word length
+		81,  // Number of messages
+
+		60, // number_of_verbs
+		62, // number_of_nouns;
+
+		0x4572,  // header
+		EARLY,   // header style
+		0x458c,  // room images
+		0x6721,  // item flags
+		FOLLOWS, // item images
+		0x45a4,  // actions
+		UNCOMPRESSED,
+		0x4f54,  // dictionary
+		0x5140,  // start_of_room_descriptions;
+		FOLLOWS, // start_of_room_connections;
+		FOLLOWS, // start_of_messages;
+		FOLLOWS, // start_of_item_descriptions;
+		0x6785,  // start_of_item_locations;
+
+		0x2502, // start_of_system_messages
+		0x2502, // start of directions
+
+		0x681b,  // start_of_characters;
+		0x701b,  // start_of_image_data
+		-0x1CD0, // image_address_offset
+		44,      // number_of_pictures;
+		C64B,    // palette
+		1,       // picture_format_version;
+		0),
+	GameInfo("The Sorcerer of Claymorgue Castle",
+		CLAYMORGUE,
+		NO_TYPE,                  // type
+		ENGLISH,                  // subtype
+		FIVE_LETTER_UNCOMPRESSED, // dictionary type
+
+		75,  // Number of items
+		267, // Number of actions
+		109, // Number of words
+		32,  // Number of rooms
+		10,  // Max carried items
+		5,   // Word length
+		79,  // Number of messages
+
+		110, // number_of_verbs
+		108, // number_of_nouns;
+
+		0x246c, // header
+		EARLY,  // header style
+
+		0x3bf6,  // room images
+		FOLLOWS, // item flags
+		FOLLOWS, // item images
+		FOLLOWS, // actions
+		UNCOMPRESSED,
+		0x4ecf,  // dictionary
+		0x53f7,  // start_of_room_descriptions;
+		0x4d6f,  // start_of_room_connections;
+		0x5605,  // start_of_messages;
+		FOLLOWS, // start_of_item_descriptions;
+		0x4e35,  // start_of_item_locations;
+
+		0x24e2, // start_of_system_messages
+		0x2877, // start of directions
+
+		0x6007,  // start_of_characters;
+		0x6807,  // start_of_image_data
+		-0x3fe5, // image_address_offset
+		37,      // number_of_pictures;
+		ZXOPT,   // palette
+		1,       // picture_format_version;
+		0),
+	GameInfo("The Sorcerer of Claymorgue Castle C64",
+		CLAYMORGUE_C64,
+		NO_TYPE,                             // type
+		static_cast<Subtype>(ENGLISH | C64), // subtype
+		FIVE_LETTER_UNCOMPRESSED,            // dictionary type
+
+		75,  // Number of items
+		265, // Number of actions
+		108, // Number of words
+		32,  // Number of rooms
+		10,  // Max carried items
+		5,   // Word length
+		79,  // Number of messages
+
+		110, // number_of_verbs
+		108, // number_of_nouns;
+
+		0x47d6, // header
+		EARLY,  // header style
+
+		0x471f,  // room images
+		FOLLOWS, // item flags
+		FOLLOWS, // item images
+		0x47f0,  // actions
+		UNCOMPRESSED,
+		0x5891,  // dictionary
+		FOLLOWS, // start_of_room_descriptions;
+		FOLLOWS, // start_of_room_connections;
+		FOLLOWS, // start_of_messages;
+		FOLLOWS, // start_of_item_descriptions;
+		FOLLOWS, // start_of_item_locations;
+
+		0xa2c, // start_of_system_messages
+		0xa2c, // start of directions
+
+		0x6b1a, // start_of_characters;
+		0x731a, // start_of_image_data
+		0x6b1a, // image_address_offset
+		44,     // number_of_pictures;
+		C64B,   // palette
+		1,      // picture_format_version;
+		0),
+	GameInfo("Questprobe 2: Spiderman",
+		SPIDERMAN,
+		NO_TYPE,                  // type
+		ENGLISH,                  // subtype
+		FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+		72,  // Number of items
+		257, // Number of actions
+		124, // Number of words
+		40,  // Number of rooms
+		12,  // Max carried items
+		4,   // Word length
+		99,  // Number of messages
+
+		125, // number_of_verbs
+		125, // number_of_nouns;
+
+		0x246b, // header
+		EARLY,  // header style
+
+		0x3dd1,  // room images
+		FOLLOWS, // item flags
+		FOLLOWS, // item images
+
+		FOLLOWS, // actions
+		UNCOMPRESSED,
+		0x5036,  // dictionary
+		0x5518,  // start_of_room_descriptions;
+		0x4eac,  // start_of_room_connections;
+		0x575e,  // start_of_messages;
+		FOLLOWS, // start_of_item_descriptions;
+		0x4fa2,  // start_of_item_locations;
+
+		0x2553, // start_of_system_messages
+		0x28f7, // start of directions
+
+		0x6296,  // start_of_characters;
+		0x6a96,  // start_of_image_data
+		-0x3fe5, // image_address_offset
+		41,      // number_of_pictures;
+		ZXOPT,   // palette
+		2,       // picture_format_version;
+		0),
+	GameInfo("Questprobe 2: Spiderman C64",
+		SPIDERMAN_C64,
+		NO_TYPE,                             // type
+		static_cast<Subtype>(ENGLISH | C64), // subtype
+		FOUR_LETTER_UNCOMPRESSED,            // dictionary type
+
+		72,  // Number of items
+		245, // Number of actions
+		124, // Number of words
+		40,  // Number of rooms
+		12,  // Max carried items
+		4,   // Word length
+		98,  // Number of messages
+
+		118, // number_of_verbs
+		124, // number_of_nouns;
+
+		0x4baf, // header
+		EARLY,  // header style
+
+		0x4bc9,  // room images
+		0x70da,  // item flags
+		FOLLOWS, // item images
+
+		0x4bf2, // actions
+		UNCOMPRESSED,
+		0x5b52,  // dictionary
+		0x600c,  // start_of_room_descriptions;
+		FOLLOWS, // start_of_room_connections;
+		FOLLOWS, // start_of_messages;
+		0x6b9b,  // start_of_item_descriptions;
+		FOLLOWS, // start_of_item_locations;
+
+		0x0a7b, // start_of_system_messages
+		0x0a7b, // start of directions
+
+		0x716c, // start_of_characters;
+		0x796c, // start_of_image_data
+		0x716c, // image_address_offset
+		41,     // number_of_pictures;
+		C64B,   // palette
+		2,      // picture_format_version;
+		0),
+	GameInfo("Savage Island part I",
+		SAVAGE_ISLAND,
+		SAVAGE_ISLAND_VARIANT,    // type
+		ENGLISH,                  // subtype
+		FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+		58,  // Number of items
+		259, // Number of actions
+		84,  // Number of words
+		34,  // Number of rooms
+		6,   // Max carried items
+		4,   // Word length
+		99,  // Number of messages
+
+		82, // number_of_verbs
+		84, // number_of_nouns;
+
+		0x236d,  // header
+		LATE,    // header style
+		0x390c,  // room images
+		FOLLOWS, // item flags
+		FOLLOWS, // item images
+		0x39a6,  // actions
+		COMPRESSED,
+		0x4484,  // dictionary
+		0x47c7,  // start_of_room_descriptions;
+		FOLLOWS, // start_of_room_connections;
+		0x4b91,  // start_of_messages;
+		FOLLOWS, // start_of_item_descriptions;
+		FOLLOWS, // start_of_item_locations;
+
+		0x2423, // start_of_system_messages
+		0x25f3, // start of directions
+
+		0x570f,  // start_of_characters;
+		FOLLOWS, // start_of_image_data
+		0x600f,  // image_address_offset
+		37,      // number_of_pictures;
+		ZXOPT,   // palette
+		3,       // picture_format_version;
+		0),
+	GameInfo("Savage Island part I (C64)",
+		SAVAGE_ISLAND_C64,
+		SAVAGE_ISLAND_VARIANT,               // type
+		static_cast<Subtype>(ENGLISH | C64), // subtype
+		FOUR_LETTER_UNCOMPRESSED,            // dictionary type
+
+		58,  // Number of items
+		259, // Number of actions
+		84,  // Number of words
+		34,  // Number of rooms
+		6,   // Max carried items
+		4,   // Word length
+		99,  // Number of messages
+
+		85, // number_of_verbs
+		84, // number_of_nouns;
+
+		0x46dc,  // header
+		EARLY,   // header style
+		0x4645,  // room images
+		FOLLOWS, // item flags
+		FOLLOWS, // item images
+		0x46f6,  // actions
+		COMPRESSED,
+		0x51d6,  // dictionary
+		0x5528,  // start_of_room_descriptions;
+		FOLLOWS, // start_of_room_connections;
+		FOLLOWS, // start_of_messages;
+				 //        0x58f6, // start_of_messages;
+		FOLLOWS, // start_of_item_descriptions;
+		FOLLOWS, // start_of_item_locations;
+
+		0x0a5f, // start_of_system_messages
+		0x0a5f, // start of directions
+
+		0x6A02, // start_of_characters;
+		0x7202, // start_of_image_data
+		0x7302, // image_address_offset
+		37,     // number_of_pictures;
+		C64B,   // palette
+		3,      // picture_format_version;
+		0),
+	GameInfo("Savage Island part II",
+		SAVAGE_ISLAND2,
+		NO_TYPE,                  // type
+		ENGLISH,                  // subtype
+		FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+		48,  // Number of items
+		241, // Number of actions
+		79,  // Number of words
+		30,  // Number of rooms
+		6,   // Max carried items
+		4,   // Word length
+		95,  // Number of messages
+
+		74, // number_of_verbs
+		79, // number_of_nouns;
+
+		0x236d,  // header
+		LATE,    // header style
+		0x390c,  // room images
+		FOLLOWS, // item flags
+		FOLLOWS, // item images
+		0x398e,  // actions
+		COMPRESSED,
+		0x43f0,  // dictionary
+		0x46ed,  // start_of_room_descriptions;
+		FOLLOWS, // start_of_room_connections;
+		FOLLOWS, // start_of_messages;
+		FOLLOWS, // start_of_item_descriptions;
+		FOLLOWS, // start_of_item_locations;
+
+		0x2423, // start_of_system_messages
+		0x25f3, // start of directions
+
+		0x57d0,  // start_of_characters;
+		FOLLOWS, // start_of_image_data
+		0x60d0,  // image_address_offset
+		21,      // number_of_pictures;
+		ZXOPT,   // palette
+		3,       // picture_format_version;
+		0),
+	GameInfo("Savage Island part II (C64)",
+		SAVAGE_ISLAND2_C64,
+		NO_TYPE,                             // type
+		static_cast<Subtype>(ENGLISH | C64), // subtype
+		FOUR_LETTER_UNCOMPRESSED,            // dictionary type
+
+		48,  // Number of items
+		241, // Number of actions
+		78,  // Number of words
+		30,  // Number of rooms
+		6,   // Max carried items
+		4,   // Word length
+		94,  // Number of messages
+
+		79, // number_of_verbs
+		79, // number_of_nouns;
+
+		0x4654,  // header
+		EARLY,   // header style
+		0x6310,  // room images
+		FOLLOWS, // item flags
+		FOLLOWS, // item images
+		0x466e,  // actions
+		COMPRESSED,
+		0x50d2,  // dictionary
+		0x53e8,  // start_of_room_descriptions;
+		FOLLOWS, // start_of_room_connections;
+		FOLLOWS, // start_of_messages;
+		FOLLOWS, // start_of_item_descriptions;
+		FOLLOWS, // start_of_item_locations;
+
+		0x0a50, // start_of_system_messages
+		0x0a50, // start of directions
+
+		0x6A02, // start_of_characters;
+		0x7202, // start_of_image_data
+		0x7302, // image_address_offset
+		21,     // number_of_pictures;
+		C64B,   // palette
+		3,      // picture_format_version;
+		0),
+	GameInfo("Gremlins",
+		GREMLINS,
+		GREMLINS_VARIANT,         // type
+		ENGLISH,                  // subtype
+		FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+		99,  // Number of items
+		236, // Number of actions
+		126, // Number of words
+		42,  // Number of rooms
+		6,   // Max carried items
+		4,   // Word length
+		98,  // Number of messages
+
+		115, // number_of_verbs
+		126, // number_of_nouns;
+
+		0x2370, // header
+		LATE,   // header style
+
+		0x3a09,  // room images
+		FOLLOWS, // item flags
+		FOLLOWS, // item images
+
+		0x3afe,     // actions
+		COMPRESSED, // actions_style;
+		0x45e5,     // dictionary
+		FOLLOWS,    // start_of_room_descriptions;
+		FOLLOWS,    // start_of_room_connections;
+		FOLLOWS,    // start_of_messages;
+		FOLLOWS,    // start_of_item_descriptions;
+		FOLLOWS,    // start_of_item_locations;
+		0x2426,     // start_of_system_messages
+		0x25f6,     // start of directions
+
+		0x5be1, // start_of_characters;
+		0x63e1, // start_of_image_data
+		0x64e1, // image_address_offset
+		78,     // number_of_pictures;
+		ZXOPT,  // palette
+		3,      // picture_format_version;
+		0),
+	GameInfo("Gremlins (alternative)",
+		GREMLINS_ALT,
+		GREMLINS_VARIANT,         // type
+		ENGLISH,                  // subtype
+		FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+		99,  // Number of items
+		236, // Number of actions
+		126, // Number of words
+		42,  // Number of rooms
+		6,   // Max carried items
+		4,   // Word length
+		98,  // Number of messages
+
+		115, // number_of_verbs
+		126, // number_of_nouns;
+
+		0x2378, // header
+		LATE,   // header style
+
+		0x3a0d,  // room images
+		FOLLOWS, // item flags
+		FOLLOWS, // item images
+
+		0x3b02,     // actions
+		COMPRESSED, // actions_style;
+		0x45e5,     // dictionary
+		FOLLOWS,    // start_of_room_descriptions;
+		FOLLOWS,    // start_of_room_connections;
+		FOLLOWS,    // start_of_messages;
+		FOLLOWS,    // start_of_item_descriptions;
+		FOLLOWS,    // start_of_item_locations;
+		0x242e,     // start_of_system_messages
+		0x25FE,     // start of directions
+
+		0x5bdd, // start_of_characters;
+		0x63dd, // start_of_image_data
+		0x64dd, // image_address_offset
+		78,     // number_of_pictures;
+		ZXOPT,  // palette
+		3,      // picture_format_version;
+		0),
+	GameInfo("Gremlins (German)",
+		GREMLINS_GERMAN,
+		GREMLINS_VARIANT, // type
+		LOCALIZED,        // subtype
+		GERMAN,           // dictionary type
+
+		99,  // Number of items
+		236, // Number of actions
+		126, // Number of words
+		42,  // Number of rooms
+		6,   // Max carried items
+		5,   // Word length
+		98,  // Number of messages
+
+		115, // number_of_verbs
+		126, // number_of_nouns
+
+		0x237d, // header
+		LATE,   // header style
+
+		0x3a07,  // room images
+		FOLLOWS, // item flags
+		FOLLOWS, // item images
+
+		0x3afc, // actions
+		COMPRESSED,
+		0x45d9,  // dictionary
+		FOLLOWS, // start_of_room_descriptions;
+		FOLLOWS, // start_of_room_connections;
+		FOLLOWS, // start_of_messages;
+		FOLLOWS, // start_of_item_descriptions;
+		FOLLOWS, // start_of_item_locations;
+
+		0x23de, // start_of_system_messages
+		0x2623, // start of directions
+
+		0x643e, // start_of_characters
+		0x6c3e, // start_of_image_data
+		0x6d3e, // image_address_offset
+		72,     // number_of_pictures;
+		ZX,     // palette
+		3,      // picture_format_version;
+		0),
+	GameInfo("Gremlins C64",
+		GREMLINS_C64,
+		GREMLINS_VARIANT,                    // type
+		static_cast<Subtype>(ENGLISH | C64), // subtype
+		FOUR_LETTER_UNCOMPRESSED,            // dictionary type
+
+		99,  // Number of items
+		243, // Number of actions
+		126, // Number of words
+		42,  // Number of rooms
+		6,   // Max carried items
+		4,   // Word length
+		99,  // Number of messages
+
+		115, // number_of_verbs
+		126, // number_of_nouns;
+
+		0x4584,              // header
+		GREMLINS_C64_HEADER, // header style
+
+		0x465e,  // room images
+		FOLLOWS, // item flags
+		FOLLOWS, // item images
+
+		0x4751,     // actions
+		COMPRESSED, // actions_style;
+		0x527f,     // dictionary
+		FOLLOWS,    // start_of_room_descriptions;
+		FOLLOWS,    // start_of_room_connections;
+		FOLLOWS,    // start_of_messages;
+		FOLLOWS,    // start_of_item_descriptions;
+		0x4596,     // start_of_item_locations;
+		0x0a5e,     // start_of_system_messages
+		0x0a5e,     // start of directions
+
+		0x6a01, // start_of_characters;
+		0x7201, // start_of_image_data
+		0x7301, // image_address_offset
+		91,     // number_of_pictures;
+		C64B,   // palette
+		3,      // picture_format_version;
+		0),
+	GameInfo("Gremlins (German, C64)",
+		GREMLINS_GERMAN_C64,
+		GREMLINS_VARIANT,                      // type
+		static_cast<Subtype>(LOCALIZED | C64), // subtype
+		FIVE_LETTER_COMPRESSED,                // dictionary type
+
+		99,  // Number of items
+		243, // Number of actions
+		125, // Number of words
+		42,  // Number of rooms
+		6,   // Max carried items
+		5,   // Word length
+		98,  // Number of messages
+
+		125, // number_of_verbs
+		124, // number_of_nouns
+
+		0x4bb2,              // header
+		GREMLINS_C64_HEADER, // header style
+
+		0x4c8c,  // room images
+		FOLLOWS, // item flags
+		FOLLOWS, // item images
+
+		0x4d81, // actions
+		COMPRESSED,
+		0x585e,  // dictionary
+		FOLLOWS, // start_of_room_descriptions;
+		FOLLOWS, // start_of_room_connections;
+		FOLLOWS, // start_of_messages;
+		FOLLOWS, // start_of_item_descriptions;
+		0x4bc4,  // start_of_item_locations;
+
+		0x1325, // start_of_system_messages
+		0x1325, // start of directions
+
+		0x76c5, // start_of_characters
+		0x7cfd, // start_of_image_data
+		0x7db3, // image_address_offset
+		91,     // number_of_pictures;
+		C64B,   // palette
+		3,      // picture_format_version;
+		0),
+	GameInfo("Gremlins (Spanish)",
+		GREMLINS_SPANISH,
+		GREMLINS_VARIANT, // type
+		LOCALIZED,        // subtype
+		SPANISH,          // dictionary type
+
+		99,  // Number of items
+		236, // Number of actions
+		126, // Number of words
+		42,  // Number of rooms
+		6,   // Max carried items
+		4,   // Word length
+		98,  // Number of messages
+
+		115, // number_of_verbs
+		126, // number_of_nouns
+
+		0x23c5, // header
+		LATE,   // header style
+
+		0x3993,  // room images
+		FOLLOWS, // item flags
+		FOLLOWS, // item images
+
+		0x3a88, // actions
+		COMPRESSED,
+		0x455f,  // dictionary
+		FOLLOWS, // start_of_room_descriptions;
+		FOLLOWS, // start_of_room_connections;
+		FOLLOWS, // start_of_messages;
+		FOLLOWS, // start_of_item_descriptions;
+		FOLLOWS, // start_of_item_locations;
+
+		0x2426, // start_of_system_messages
+		0x25c4, // start of directions
+
+		0x6171, // start_of_characters
+		0x6971, // start_of_image_data
+		0x6A71, // image_address_offset
+		74,     // number_of_pictures;
+		ZXOPT,  // palette
+		3,      // picture_format_version;
+		0),
+	GameInfo("Supergran",
+		SUPERGRAN,
+		NO_TYPE,                  // type
+		ENGLISH,                  // subtype
+		FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+		85,  // Number of items
+		204, // Number of actions
+		105, // Number of words
+		39,  // Number of rooms
+		6,   // Max carried items
+		4,   // Word length
+		99,  // Number of messages
+
+		101, // number_of_verbs
+		106, // number_of_nouns;
+
+		0x236d,  // header
+		LATE,    // header style
+		0x38c8,  // room images
+		FOLLOWS, // item flags
+		FOLLOWS, // item images
+		0x399e,  // actions
+		COMPRESSED,
+		0x42fd,  // dictionary
+		0x4708,  // start_of_room_descriptions;
+		FOLLOWS, // start_of_room_connections;
+		FOLLOWS, // start_of_messages;
+		FOLLOWS, // start_of_item_descriptions;
+		FOLLOWS, // start_of_item_locations;
+
+		0x2423, // start_of_system_messages
+		0x25f3, // start of directions
+
+		0x5a4e,  // start_of_characters;
+		FOLLOWS, // start_of_image_data
+		0x634e,  // image_address_offset
+		47,      // number_of_pictures;
+		ZXOPT,   // palette
+		3,       // picture_format_version;
+		0),
+	GameInfo("Supergran C64",
+		SUPERGRAN_C64,
+		NO_TYPE,                             // type
+		static_cast<Subtype>(ENGLISH | C64), // subtype
+		FOUR_LETTER_UNCOMPRESSED,            // dictionary type
+
+		85,  // Number of items
+		204, // Number of actions
+		105, // Number of words
+		39,  // Number of rooms
+		6,   // Max carried items
+		4,   // Word length
+		99,  // Number of messages
+
+		101, // number_of_verbs
+		105, // number_of_nouns;
+
+		0x4624,               // header
+		SUPERGRAN_C64_HEADER, // header style
+		0x4636,               // room images
+		FOLLOWS,              // item flags
+		FOLLOWS,              // item images
+		0x47b5,               // actions
+		COMPRESSED,
+		0x5119,  // dictionary
+		0x5524,  // start_of_room_descriptions;
+		FOLLOWS, // start_of_room_connections;
+		FOLLOWS, // start_of_messages;
+		FOLLOWS, // start_of_item_descriptions;
+		0x470a,  // start_of_item_locations;
+
+		0x0a53, // start_of_system_messages
+		0x0a53, // start of directions
+
+		0x6a02,  // start_of_characters;
+		FOLLOWS, // start_of_image_data
+		0x7302,  // image_address_offset
+		49,      // number_of_pictures;
+		C64B,    // palette
+		3,       // picture_format_version;
+		0),
+	GameInfo("Robin of Sherwood",
+		ROBIN_OF_SHERWOOD,
+		SHERWOOD_VARIANT,       // type
+		ENGLISH,                // subtype
+		FOUR_LETTER_COMPRESSED, // dictionary type
+
+		87,  // Number of items
+		295, // Number of actions
+		114, // Number of words
+		93,  // Number of rooms
+		10,  // Max carried items
+		4,   // Word length
+		98,  // Number of messages
+
+		115, // number_of_verbs
+		109, // number_of_nouns;
+
+		0x3b5a, // header
+		LATE,   // header style
+
+		0,       // 0x3d99 room images, zero because it needs custom handling
+		0x3db8,  // item flags
+		FOLLOWS, // item images
+
+		0x409b, // actions
+		COMPRESSED,
+		0x4dc3, // dictionary
+		0,      // 0x9b53 start_of_room_descriptions, zero because of custom handling
+		0x3e67, // start_of_room_connections
+		0x5147, // start_of_messages
+		0x5d65, // start_of_item_descriptions
+		0x4d6b, // start_of_item_locations
+
+		0x250b, // start_of_system_messages
+		0x26b5, // start of directions
+
+		0x614f, // start_of_characters
+		0x66bf, // start_of_image_data
+		0x6765, // image_address_offset
+		83,     // number_of_pictures
+		ZXOPT,  // palette
+		4,      // picture_format_version
+		0),
+	GameInfo("Robin of Sherwood C64",
+		ROBIN_OF_SHERWOOD_C64,
+		SHERWOOD_VARIANT,                    // type
+		static_cast<Subtype>(ENGLISH | C64), // subtype
+		FOUR_LETTER_COMPRESSED,              // dictionary type
+
+		87,  // Number of items
+		296, // Number of actions
+		114, // Number of words
+		93,  // Number of rooms
+		10,  // Max carried items
+		4,   // Word length
+		98,  // Number of messages
+
+		115, // number_of_verbs
+		109, // number_of_nouns;
+
+		0x1f85,           // header
+		ROBIN_C64_HEADER, // header style
+
+		0,       // room images, zero because it needs custom handling
+		0x201c,  // item flags
+		FOLLOWS, // item images
+
+		0x252b, // actions
+		COMPRESSED,
+		0x320a, // dictionary
+		0,      // 0x9b53 start_of_room_descriptions, zero because of custom handling
+		0x20cc, // start_of_room_connections
+		0x358e, // start_of_messages
+		0x4215, // start_of_item_descriptions
+		0x1fa5, // start_of_item_locations
+
+		0x428, // start_of_system_messages
+		0x428, // start of directions
+
+		0x45ff, // start_of_characters
+		0x4b6f, // start_of_image_data
+		0x4c23, // image_address_offset
+		90,     // number_of_pictures
+		C64B,   // palette
+		4,      // picture_format_version
+		0),
+	GameInfo("Seas of Blood",
+		SEAS_OF_BLOOD,
+		SEAS_OF_BLOOD_VARIANT,  // type
+		ENGLISH,                // subtype
+		FOUR_LETTER_COMPRESSED, // dictionary type
+
+		125, // Number of items
+		344, // Number of actions
+		134, // Number of words
+		83,  // Number of rooms
+		10,  // Max carried items
+		4,   // Word length
+		99,  // Number of messages
+
+		69,  // number_of_verbs
+		134, // number_of_nouns;
+
+		0x494d, // header
+		LATE,   // header style
+
+		0,       // no room images
+		0x4961,  // item flags
+		FOLLOWS, // item images
+
+		FOLLOWS, // actions
+		COMPRESSED,
+		0x591b, // dictionary
+		0x67cb, // start_of_room_descriptions;
+		0x5c4b, // start_of_room_connections;
+		0x5ebb, // start_of_messages;
+		0x6ce0, // start_of_item_descriptions;
+		0x5e3d, // start_of_item_locations;
+
+		0x24ed, // start_of_system_messages
+		0x26fc, // start of directions
+
+		0x7389, // start_of_characters;
+		0x7a89, // start_of_image_data;
+		0x7b9f, // image_address_offset
+		139,    // number_of_pictures;
+		ZXOPT,  // palette
+		4,      // picture_format_version;
+		0),
+	GameInfo("Seas of Blood C64",
+		SEAS_OF_BLOOD_C64,
+		SEAS_OF_BLOOD_VARIANT,               // type
+		static_cast<Subtype>(ENGLISH | C64), // subtype
+		FOUR_LETTER_COMPRESSED,              // dictionary type
+
+		125, // Number of items
+		347, // Number of actions
+		134, // Number of words
+		82,  // Number of rooms
+		10,  // Max carried items
+		4,   // Word length
+		99,  // Number of messages
+
+		69,  // number_of_verbs
+		134, // number_of_nouns;
+
+		0x527d,                   // header
+		SEAS_OF_BLOOD_C64_HEADER, // header style
+
+		0,       // no room images
+		0x5a73,  // item flags
+		FOLLOWS, // item images
+
+		0x5b69, // actions
+		COMPRESSED,
+		0x6a2d, // dictionary
+		0x78dd, // start_of_room_descriptions;
+		0x6d5d, // start_of_room_connections;
+		0x6fcd, // start_of_messages;
+		0x7df2, // start_of_item_descriptions;
+		0x6f4f, // start_of_item_locations;
+
+		0x0b9d, // start_of_system_messages
+		0x0b9d, // start of directions
+
+		0x84a5, // start_of_characters;
+		0x8ba5, // start_of_image_data;
+		0x8CBB, // image_address_offset
+		139,    // number_of_pictures;
+		C64B,   // palette
+		4,      // picture_format_version;
+		0)
+};
+
+/* This is supposed to be the original ScottFree system
+ messages in second person, as far as possible */
+const char *g_sysDict[] = {
+	"North",
+	"South",
+	"East",
+	"West",
+	"Up",
+	"Down",
+	"The game is now over. Play again?",
+	"You have stored",
+	"treasures. ",
+	"On a scale of 0 to 100 that rates",
+	"O.K.",
+	"O.K.",
+	"O.K. ",
+	"Well done.\n",
+	"I don't understand ",
+	"You can't do that yet. ",
+	"Huh ? ",
+	"Give me a direction too. ",
+	"You haven't got it. ",
+	"You have it. ",
+	"You don't see it. ",
+	"It is beyond your power to do that. ",
+	"\nDangerous to move in the dark! ",
+	"You fell down and broke your neck. ",
+	"You can't go in that direction. ",
+	"I don't know how to \"",
+	"\" something. ",
+	"I don't know what a \"",
+	"\" is. ",
+	"You can't see. It is too dark!\n",
+	"You are in a ",
+	"\nYou can also see: ",
+	"Obvious exits: ",
+	"You are carrying:\n",
+	"Nothing.\n",
+	"Tell me what to do ? ",
+	"<HIT ENTER>",
+	"Light has run out. ",
+	"Light runs out in",
+	"turns! ",
+	"You are carrying too much. \n",
+	"You're dead. ",
+	"Resume a saved game? ",
+	"None",
+	"There's nothing here to take. ",
+	"You carry nothing. ",
+	"Your light is growing dim. ",
+	", ",
+	"\n",
+	" - ",
+	"What ?",
+	"yes",
+	"no",
+	"Answer yes or no.\n",
+	"Are you sure? ",
+	"Move undone. ",
+	"Can't undo on first turn. ",
+	"No more undo states stored. ",
+	"Saved. ",
+	"You can't use ALL with that verb. ",
+	"Transcript is now off.\n",
+	"Transcript is now on.\n",
+	"No transcript is currently running.\n",
+	"A transcript is already running.\n",
+	"Failed to create transcript file. ",
+	"Start of transcript\n\n",
+	"\n\nEnd of transcript\n",
+	"BAD DATA! Invalid save file.\n",
+	"State saved.\n",
+	"State restored.\n",
+	"No saved state exists.\n"
+};
+
+/* These are supposed to be the original ScottFree system
+ messages in first person, as far as possible */
+const char *g_sysDictIAm[] = {
+	"North",
+	"South",
+	"East",
+	"West",
+	"Up",
+	"Down",
+	"The game is now over. Play again?",
+	"You have stored",
+	"treasures. ",
+	"On a scale of 0 to 100 that rates",
+	"O.K.",
+	"O.K.",
+	"O.K. ",
+	"Well done.\n",
+	"I don't understand ",
+	"I can't do that yet. ",
+	"Huh ? ",
+	"Give me a direction too.",
+	"I'm not carrying it. ",
+	"I already have it. ",
+	"I don't see it here. ",
+	"It is beyond my power to do that. ",
+	"Dangerous to move in the dark! ",
+	"\nI fell and broke my neck.",
+	"I can't go in that direction. ",
+	"I don't know how to \"",
+	"\" something. ",
+	"I don't know what a \"",
+	"\" is. ",
+	"I can't see. It is too dark!\n",
+	"I'm in a ",
+	"\nI can also see: ",
+	"Obvious exits: ",
+	"I'm carrying: \n",
+	"Nothing.\n",
+	"Tell me what to do ? ",
+	"<HIT ENTER>",
+	"Light has run out. ",
+	"Light runs out in",
+	"turns! ",
+	"I've too much to carry. \n",
+	"I'm dead. ",
+	"Resume a saved game? ",
+	"None",
+	"There's nothing here to take. ",
+	"I have nothing to drop. ",
+	"My light is growing dim. ",
+	nullptr
+};
+
+/* These are supposed to be the original TI-99/4A system
+ messages in first person, as far as possible */
+const char *g_sysDictTI994A[] = {
+	"North",
+	"South",
+	"East",
+	"West",
+	"Up",
+	"Down",
+	"This adventure is over. Play again?",
+	"You have stored",
+	"treasures. ",
+	"On a scale of 0 to 100 that rates",
+	"OK. ",
+	"OK. ",
+	"OK. ",
+	"Well done.\n",
+	"I don't understand the command. ",
+	"I can't do that yet. ",
+	"Huh? ",
+	"Give me a direction too.",
+	"I'm not carrying it. ",
+	"I already have it. ",
+	"I don't see it here. ",
+	"It is beyond my power to do that. ",
+	"Dangerous to move in the dark!\n",
+	"\nI fell down and broke my neck.",
+	"I can't go in that direction. ",
+	"I don't know how to \"",
+	"\" something. ",
+	"I don't know what a \"",
+	"\" is. ",
+	"I can't see. It is too dark!\n",
+	"I am in a ",
+	"\nVisible items are : ",
+	"Obvious exits : ",
+	"I am carrying : ",
+	"Nothing. ",
+	"What shall I do? ",
+	"<HIT ENTER>",
+	"Light went out! ",
+	"Light runs out in",
+	"turns! ",
+	"I am carrying too much.\n",
+	"I'm dead... ",
+	"Resume a saved game? ",
+	"None",
+	"There's nothing here to take. ",
+	"I have nothing to drop. ",
+	"Light is growing dim ",
+	", ",
+	" ",
+	", ",
+	nullptr,
+};
+
+const char *g_sysDictZX[] = {
+	"NORTH",
+	"SOUTH",
+	"EAST",
+	"WEST",
+	"UP",
+	"DOWN",
+	"The Adventure is over. Want to try this Adventure again? ",
+	"I've stored",
+	"Treasures. ",
+	"On a scale of 0 to 100 that rates",
+	"Dropped.",
+	"Taken.",
+	"O.K. ",
+	"FANTASTIC! You've solved it ALL! \n",
+	"I must be stupid, but I just don't understand what you mean ",
+	"I can't do that...yet! ",
+	"Huh? ",
+	"I need a direction too. ",
+	"I'm not carrying it. ",
+	"I already have it. ",
+	"I don't see it here. ",
+	"It's beyond my Power to do that. ",
+	"It's dangerous to move in the dark! ",
+	"\nI fell and broke my neck! I'm DEAD! ",
+	"I can't go in that direction. ",
+	"I don't know how to \"",
+	"\" something. ",
+	"I don't know what a \"",
+	"\" is. ",
+	"It's too dark to see!\n",
+	"I am in a ",
+	". Visible items:\n",
+	"Exits: ",
+	"I'm carrying the following: ",
+	"Nothing at all. ",
+	"---TELL ME WHAT TO DO ? ",
+	"<HIT ENTER> ",
+	"Light has run out. ",
+	"Light runs out in",
+	"turns! ",
+	"I'm carrying too much! Try: TAKE INVENTORY. ",
+	"I'm DEAD!! ",
+	"Restore a previously saved game ? ",
+	"None",
+	"There's nothing here to take. ",
+	"I have nothing to drop. ",
+	"My light is growing dim. ",
+	" ",
+	" ",
+	". ",
+	"What ? ",
+	nullptr
+};
+
+} // End of namespace Scott
+} // End of namespace Glk
diff --git a/engines/glk/scott/game_info.h b/engines/glk/scott/game_info.h
new file mode 100644
index 00000000000..9c741aa6edd
--- /dev/null
+++ b/engines/glk/scott/game_info.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 GLK_SCOTT_GAMEINFO
+#define GLK_SCOTT_GAMEINFO
+
+#include "glk/scott/definitions.h"
+#include "common/array.h"
+
+namespace Glk {
+namespace Scott {
+
+extern Common::Array<GameInfo> g_games;
+extern const char *g_sysDict[];
+extern const char *g_sysDictIAm[];
+extern const char *g_sysDictZX[];
+extern const char *g_sysDictTI994A[];
+
+} // End of namespace Scott
+} // End of namespace Glk
+
+#endif


Commit: 855e9b1ae604071aaeef125bcc6d790d1059a0bd
    https://github.com/scummvm/scummvm/commit/855e9b1ae604071aaeef125bcc6d790d1059a0bd
Author: Avijeet (am388488 at gmail.com)
Date: 2022-04-12T20:35:24-07:00

Commit Message:
GLK: SCOTT: Various enums for game data.

Changed paths:
  A engines/glk/scott/definitions.cpp
  A engines/glk/scott/definitions.h


diff --git a/engines/glk/scott/definitions.cpp b/engines/glk/scott/definitions.cpp
new file mode 100644
index 00000000000..51fc3829307
--- /dev/null
+++ b/engines/glk/scott/definitions.cpp
@@ -0,0 +1,78 @@
+/* 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 "glk/scott/definitions.h"
+
+namespace Glk {
+namespace Scott {
+
+GameInfo::GameInfo() {}
+
+GameInfo::GameInfo(
+					Common::String title,
+					GameIDType gameID,
+					GameType type,
+					Subtype subType,
+					DictionaryType dictionary,
+					int numberOfItems,
+					int numberOfActions,
+					int numberOfWords,
+					int numberOfRooms,
+					int maxCarried,
+					int wordLength,
+					int numberOfMessages,
+					int numberOfVerbs,
+					int numberOfNouns,
+					int startOfHeader,
+					HeaderType headerStyle,
+					int startOfRoomImageList,
+					int startOfItemFlags,
+					int startOfItemImageList,
+					int startOfActions,
+					ActionTableType actionsStyle,
+					int startOfDictionary,
+					int startOfRoomDescriptions,
+					int startOfRoomConnections,
+					int startOfMessages,
+					int startOfItemDescriptions,
+					int startOfItemLocations,
+					int startOfSystemMessages,
+					int startOfDirections,
+					int startOfCharacters,
+					int startOfImageData,
+					int imageAddressOffset,
+					int numberOfPictures,
+					PaletteType palette,
+					int pictureFormatVersion,
+					int startOfIntroText)
+	: _title(title), _gameID(gameID), _type(type), _subType(subType), _dictionary(dictionary), _numberOfItems(numberOfItems),
+	  _numberOfActions(numberOfActions), _numberOfWords(numberOfWords), _numberOfRooms(numberOfRooms), _maxCarried(maxCarried),
+	  _wordLength(wordLength), _numberOfMessages(numberOfMessages), _numberOfVerbs(numberOfVerbs), _numberOfNouns(numberOfNouns),
+	  _startOfHeader(startOfHeader), _headerStyle(headerStyle), _startOfRoomImageList(startOfRoomImageList),
+	  _startOfItemFlags(startOfItemFlags), _startOfItemImageList(startOfItemImageList), _startOfActions(startOfActions),
+	  _actionsStyle(actionsStyle), _startOfDictionary(startOfDictionary), _startOfRoomDescriptions(startOfRoomDescriptions),
+	  _startOfRoomConnections(startOfRoomConnections), _startOfMessages(startOfMessages), _startOfItemDescriptions(startOfItemDescriptions),
+	  _startOfItemLocations(startOfItemLocations), _startOfSystemMessages(startOfSystemMessages), _startOfDirections(startOfDirections),
+	  _startOfCharacters(startOfCharacters), _startOfImageData(startOfImageData), _imageAddressOffset(imageAddressOffset),
+	  _numberOfPictures(numberOfPictures), _palette(palette), _pictureFormatVersion(pictureFormatVersion), _startOfIntroText(startOfIntroText) {}
+
+} // End of namespace Scott
+} // End of namespace Glk
diff --git a/engines/glk/scott/definitions.h b/engines/glk/scott/definitions.h
new file mode 100644
index 00000000000..9ee66c56c2c
--- /dev/null
+++ b/engines/glk/scott/definitions.h
@@ -0,0 +1,309 @@
+/* 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 GLK_SCOTT_DEFINITIONS
+#define GLK_SCOTT_DEFINITIONS
+
+#include "common/str.h"
+
+namespace Glk {
+namespace Scott {
+
+const int FOLLOWS = 0xFFFF;
+
+const int GO = 1;
+const int TAKE = 10;
+const int DROP = 18;
+
+const int LASTALL = 128;
+
+enum GameIDType {
+	UNKNOWN_GAME,
+	SCOTTFREE,
+	TI994A,
+	PIRATE,
+	VOODOO,
+	STRANGE,
+	BANZAI,
+	BATON,
+	BATON_C64,
+	TIME_MACHINE,
+	TIME_MACHINE_C64,
+	ARROW1,
+	ARROW1_C64,
+	ARROW2,
+	ARROW2_C64,
+	PULSAR7,
+	PULSAR7_C64,
+	CIRCUS,
+	CIRCUS_C64,
+	FEASIBILITY,
+	FEASIBILITY_C64,
+	AKYRZ,
+	AKYRZ_C64,
+	PERSEUS,
+	PERSEUS_C64,
+	PERSEUS_ITALIAN,
+	INDIANS,
+	INDIANS_C64,
+	WAXWORKS,
+	WAXWORKS_C64,
+	HULK,
+	HULK_C64,
+	ADVENTURELAND,
+	ADVENTURELAND_C64,
+	SECRET_MISSION,
+	SECRET_MISSION_C64,
+	CLAYMORGUE,
+	CLAYMORGUE_C64,
+	SPIDERMAN,
+	SPIDERMAN_C64,
+	SAVAGE_ISLAND,
+	SAVAGE_ISLAND_C64,
+	SAVAGE_ISLAND2,
+	SAVAGE_ISLAND2_C64,
+	GREMLINS,
+	GREMLINS_ALT,
+	GREMLINS_C64,
+	GREMLINS_GERMAN,
+	GREMLINS_GERMAN_C64,
+	GREMLINS_SPANISH,
+	SUPERGRAN,
+	SUPERGRAN_C64,
+	ROBIN_OF_SHERWOOD,
+	ROBIN_OF_SHERWOOD_C64,
+	SEAS_OF_BLOOD,
+	SEAS_OF_BLOOD_C64,
+	NUMGAMES
+};
+
+enum ExplicitResultType {
+	ER_NO_RESULT,
+	ER_SUCCESS = 0,
+	ER_RAN_ALL_LINES_NO_MATCH = -1,
+	ER_RAN_ALL_LINES = -2
+};
+
+enum ActionResultType {
+	ACT_SUCCESS = 0,
+	ACT_FAILURE = 1,
+	ACT_CONTINUE,
+	ACT_GAMEOVER
+};
+
+enum SysMessageType {
+	NORTH,
+	SOUTH,
+	EAST,
+	WEST,
+	UP,
+	DOWN,
+	PLAY_AGAIN,
+	IVE_STORED,
+	TREASURES,
+	ON_A_SCALE_THAT_RATES,
+	DROPPED,
+	TAKEN,
+	OK,
+	YOUVE_SOLVED_IT,
+	I_DONT_UNDERSTAND,
+	YOU_CANT_DO_THAT_YET,
+	HUH,
+	DIRECTION,
+	YOU_HAVENT_GOT_IT,
+	YOU_HAVE_IT,
+	YOU_DONT_SEE_IT,
+	THATS_BEYOND_MY_POWER,
+	DANGEROUS_TO_MOVE_IN_DARK,
+	YOU_FELL_AND_BROKE_YOUR_NECK,
+	YOU_CANT_GO_THAT_WAY,
+	I_DONT_KNOW_HOW_TO,
+	SOMETHING,
+	I_DONT_KNOW_WHAT_A,
+	IS,
+	TOO_DARK_TO_SEE,
+	YOU_ARE,
+	YOU_SEE,
+	EXITS,
+	INVENTORY,
+	NOTHING,
+	WHAT_NOW,
+	HIT_ENTER,
+	LIGHT_HAS_RUN_OUT,
+	LIGHT_RUNS_OUT_IN,
+	TURNS,
+	YOURE_CARRYING_TOO_MUCH,
+	IM_DEAD,
+	RESUME_A_SAVED_GAME,
+	NONE,
+	NOTHING_HERE_TO_TAKE,
+	YOU_HAVE_NOTHING,
+	LIGHT_GROWING_DIM,
+	EXITS_DELIMITER,
+	MESSAGE_DELIMITER,
+	ITEM_DELIMITER,
+	WHAT,
+	YES,
+	NO,
+	ANSWER_YES_OR_NO,
+	ARE_YOU_SURE,
+	MOVE_UNDONE,
+	CANT_UNDO_ON_FIRST_TURN,
+	NO_UNDO_STATES,
+	SAVED,
+	CANT_USE_ALL,
+	TRANSCRIPT_OFF,
+	TRANSCRIPT_ON,
+	NO_TRANSCRIPT,
+	TRANSCRIPT_ALREADY,
+	FAILED_TRANSCRIPT,
+	TRANSCRIPT_START,
+	TRANSCRIPT_END,
+	BAD_DATA,
+	STATE_SAVED,
+	STATE_RESTORED,
+	NO_SAVED_STATE,
+	LAST_SYSTEM_MESSAGE
+};
+
+const SysMessageType MAX_SYSMESS = LAST_SYSTEM_MESSAGE;
+
+enum DictionaryType {
+	NOT_A_GAME,
+	FOUR_LETTER_UNCOMPRESSED,
+	THREE_LETTER_UNCOMPRESSED,
+	FIVE_LETTER_UNCOMPRESSED,
+	FOUR_LETTER_COMPRESSED,
+	FIVE_LETTER_COMPRESSED,
+	GERMAN,
+	SPANISH,
+	ITALIAN
+};
+
+enum GameType {
+	NO_TYPE,
+	GREMLINS_VARIANT,
+	SHERWOOD_VARIANT,
+	SAVAGE_ISLAND_VARIANT,
+	SECRET_MISSION_VARIANT,
+	SEAS_OF_BLOOD_VARIANT,
+	OLD_STYLE,
+};
+
+enum Subtype {
+	ENGLISH = 0x1,
+	MYSTERIOUS = 0x2,
+	LOCALIZED = 0x4,
+	C64 = 0x8
+};
+
+enum PaletteType {
+	NO_PALETTE,
+	ZX,
+	ZXOPT,
+	C64A,
+	C64B,
+	VGA
+};
+
+enum HeaderType {
+	NO_HEADER,
+	EARLY,
+	LATE,
+	HULK_HEADER,
+	GREMLINS_C64_HEADER,
+	ROBIN_C64_HEADER,
+	SUPERGRAN_C64_HEADER,
+	SEAS_OF_BLOOD_C64_HEADER,
+	MYSTERIOUS_C64_HEADER,
+	ARROW_OF_DEATH_PT_2_C64_HEADER,
+	INDIANS_C64_HEADER
+};
+
+enum ActionTableType {
+	UNKNOWN_ACTIONS_TYPE,
+	COMPRESSED,
+	UNCOMPRESSED,
+	HULK_ACTIONS
+};
+
+struct GameInfo {
+	GameInfo();
+	GameInfo(Common::String title, GameIDType gameID, GameType type, Subtype subType, DictionaryType dictionary,
+			 int numberOfItems, int numberOfActions, int numberOfWords, int numberOfRooms, int maxCarried,
+			 int wordLength, int numberOfMessages, int numberOfVerbs, int numberOfNouns, int startOfHeader,
+			 HeaderType headerStyle, int startOfRoomImageList, int startOfItemFlags, int startOfItemImageList,
+			 int startOfActions, ActionTableType actionsStyle, int startOfDictionary, int startOfRoomDescriptions,
+			 int startOfRoomConnections, int startOfMessages, int startOfItemDescriptions, int startOfItemLocations,
+			 int startOfSystemMessages, int startOfDirections, int startOfCharacters, int startOfImageData,
+			 int imageAddressOffset, int numberOfPictures, PaletteType palette, int pictureFormatVersion,
+			 int startOfIntroText);
+	Common::String _title;
+
+	GameIDType _gameID = UNKNOWN_GAME;
+	GameType _type = NO_TYPE;
+	Subtype _subType = ENGLISH;
+	DictionaryType _dictionary = NOT_A_GAME;
+
+	int _numberOfItems = 0;
+	int _numberOfActions = 0;
+	int _numberOfWords = 0;
+	int _numberOfRooms = 0;
+	int _maxCarried = 0;
+	int _wordLength = 0;
+	int _numberOfMessages = 0;
+
+	int _numberOfVerbs = 0;
+	int _numberOfNouns = 0;
+
+	int _startOfHeader = 0;
+	HeaderType _headerStyle = NO_HEADER;
+
+	int _startOfRoomImageList = 0;
+	int _startOfItemFlags = 0;
+	int _startOfItemImageList = 0;
+
+	int _startOfActions = 0;
+	ActionTableType _actionsStyle = UNKNOWN_ACTIONS_TYPE;
+	int _startOfDictionary = 0;
+	int _startOfRoomDescriptions = 0;
+	int _startOfRoomConnections = 0;
+	int _startOfMessages = 0;
+	int _startOfItemDescriptions = 0;
+	int _startOfItemLocations = 0;
+
+	int _startOfSystemMessages = 0;
+	int _startOfDirections = 0;
+
+	int _startOfCharacters = 0;
+	int _startOfImageData = 0;
+	int _imageAddressOffset = 0; /* This is the difference between the value given by
+							   the image data lookup table and a usable file
+							   offset */
+	int _numberOfPictures = 0;
+	PaletteType _palette = NO_PALETTE;
+	int _pictureFormatVersion = 0;
+	int _startOfIntroText = 0;
+};
+
+} // End of namespace Scott
+} // End of namespace Glk
+#endif /* definitions_h */


Commit: 9cae726fcaa2ff630e87c1367162006dd5ca73fa
    https://github.com/scummvm/scummvm/commit/9cae726fcaa2ff630e87c1367162006dd5ca73fa
Author: Avijeet (am388488 at gmail.com)
Date: 2022-04-12T20:35:24-07:00

Commit Message:
GLK: SCOTT: Support .z80 files.

Changed paths:
    engines/glk/scott/detection.cpp


diff --git a/engines/glk/scott/detection.cpp b/engines/glk/scott/detection.cpp
index 3efc4dbdbc9..d936d316b64 100644
--- a/engines/glk/scott/detection.cpp
+++ b/engines/glk/scott/detection.cpp
@@ -44,7 +44,7 @@ GameDescriptor ScottMetaEngine::findGame(const char *gameId) {
 }
 
 bool ScottMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &gameList) {
-	const char *const EXTENSIONS[] = { ".saga", ".dat", nullptr };
+	const char *const EXTENSIONS[] = { ".z80", ".saga", ".dat", nullptr };
 
 	// Loop through the files of the folder
 	for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {


Commit: f994bd285906037cbacba9af68a09c01572b7b17
    https://github.com/scummvm/scummvm/commit/f994bd285906037cbacba9af68a09c01572b7b17
Author: Avijeet (am388488 at gmail.com)
Date: 2022-04-12T20:35:24-07:00

Commit Message:
GLK: SCOTT: Add detection for 11 Mysterious Adventures.

Changed paths:
    engines/glk/scott/detection_tables.h


diff --git a/engines/glk/scott/detection_tables.h b/engines/glk/scott/detection_tables.h
index 0f26b55bb92..eb682232955 100644
--- a/engines/glk/scott/detection_tables.h
+++ b/engines/glk/scott/detection_tables.h
@@ -147,6 +147,19 @@ const GlkDetectionEntry SCOTT_GAMES[] = {
 	DT_ENTRY0("romulanadv",       "d97b5cb5ed66eb276ef9f1c1bae0b8dd", 13959),
 	DT_ENTRY0("topsecretadv",     "effb411e74dfe3a8d69b57b9bc3a2cef", 15575),
 
+	//ZX Spectrum games
+	//11 Mysterious Adventures
+	DT_ENTRY1("goldenbaton", "ZXSpectrum", "cb7dadc9d5f8bce453b9139265e4dd7d", 32060),
+	DT_ENTRY1("timemachine", "ZXSpectrum", "b22d1f4d46c99ff4443d541d3fe424c1", 30928),
+	DT_ENTRY1("arrowofdeath1", "ZXSpectrum", "3a5c3f4079c1c0347f03420db8ad4596", 34105),
+	DT_ENTRY1("arrowofdeath2", "ZXSpectrum", "d3f8943c4f5f71ce00139065055a72ee", 38043),
+	DT_ENTRY1("pulsar7", "ZXSpectrum", "441edd90fc7f9ff39a5eebe035a974e9", 29961),
+	DT_ENTRY1("circus", "ZXSpectrum", "ed99306a2fb23bf6579068a4d74034ee", 27746),
+	DT_ENTRY1("feasibility", "ZXSpectrum", "5e381e83f15d77e3542be4a4cffc8e25", 37395),
+	DT_ENTRY1("akyrz", "ZXSpectrum", "b0f8676817475753f1edd7f1eeea31fb", 33753),
+	DT_ENTRY1("perseus", "ZXSpectrum", "84d5fbb16a37e495abf09d191fd8b1a2", 31504),
+	DT_ENTRY1("10indians", "ZXSpectrum", "afde056c152de79ea20453c42a2d08af", 31664),
+	DT_ENTRY1("waxworks11", "ZXSpectrum", "6c6fbbbb50032463a6ea71c6750ea1f5", 32662),
 	DT_END_MARKER
 };
 


Commit: faa4ae472dfee8bd537c5a85fcd0e11e2950376c
    https://github.com/scummvm/scummvm/commit/faa4ae472dfee8bd537c5a85fcd0e11e2950376c
Author: Avijeet (am388488 at gmail.com)
Date: 2022-04-12T20:35:24-07:00

Commit Message:
GLK: SCOTT: Class for holding global data.

Changed paths:
  A engines/glk/scott/globals.cpp
  A engines/glk/scott/globals.h


diff --git a/engines/glk/scott/globals.cpp b/engines/glk/scott/globals.cpp
new file mode 100644
index 00000000000..22df5b1659c
--- /dev/null
+++ b/engines/glk/scott/globals.cpp
@@ -0,0 +1,74 @@
+/* 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 "glk/scott/globals.h"
+#include "glk/scott/scott.h"
+#include "glk/scott/command_parser.h"
+#include "glk/scott/line_drawing.h"
+#include "glk/scott/saga_draw.h"
+#include "common/algorithm.h"
+
+namespace Glk {
+namespace Scott {
+
+Globals *g_globals;
+
+Globals::Globals() : _sys(MAX_SYSMESS), _directions(NUMBER_OF_DIRECTIONS), _extraNouns(NUMBER_OF_EXTRA_NOUNS),
+					 _skipList(NUMBER_OF_SKIPPABLE_WORDS), _delimiterList(NUMBER_OF_DELIMITERS), _systemMessages(60),
+					 _vectorState(NO_VECTOR_IMAGE) {
+
+	g_globals = this;
+
+	_gameHeader = new Header;
+
+	_englishDirections = {nullptr, "north", "south", "east", "west", "up", "down", "n", "s", "e", "w", "u", "d", " "};
+
+	_extraCommands = {nullptr, "restart", "save", "restore", "load", "transcript", "script", "oops", "undo", "ram",
+					  "ramload", "ramrestore", "ramsave", "except", "but", " ", " ", " ", " ", " "};
+
+	_extraCommandsKey = {NO_COMMAND, RESTART, SAVE, RESTORE, RESTORE, SCRIPT, SCRIPT, UNDO, UNDO, RAM,
+						 RAMLOAD, RAMLOAD, RAMSAVE, EXCEPT, EXCEPT, RESTORE, RESTORE, SCRIPT, UNDO, RESTART};
+
+	_englishExtraNouns = {nullptr, "game", "story", "on", "off", "load", "restore", "save", "move",
+						  "command", "turn", "all", "everything", "it", " ", " "};
+
+	_extraNounsKey = {NO_COMMAND, GAME, GAME, ON, OFF, RAMLOAD, RAMLOAD, RAMSAVE, COMMAND, COMMAND, COMMAND,
+					  ALL, ALL, IT, ON, OFF};
+
+	_abbreviations = {nullptr, "i", "l", "x", "z", "q"};
+
+	_abbreviationsKey = {nullptr, "inventory", "look", "examine", "wait", "quit"};
+
+	_englishSkipList = {nullptr, "at", "to", "in", "into", "the", "a", "an", "my", "quickly",
+						"carefully", "quietly", "slowly", "violently", "fast", "hard", "now", "room"};
+
+	_englishDelimiterList = {nullptr, ",", "and", "then", " "};
+
+	Common::fill(&_counters[0], &_counters[16], 0);
+	Common::fill(&_roomSaved[0], &_roomSaved[16], 0);
+}
+
+Globals::~Globals() {
+	delete _gameHeader;
+}
+
+} // End of namespace Scott
+} // End of namespace Glk
diff --git a/engines/glk/scott/globals.h b/engines/glk/scott/globals.h
new file mode 100644
index 00000000000..7a6fe86073c
--- /dev/null
+++ b/engines/glk/scott/globals.h
@@ -0,0 +1,162 @@
+/* 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 GLK_SCOTT_GLOBALS
+#define GLK_SCOTT_GLOBALS
+
+#include "glk/glk_types.h"
+#include "glk/windows.h"
+#include "glk/scott/definitions.h"
+#include "common/array.h"
+#include "common/str-array.h"
+#include <cstdint>
+
+namespace Glk {
+namespace Scott {
+
+struct Command;
+enum ExtraCommand : int;
+struct LineImage;
+enum VectorStateType : int;
+
+struct PixelToDraw;
+struct Image;
+
+typedef uint8_t RGB[3];
+typedef RGB PALETTE[16];
+
+struct Header;
+struct Item;
+struct Room;
+struct Action;
+
+struct SavedState;
+
+class Globals {
+public:
+	// scott
+	Header *_gameHeader;
+	Common::Array<Item> _items;
+	Common::Array<Room> _rooms;
+	Common::StringArray _verbs;
+	Common::StringArray _nouns;
+	Common::StringArray _messages;
+	Common::Array<Action> _actions;
+	Common::StringArray _sys;
+	Common::StringArray _systemMessages;
+	GameInfo *_game = nullptr;
+	winid_t _graphics = nullptr;
+	uint8_t *_entireFile = nullptr;
+	size_t _fileLength = 0;
+	strid_t _roomDescriptionStream = nullptr;
+	int _fileBaselineOffset = 0;
+	int _header[24];
+	int _lightRefill = 0;
+	winid_t _bottomWindow = nullptr, _topWindow = nullptr;
+	Command *_currentCommand = nullptr;
+	int _justStarted = 1;
+	int _shouldRestart = 0;
+	int _stopTime = 0;
+	strid_t _transcript = nullptr;
+	int _counters[16]; ///< Range unknown
+	int _currentCounter = 0;
+	int _savedRoom = 0;
+	int _roomSaved[16]; ///< Range unknown
+	uint32 _bitFlags = 0; ///< Might be >32 flags - I haven't seen >32 yet
+	int _autoInventory = 0;
+
+	// sagadraw
+	int _drawToBuffer = 0;
+	uint8_t _sprite[256][8];
+	uint8_t _screenchars[768][8];
+	uint8_t _buffer[384][9];
+	Common::Array<Image> _images;
+	int _pixelSize = 0;
+	int _xOffset = 0;
+	PALETTE _pal;
+	int _whiteColour = 15;
+	int _blueColour = 9;
+	glui32 _diceColour = 0xFF0000;
+	int32_t _errorCount = 0;
+	PaletteType _palChosen = NO_PALETTE;
+	size_t _hulkCoordinates = 0x26DB;
+	size_t _hulkItemImageOffsets = 0x2798;
+	size_t _hulkLookImageOffsets = 0x27BC;
+	size_t _hulkSpecialImageOffsets = 0x276E;
+	size_t _hulkImageOffset = 0x441B;
+
+	// line_drawing
+	Common::Array<LineImage> _lineImages;
+	VectorStateType _vectorState;
+	PixelToDraw **_pixelsToDraw = nullptr;
+	int _totalDrawInstructions = 0;
+	int _currentDrawInstruction = 0;
+	int _vectorImageShown = -1;
+	uint8_t *_pictureBitmap = nullptr;
+	int _lineColour = 15;
+	int _bgColour = 0;
+	int _scottGraphicsWidth = 255;
+	int _scottGraphicsHeight = 94;
+
+	// connect
+	int _gliSlowDraw = 0;
+
+	// parser
+	int _lastNoun = 0;
+	glui32 *_firstErrorMessage = nullptr;
+	glui32 **_unicodeWords = nullptr;
+	char **_charWords = nullptr;
+	int _wordsInInput = 0;
+	Common::StringArray _directions;
+	Common::StringArray _englishDirections;
+	Common::StringArray _skipList;
+	Common::StringArray _englishSkipList;
+	Common::StringArray _delimiterList;
+	Common::StringArray _englishDelimiterList;
+	Common::StringArray _extraCommands;
+	Common::StringArray _extraNouns;
+	Common::StringArray _englishExtraNouns;
+	Common::Array<ExtraCommand> _extraNounsKey;
+	Common::Array<ExtraCommand> _extraCommandsKey;
+	Common::StringArray _abbreviations;
+	Common::StringArray _abbreviationsKey;
+
+	//restore state
+	int _justUndid = 0;
+	SavedState *_initialState = nullptr;
+	SavedState *_ramSave = nullptr;
+	SavedState *_lastUndo = nullptr;
+	SavedState *_oldestUndo = nullptr;
+	int _numberOfUndos = 0;
+
+public:
+	Globals();
+	~Globals();
+};
+
+extern Globals *g_globals;
+
+#define _G(FIELD) (g_globals->FIELD)
+
+} // End of namespace Scott
+} // End of namespace Glk
+
+#endif


Commit: 7f84f25804f70fe135f4df2a8bcea8c39a84ff57
    https://github.com/scummvm/scummvm/commit/7f84f25804f70fe135f4df2a8bcea8c39a84ff57
Author: Avijeet (am388488 at gmail.com)
Date: 2022-04-12T20:35:24-07:00

Commit Message:
GLK: SCOTT: Add Questprobe: The Hulk specific functions.

Changed paths:
  A engines/glk/scott/hulk.cpp
  A engines/glk/scott/hulk.h


diff --git a/engines/glk/scott/hulk.cpp b/engines/glk/scott/hulk.cpp
new file mode 100644
index 00000000000..580b463e353
--- /dev/null
+++ b/engines/glk/scott/hulk.cpp
@@ -0,0 +1,409 @@
+/* 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 "glk/scott/hulk.h"
+#include "glk/scott/definitions.h"
+#include "glk/scott/detect_game.h"
+#include "glk/scott/globals.h"
+#include "glk/scott/scott.h"
+
+namespace Glk {
+namespace Scott {
+
+void hulkShowImageOnExamine(int noun) {
+	int image = 0;
+	switch (noun) {
+	case 55: // Dome
+		if (_G(_items)[11]._location == MY_LOC)
+			image = 28;
+		break;
+	case 108: // Natter energy egg
+		if (_G(_items)[17]._location == MY_LOC || _G(_items)[17]._location == CARRIED)
+			image = 30;
+		break;
+	case 124: // Bio-Gem
+	case 41:
+		if (_G(_items)[18]._location == MY_LOC || _G(_items)[18]._location == CARRIED)
+			image = 29;
+		break;
+	case 21: // Killer Bees
+		if (_G(_items)[24]._location == MY_LOC)
+			image = 31;
+		break;
+	case 83: // Iron ring
+		if (_G(_items)[33]._location == MY_LOC)
+			image = 32;
+		break;
+	case 121: // Cage
+		if (_G(_items)[47]._location == MY_LOC)
+			image = 33;
+		break;
+	default:
+		break;
+	}
+	if (image) {
+		g_scott->drawImage(image);
+		g_scott->output(_G(_sys)[HIT_ENTER]);
+		g_scott->hitEnter();
+	}
+}
+
+void hulkLook() {
+	g_scott->drawImage(_G(_rooms)[MY_LOC]._image);
+	for (int ct = 0; ct <= _G(_gameHeader)->_numItems; ct++) {
+		int image = _G(_items)[ct]._image;
+		if (_G(_items)[ct]._location == MY_LOC && image != 255) {
+			/* Don't draw bio gem in fuzzy area */
+			if ((ct == 18 && MY_LOC != 15) ||
+				/* Don't draw Dr. Strange until outlet is plugged */
+				(ct == 26 && _G(_items)[28]._location != MY_LOC))
+				continue;
+			g_scott->drawImage(image);
+		}
+	}
+}
+
+void drawHulkImage(int p) {
+	int image = 0;
+	switch (p) {
+	case 85:
+		image = 34;
+		break;
+	case 86:
+		image = 35;
+		break;
+	case 83:
+		image = 36;
+		break;
+	case 84:
+		image = 37;
+		break;
+	case 87:
+		image = 38;
+		break;
+	case 88:
+		image = 39;
+		break;
+	case 89:
+		image = 40;
+		break;
+	case 82:
+		image = 41;
+		break;
+	case 81:
+		image = 42;
+		break;
+	default:
+		error("Unhandled image number %d!\n", p);
+		break;
+	}
+
+	if (image != 0) {
+		g_scott->drawImage(image);
+		g_scott->output(_G(_sys)[HIT_ENTER]);
+		g_scott->hitEnter();
+	}
+}
+
+uint8_t *readHulkDictionary(GameInfo info, uint8_t **pointer) {
+	uint8_t *ptr = *pointer;
+	char *dictword = new char[info._wordLength + 2];
+	char c = 0;
+	int wordnum = 0;
+	int charindex = 0;
+
+	int nv = info._numberOfVerbs;
+	int nn = info._numberOfNouns;
+
+	for (int i = 0; i < nn - nv; i++)
+		_G(_verbs)[nv + i] = ".\0";
+
+	for (int i = 0; i < nv - nn; i++)
+		_G(_nouns)[nn + i] = ".\0";
+
+	do {
+		for (int i = 0; i < info._wordLength; i++) {
+			c = *(ptr++);
+			if (c == 0) {
+				if (charindex == 0) {
+					c = *(ptr++);
+				}
+			}
+			dictword[charindex] = c;
+			if (c == '*')
+				i--;
+			charindex++;
+
+			dictword[charindex] = 0;
+		}
+
+		if (wordnum < nn) {
+			_G(_nouns)[wordnum] = Common::String(dictword, charindex + 1);
+		} else {
+			_G(_verbs)[wordnum - nn] = Common::String(dictword, charindex + 1);
+		}
+		wordnum++;
+
+		if (c != 0 && !isascii(c))
+			return ptr;
+
+		charindex = 0;
+	} while (wordnum <= nv + nn);
+
+	delete[] dictword;
+	return ptr;
+}
+
+int tryLoadingHulk(GameInfo info, int dictStart) {
+	int ni, na, nw, nr, mc, pr, tr, wl, lt, mn, trm;
+	int ct;
+
+	Action *ap;
+	Room *rp;
+	Item *ip;
+
+	/* Load the header */
+	uint8_t *ptr = _G(_entireFile);
+
+	_G(_fileBaselineOffset) = dictStart - info._startOfDictionary - 645;
+
+	int offset = info._startOfHeader + _G(_fileBaselineOffset);
+	ptr = seekToPos(_G(_entireFile), offset);
+
+	if (ptr == 0)
+		return 0;
+
+	readHeader(ptr);
+
+	parseHeader(_G(_header), info._headerStyle, &ni, &na, &nw, &nr, &mc, &pr, &tr, &wl, &lt, &mn, &trm);
+
+	_G(_gameHeader)->_numItems = ni;
+	_G(_items).resize(ni + 1);
+	_G(_gameHeader)->_numActions = na;
+	_G(_actions).resize(na + 1);
+	_G(_gameHeader)->_numWords = nw;
+	_G(_gameHeader)->_wordLength = wl;
+	_G(_verbs).resize(nw + 1);
+	_G(_nouns).resize(nw + 1);
+	_G(_gameHeader)->_numRooms = nr;
+	_G(_rooms).resize(nr + 1);
+	_G(_gameHeader)->_maxCarry = mc;
+	_G(_gameHeader)->_playerRoom = pr;
+	_G(_gameHeader)->_treasures = tr;
+	_G(_gameHeader)->_lightTime = lt;
+	_G(_lightRefill) = lt;
+	_G(_gameHeader)->_numMessages = mn;
+	_G(_messages).resize(mn + 1);
+	_G(_gameHeader)->_treasureRoom = trm;
+
+	if (_G(_header)[0] != info._wordLength || _G(_header)[1] != info._numberOfWords || _G(_header)[2] != info._numberOfActions || _G(_header)[3] != info._numberOfItems || _G(_header)[4] != info._numberOfMessages || _G(_header)[5] != info._numberOfRooms || _G(_header)[6] != info._maxCarried) {
+		return 0;
+	}
+
+#pragma mark Dictionary
+
+	if (seekIfNeeded(info._startOfDictionary, &offset, &ptr) == 0)
+		return 0;
+
+	readHulkDictionary(info, &ptr);
+
+#pragma mark Rooms
+
+	if (seekIfNeeded(info._startOfRoomDescriptions, &offset, &ptr) == 0)
+		return 0;
+
+	ct = 0;
+
+	uint8_t string_length = 0;
+	do {
+		rp = &_G(_rooms)[ct];
+		string_length = *(ptr++);
+		if (string_length == 0) {
+			rp->_text = ".\0";
+		} else {
+			for (int i = 0; i < string_length; i++) {
+				rp->_text += *(ptr++);
+			}
+		}
+		ct++;
+	} while (ct < nr + 1);
+
+#pragma mark Messages
+
+	ct = 0;
+	const char *string;
+
+	do {
+		string_length = *(ptr++);
+		if (string_length == 0) {
+			string = ".\0";
+			_G(_messages)[ct] = string;
+		} else {
+			char *s = new char[string_length + 1];
+			for (int i = 0; i < string_length; i++) {
+				s[i] = *(ptr++);
+			}
+			s[string_length] = 0;
+			_G(_messages)[ct] = s;
+		}
+		ct++;
+	} while (ct < mn + 1);
+
+#pragma mark Items
+
+	if (seekIfNeeded(info._startOfItemDescriptions, &offset, &ptr) == 0)
+		return 0;
+
+	ct = 0;
+
+	do {
+		ip = &_G(_items)[ct];
+		string_length = *(ptr++);
+		if (string_length == 0) {
+			ip->_text = ".\0";
+		} else {
+			for (int i = 0; i < string_length; i++) {
+				ip->_text += *(ptr++);
+			}
+			const char *p = strchr(ip->_text.c_str(), '/');
+			if (p) {
+				ip->_autoGet = Common::String(p);
+
+				/* Some games use // to mean no auto get/drop word! */
+				if (!(ip->_autoGet == "//") && !(ip->_autoGet == "/*")) {
+					ip->_text = Common::String(ip->_text.c_str(), p);
+					ip->_autoGet.deleteChar(0);
+
+					const char *t = strchr(ip->_autoGet.c_str(), '/');
+					if (t) {
+						ip->_autoGet = Common::String(ip->_autoGet.c_str(), t);
+					}
+					ptr++;
+				}
+			}
+		}
+
+		ct++;
+	} while (ct < ni + 1);
+
+#pragma mark Room connections
+
+	if (seekIfNeeded(info._startOfRoomConnections, &offset, &ptr) == 0)
+		return 0;
+
+	/* The room connections are ordered by direction, not room, so all the North
+	 * connections for all the rooms come first, then the South connections, and
+	 * so on. */
+	for (int j = 0; j < 6; j++) {
+		ct = 0;
+
+		while (ct < nr + 1) {
+			rp = &_G(_rooms)[ct];
+			rp->_exits[j] = *(ptr++);
+			ptr++;
+			ct++;
+		}
+	}
+
+#pragma mark item locations
+
+	if (seekIfNeeded(info._startOfItemLocations, &offset, &ptr) == 0)
+		return 0;
+
+	ct = 0;
+	while (ct < ni + 1) {
+		ip = &_G(_items)[ct];
+		ip->_location = *(ptr++);
+		ip->_location += *(ptr++) * 0x100;
+		ip->_initialLoc = ip->_location;
+		ct++;
+	}
+
+#pragma mark room images
+
+	if (seekIfNeeded(info._startOfRoomImageList, &offset, &ptr) == 0)
+		return 0;
+
+	for (ct = 0; ct <= _G(_gameHeader)->_numRooms; ct++) {
+		rp = &_G(_rooms)[ct];
+		rp->_image = *(ptr++);
+	}
+
+#pragma mark item images
+
+	if (seekIfNeeded(info._startOfItemImageList, &offset, &ptr) == 0)
+		return 0;
+
+	for (ct = 0; ct <= _G(_gameHeader)->_numItems; ct++) {
+		ip = &_G(_items)[ct];
+		ip->_image = 255;
+	}
+
+	int index, image = 10;
+
+	for (index = (*ptr++); index != 255; index = (*ptr++)) {
+		_G(_items)[index]._image = image++;
+	}
+
+#pragma mark item flags
+
+	/* Hulk does not seem to use item flags */
+
+#pragma mark Actions
+
+	if (seekIfNeeded(info._startOfActions, &offset, &ptr) == 0)
+		return 0;
+
+	ct = 0;
+
+	int verb, noun, value, value2, plus;
+	while (ct < na + 1) {
+		ap = &_G(_actions)[ct];
+		plus = na + 1;
+		verb = _G(_entireFile)[offset + ct];
+		noun = _G(_entireFile)[offset + ct + plus];
+
+		ap->_vocab = verb * 150 + noun;
+
+		for (int j = 0; j < 2; j++) {
+			plus += na + 1;
+			value = _G(_entireFile)[offset + ct + plus];
+			plus += na + 1;
+			value2 = _G(_entireFile)[offset + ct + plus];
+			ap->_action[j] = 150 * value + value2;
+		}
+
+		int offset2 = offset + 0x624;
+		plus = 0;
+
+		for (int j = 0; j < 5; j++) {
+			value = _G(_entireFile)[offset2 + ct * 2 + plus];
+			value2 = _G(_entireFile)[offset2 + ct * 2 + plus + 1];
+			ap->_condition[j] = value + value2 * 0x100;
+			plus += (na + 1) * 2;
+		}
+		ct++;
+	}
+	return 1;
+}
+
+} // End of namespace Scott
+} // End of namespace Glk
diff --git a/engines/glk/scott/hulk.h b/engines/glk/scott/hulk.h
new file mode 100644
index 00000000000..f69f2cb8398
--- /dev/null
+++ b/engines/glk/scott/hulk.h
@@ -0,0 +1,38 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 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 GLK_SCOTT_HULK
+#define GLK_SCOTT_HULK
+
+struct GameInfo;
+
+namespace Glk {
+namespace Scott {
+
+void hulkShowImageOnExamine(int noun);
+void hulkLook();
+void drawHulkImage(int p);
+int tryLoadingHulk(GameInfo info, int dictStart);
+
+} // End of namespace Scott
+} // End of namespace Glk
+
+#endif


Commit: a864ce872f17b4fbb15c78c3986a03521a61f734
    https://github.com/scummvm/scummvm/commit/a864ce872f17b4fbb15c78c3986a03521a61f734
Author: Avijeet (am388488 at gmail.com)
Date: 2022-04-12T20:35:24-07:00

Commit Message:
GLK: SCOTT: Add text formatting.

Changed paths:
  A engines/glk/scott/layout_text.cpp
  A engines/glk/scott/layout_text.h


diff --git a/engines/glk/scott/layout_text.cpp b/engines/glk/scott/layout_text.cpp
new file mode 100644
index 00000000000..1ba4700c960
--- /dev/null
+++ b/engines/glk/scott/layout_text.cpp
@@ -0,0 +1,108 @@
+/* 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 "glk/scott/layout_text.h"
+#include "common/util.h"
+
+namespace Glk {
+namespace Scott {
+
+int findBreak(const char *buf, int pos, int columns) {
+	int diff = 0;
+
+	while (diff < columns && !Common::isSpace((unsigned char)buf[pos])) {
+		pos--;
+		diff++;
+	}
+
+	if (diff >= columns || diff < 1) /* Found no space */ {
+		return -1;
+	}
+
+	return diff;
+}
+
+/* Breaks a null-terminated string up by inserting newlines, moving words
+ down to the next line when reaching the end of the line */
+char *lineBreakText(char *source, int columns, int *rows, int *length) {
+	columns -= 1;
+
+	char *result = nullptr;
+	char buf[768];
+	int col = 0;
+	int row = 0;
+	int sourcepos = 0;
+	int destpos = 0;
+	int diff = 0;
+	*rows = 0;
+	while (source[sourcepos] != '\0') {
+		while (col < columns && source[sourcepos] != '\0') {
+			if (source[sourcepos] == 10 || source[sourcepos] == 13) {
+				/* Found a line break. */
+				/* Any spaces before a line break may cause trouble, */
+				/* so we delete them */
+				while (destpos && buf[destpos - 1] == ' ') {
+					destpos--;
+				}
+				col = 0;
+				row++;
+			} else {
+				col++;
+			}
+
+			buf[destpos++] = source[sourcepos++];
+
+			if (source[sourcepos] == 10 || source[sourcepos] == 13)
+				col--;
+		}
+
+		/* We have reached the end of a line */
+		row++;
+		col = 0;
+
+		if (source[sourcepos] == '\0') {
+			break;
+		}
+
+		diff = findBreak(source, sourcepos, columns);
+		if (diff > -1) { /* We found a suitable break */
+			sourcepos = sourcepos - diff;
+			destpos = destpos - diff;
+			buf[destpos++] = '\n';
+
+			if (Common::isSpace((unsigned char)source[sourcepos])) {
+				sourcepos++;
+			}
+		}
+	}
+	*rows = row;
+	*length = 0;
+	result = new char[destpos + 1];
+	if (result == nullptr)
+		return nullptr;
+	memcpy(result, buf, destpos);
+	result[destpos] = '\0';
+	*length = destpos;
+	return result;
+}
+
+} // End of namespace Scott
+} // End of namespace Glk
diff --git a/engines/glk/scott/layout_text.h b/engines/glk/scott/layout_text.h
new file mode 100644
index 00000000000..54d3125558f
--- /dev/null
+++ b/engines/glk/scott/layout_text.h
@@ -0,0 +1,34 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 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 GLK_SCOTT_LAYOUTTEXT
+#define GLK_SCOTT_LAYOUTTEXT
+
+namespace Glk {
+namespace Scott {
+
+/* Breaks a null-terminated string up by inserting newlines,*/
+/* moving words down to the next line when reaching the end of the line */
+char *lineBreakText(char *source, int columns, int *rows, int *length);
+
+} // End of namespace Scott
+} // End of namespace Glk
+#endif


Commit: ef3621924539d0e7ef5fa1abc491e165f394e8a2
    https://github.com/scummvm/scummvm/commit/ef3621924539d0e7ef5fa1abc491e165f394e8a2
Author: Avijeet (am388488 at gmail.com)
Date: 2022-04-12T20:35:24-07:00

Commit Message:
GLK: SCOTT: Add support for saving and loading game state.

Changed paths:
  A engines/glk/scott/restore_state.cpp
  A engines/glk/scott/restore_state.h


diff --git a/engines/glk/scott/restore_state.cpp b/engines/glk/scott/restore_state.cpp
new file mode 100644
index 00000000000..1fd46e0da16
--- /dev/null
+++ b/engines/glk/scott/restore_state.cpp
@@ -0,0 +1,158 @@
+/* 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 "glk/scott/restore_state.h"
+#include "glk/scott/globals.h"
+#include "glk/scott/definitions.h"
+#include "glk/scott/scott.h"
+
+namespace Glk {
+namespace Scott {
+
+#define MAX_UNDOS 100
+
+void saveUndo() {
+	if (_G(_justUndid)) {
+		_G(_justUndid) = 0;
+		return;
+	}
+	if (_G(_lastUndo) == nullptr) {
+		_G(_lastUndo) = saveCurrentState();
+		_G(_oldestUndo) = _G(_lastUndo);
+		_G(_numberOfUndos) = 1;
+		return;
+	}
+
+	if (_G(_numberOfUndos) == 0)
+		g_scott->fatal("Number of undos == 0 but _G(_lastUndo) != nullptr!");
+
+	_G(_lastUndo)->_nextState = saveCurrentState();
+	SavedState *current = _G(_lastUndo)->_nextState;
+	current->_previousState = _G(_lastUndo);
+	_G(_lastUndo) = current;
+	if (_G(_numberOfUndos) == MAX_UNDOS) {
+		SavedState *oldest = _G(_oldestUndo);
+		_G(_oldestUndo) = _G(_oldestUndo)->_nextState;
+		_G(_oldestUndo)->_previousState = nullptr;
+		delete[] oldest->_itemLocations;
+		delete oldest;
+	} else {
+		_G(_numberOfUndos)++;
+	}
+}
+
+void restoreUndo() {
+	if (_G(_justStarted)) {
+		g_scott->output(_G(_sys)[CANT_UNDO_ON_FIRST_TURN]);
+		return;
+	}
+	if (_G(_lastUndo) == nullptr || _G(_lastUndo)->_previousState == nullptr) {
+		g_scott->output(_G(_sys)[NO_UNDO_STATES]);
+		return;
+	}
+	SavedState *current = _G(_lastUndo);
+	_G(_lastUndo) = current->_previousState;
+	if (_G(_lastUndo)->_previousState == nullptr)
+		_G(_oldestUndo) = _G(_lastUndo);
+	restoreState(_G(_lastUndo));
+	g_scott->output(_G(_sys)[MOVE_UNDONE]);
+	delete[] current->_itemLocations;
+	delete current;
+	_G(_numberOfUndos)--;
+	_G(_justUndid) = 1;
+}
+
+void ramSave() {
+	if (_G(_ramSave) != nullptr) {
+		delete[] _G(_ramSave)->_itemLocations;
+		delete _G(_ramSave);
+	}
+
+	_G(_ramSave) = saveCurrentState();
+	g_scott->output(_G(_sys)[STATE_SAVED]);
+}
+
+void ramRestore() {
+	if (_G(_ramSave) == nullptr) {
+		g_scott->output(_G(_sys)[NO_SAVED_STATE]);
+		return;
+	}
+
+	restoreState(_G(_ramSave));
+	g_scott->output(_G(_sys)[STATE_RESTORED]);
+	saveUndo();
+}
+
+SavedState *saveCurrentState() {
+	SavedState *s = new SavedState;
+	for (int ct = 0; ct < 16; ct++) {
+		s->_counters[ct] = _G(_counters)[ct];
+		s->_roomSaved[ct] = _G(_roomSaved)[ct];
+	}
+
+	s->_bitFlags = _G(_bitFlags);
+	s->_currentLoc = MY_LOC;
+	s->_currentCounter = _G(_currentCounter);
+	s->_savedRoom = _G(_savedRoom);
+	s->_lightTime = _G(_gameHeader)->_lightTime;
+	s->_autoInventory = _G(_autoInventory);
+
+	s->_itemLocations = new uint8_t[_G(_gameHeader)->_numItems + 1];
+
+	for (int ct = 0; ct <= _G(_gameHeader)->_numItems; ct++) {
+		s->_itemLocations[ct] = _G(_items)[ct]._location;
+	}
+
+	s->_previousState = nullptr;
+	s->_nextState = nullptr;
+
+	return s;
+}
+
+void recoverFromBadRestore(SavedState *state) {
+	g_scott->output(_G(_sys)[BAD_DATA]);
+	restoreState(state);
+	delete state;
+}
+
+void restoreState(SavedState *state) {
+	for (int ct = 0; ct < 16; ct++) {
+		_G(_counters)[ct] = state->_counters[ct];
+		_G(_roomSaved)[ct] = state->_roomSaved[ct];
+	}
+
+	_G(_bitFlags) = state->_bitFlags;
+
+	MY_LOC = state->_currentLoc;
+	_G(_currentCounter) = state->_currentCounter;
+	_G(_savedRoom) = state->_savedRoom;
+	_G(_gameHeader)->_lightTime = state->_lightTime;
+	_G(_autoInventory) = state->_autoInventory;
+
+	for (int ct = 0; ct <= _G(_gameHeader)->_numItems; ct++) {
+		_G(_items)[ct]._location = state->_itemLocations[ct];
+	}
+
+	_G(_stopTime) = 1;
+}
+
+} // End of namespace Scott
+} // End of namespace Glk
diff --git a/engines/glk/scott/restore_state.h b/engines/glk/scott/restore_state.h
new file mode 100644
index 00000000000..7f5c835484e
--- /dev/null
+++ b/engines/glk/scott/restore_state.h
@@ -0,0 +1,55 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 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 GLK_SCOTT_RESTORESTATE
+#define GLK_SCOTT_RESTORESTATE
+
+#include <stdint.h>
+
+namespace Glk {
+namespace Scott {
+
+struct SavedState {
+	int _counters[16];
+	int _roomSaved[16];
+	uint32_t _bitFlags = 0;
+	int _currentLoc = 0;
+	int _currentCounter = 0;
+	int _savedRoom = 0;
+	int _lightTime = 0;
+	int _autoInventory = 0;
+	uint8_t *_itemLocations = nullptr;
+	SavedState *_previousState = nullptr;
+	SavedState *_nextState = nullptr;
+};
+
+void saveUndo();
+void restoreUndo();
+void ramSave();
+void ramRestore();
+SavedState *saveCurrentState();
+void recoverFromBadRestore(SavedState *state);
+void restoreState(SavedState *state);
+
+} // End of namespace Scott
+} // End of namespace Glk
+
+#endif


Commit: 72dc87f4b43b08993efcbb96752ba2b7086c79c9
    https://github.com/scummvm/scummvm/commit/72dc87f4b43b08993efcbb96752ba2b7086c79c9
Author: Avijeet (am388488 at gmail.com)
Date: 2022-04-12T20:35:24-07:00

Commit Message:
GLK: Add new source files.

Changed paths:
    engines/glk/module.mk


diff --git a/engines/glk/module.mk b/engines/glk/module.mk
index cab4fac4aa0..b3b2d99a28b 100644
--- a/engines/glk/module.mk
+++ b/engines/glk/module.mk
@@ -234,6 +234,19 @@ MODULE_OBJS := \
 	quest/read_file.o \
 	quest/string.o \
 	quest/streams.o \
+	scott/command_parser.o \
+	scott/decompress_text.o \
+	scott/decompress_z80.o \
+	scott/definitions.o \
+	scott/detect_game.o \
+	scott/game_info.o \
+	scott/globals.o \
+	scott/hulk.o \
+	scott/layout_text.o \
+	scott/line_drawing.o \
+	scott/restore_state.o \
+	scott/ring_buffer.o \
+	scott/saga_draw.o \
 	scott/scott.o \
 	tads/os_banners.o \
 	tads/os_buffer.o \


Commit: 8b846ec0dd44590195579648a245294fc935aaf9
    https://github.com/scummvm/scummvm/commit/8b846ec0dd44590195579648a245294fc935aaf9
Author: Avijeet (am388488 at gmail.com)
Date: 2022-04-12T20:35:24-07:00

Commit Message:
GLK: SCOTT: Import size_t.

Changed paths:
    engines/glk/scott/decompress_z80.h
    engines/glk/scott/detect_game.h
    engines/glk/scott/globals.h
    engines/glk/scott/hulk.cpp
    engines/glk/scott/line_drawing.h
    engines/glk/scott/ring_buffer.h
    engines/glk/scott/saga_draw.h
    engines/glk/scott/scott.h


diff --git a/engines/glk/scott/decompress_z80.h b/engines/glk/scott/decompress_z80.h
index 4aa8e3c88ea..2769ecf4f9a 100644
--- a/engines/glk/scott/decompress_z80.h
+++ b/engines/glk/scott/decompress_z80.h
@@ -22,7 +22,8 @@
 #ifndef GLK_SCOTT_DECOMPRESSZ80
 #define GLK_SCOTT_DECOMPRESSZ80
 
-#include <cstdint>
+#include <stdint.h>
+#include <stddef.h>
 
 namespace Glk {
 namespace Scott {
diff --git a/engines/glk/scott/detect_game.h b/engines/glk/scott/detect_game.h
index cbc0643f358..28a33d0b182 100644
--- a/engines/glk/scott/detect_game.h
+++ b/engines/glk/scott/detect_game.h
@@ -25,6 +25,7 @@
 #include "common/stream.h"
 #include "glk/scott/definitions.h"
 #include <stdint.h>
+#include <stddef.h>
 
 namespace Glk {
 namespace Scott {
diff --git a/engines/glk/scott/globals.h b/engines/glk/scott/globals.h
index 7a6fe86073c..3d3bbad0918 100644
--- a/engines/glk/scott/globals.h
+++ b/engines/glk/scott/globals.h
@@ -27,7 +27,8 @@
 #include "glk/scott/definitions.h"
 #include "common/array.h"
 #include "common/str-array.h"
-#include <cstdint>
+#include <stdint.h>
+#include <stddef.h>
 
 namespace Glk {
 namespace Scott {
diff --git a/engines/glk/scott/hulk.cpp b/engines/glk/scott/hulk.cpp
index 580b463e353..2cf037d5d98 100644
--- a/engines/glk/scott/hulk.cpp
+++ b/engines/glk/scott/hulk.cpp
@@ -24,6 +24,8 @@
 #include "glk/scott/detect_game.h"
 #include "glk/scott/globals.h"
 #include "glk/scott/scott.h"
+#include <stddef.h>
+#include <stdint.h>
 
 namespace Glk {
 namespace Scott {
diff --git a/engines/glk/scott/line_drawing.h b/engines/glk/scott/line_drawing.h
index 7e87abd79b9..f2d1810899d 100644
--- a/engines/glk/scott/line_drawing.h
+++ b/engines/glk/scott/line_drawing.h
@@ -22,6 +22,7 @@
 #ifndef GLK_SCOTT_LINEDRAWING
 #define GLK_SCOTT_LINEDRAWING
 
+#include <stddef.h>
 #include <stdint.h>
 
 namespace Glk {
diff --git a/engines/glk/scott/ring_buffer.h b/engines/glk/scott/ring_buffer.h
index 1a0a3597565..8c0c68ff973 100644
--- a/engines/glk/scott/ring_buffer.h
+++ b/engines/glk/scott/ring_buffer.h
@@ -22,6 +22,7 @@
 #ifndef GLK_SCOTT_RINGBUFFER
 #define GLK_SCOTT_RINGBUFFER
 
+#include <stddef.h>
 #include <stdint.h>
 
 namespace Glk {
diff --git a/engines/glk/scott/saga_draw.h b/engines/glk/scott/saga_draw.h
index 3d8ade31903..b9319b7d8d0 100644
--- a/engines/glk/scott/saga_draw.h
+++ b/engines/glk/scott/saga_draw.h
@@ -23,6 +23,7 @@
 #define GLK_SCOTT_SAGADRAW
 
 #include "glk/glk_types.h"
+#include <stddef.h>
 #include <stdint.h>
 
 namespace Glk {
diff --git a/engines/glk/scott/scott.h b/engines/glk/scott/scott.h
index 23561404271..4d5b1c96185 100644
--- a/engines/glk/scott/scott.h
+++ b/engines/glk/scott/scott.h
@@ -32,7 +32,8 @@
 #include "glk/glk_api.h"
 #include "glk/scott/definitions.h"
 #include "glk/scott/globals.h"
-#include <cstdint>
+#include <stddef.h>
+#include <stdint.h>
 
 namespace Glk {
 namespace Scott {


Commit: a7c29553f73df5cefef571066c4c0b2ed7df74f6
    https://github.com/scummvm/scummvm/commit/a7c29553f73df5cefef571066c4c0b2ed7df74f6
Author: Avijeet (am388488 at gmail.com)
Date: 2022-04-12T20:35:24-07:00

Commit Message:
GLK: SCOTT: Typedefs for used types.

Changed paths:
  A engines/glk/scott/types.h


diff --git a/engines/glk/scott/types.h b/engines/glk/scott/types.h
new file mode 100644
index 00000000000..43abcde29d0
--- /dev/null
+++ b/engines/glk/scott/types.h
@@ -0,0 +1,42 @@
+/* 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 GLK_SCOTT_TYPES
+#define GLK_SCOTT_TYPES
+
+#include "common/scummsys.h"
+
+namespace Glk {
+namespace Scott {
+
+typedef uint8 uint8_t;
+typedef uint16 uint16_t;
+typedef uint32 uint32_t;
+typedef int8 int8_t;
+typedef int16 int16_t;
+typedef int32 int32_t;
+typedef int64 int64_t;
+typedef unsigned long long size_t;
+
+} // End of namespace Scott
+} // End of namespace Glk
+
+#endif


Commit: 67adecb2d782021cf43304d65e6fa8d7d9a45b6f
    https://github.com/scummvm/scummvm/commit/67adecb2d782021cf43304d65e6fa8d7d9a45b6f
Author: Avijeet (am388488 at gmail.com)
Date: 2022-04-12T20:35:24-07:00

Commit Message:
GLK: SCOTT: Remove standard library includes.

Changed paths:
    engines/glk/scott/decompress_text.h
    engines/glk/scott/decompress_z80.h
    engines/glk/scott/detect_game.h
    engines/glk/scott/globals.h
    engines/glk/scott/hulk.cpp
    engines/glk/scott/line_drawing.h
    engines/glk/scott/restore_state.h
    engines/glk/scott/ring_buffer.h
    engines/glk/scott/saga_draw.h
    engines/glk/scott/scott.h


diff --git a/engines/glk/scott/decompress_text.h b/engines/glk/scott/decompress_text.h
index d90342f02c8..61756631f46 100644
--- a/engines/glk/scott/decompress_text.h
+++ b/engines/glk/scott/decompress_text.h
@@ -22,7 +22,7 @@
 #ifndef GLK_SCOTT_DECOMPRESSTEXT
 #define GLK_SCOTT_DECOMPRESSTEXT
 
-#include <stdint.h>
+#include "glk/scott/types.h"
 
 namespace Glk {
 namespace Scott {
diff --git a/engines/glk/scott/decompress_z80.h b/engines/glk/scott/decompress_z80.h
index 2769ecf4f9a..ae80faac43e 100644
--- a/engines/glk/scott/decompress_z80.h
+++ b/engines/glk/scott/decompress_z80.h
@@ -22,8 +22,7 @@
 #ifndef GLK_SCOTT_DECOMPRESSZ80
 #define GLK_SCOTT_DECOMPRESSZ80
 
-#include <stdint.h>
-#include <stddef.h>
+#include "glk/scott/types.h"
 
 namespace Glk {
 namespace Scott {
diff --git a/engines/glk/scott/detect_game.h b/engines/glk/scott/detect_game.h
index 28a33d0b182..b038fa3e786 100644
--- a/engines/glk/scott/detect_game.h
+++ b/engines/glk/scott/detect_game.h
@@ -24,8 +24,7 @@
 
 #include "common/stream.h"
 #include "glk/scott/definitions.h"
-#include <stdint.h>
-#include <stddef.h>
+#include "glk/scott/types.h"
 
 namespace Glk {
 namespace Scott {
diff --git a/engines/glk/scott/globals.h b/engines/glk/scott/globals.h
index 3d3bbad0918..7d02c0f01bc 100644
--- a/engines/glk/scott/globals.h
+++ b/engines/glk/scott/globals.h
@@ -25,10 +25,9 @@
 #include "glk/glk_types.h"
 #include "glk/windows.h"
 #include "glk/scott/definitions.h"
+#include "glk/scott/types.h"
 #include "common/array.h"
 #include "common/str-array.h"
-#include <stdint.h>
-#include <stddef.h>
 
 namespace Glk {
 namespace Scott {
diff --git a/engines/glk/scott/hulk.cpp b/engines/glk/scott/hulk.cpp
index 2cf037d5d98..33073c86c5b 100644
--- a/engines/glk/scott/hulk.cpp
+++ b/engines/glk/scott/hulk.cpp
@@ -24,8 +24,7 @@
 #include "glk/scott/detect_game.h"
 #include "glk/scott/globals.h"
 #include "glk/scott/scott.h"
-#include <stddef.h>
-#include <stdint.h>
+#include "glk/scott/types.h"
 
 namespace Glk {
 namespace Scott {
diff --git a/engines/glk/scott/line_drawing.h b/engines/glk/scott/line_drawing.h
index f2d1810899d..86e7bf3ada0 100644
--- a/engines/glk/scott/line_drawing.h
+++ b/engines/glk/scott/line_drawing.h
@@ -22,8 +22,7 @@
 #ifndef GLK_SCOTT_LINEDRAWING
 #define GLK_SCOTT_LINEDRAWING
 
-#include <stddef.h>
-#include <stdint.h>
+#include "glk/scott/types.h"
 
 namespace Glk {
 namespace Scott {
diff --git a/engines/glk/scott/restore_state.h b/engines/glk/scott/restore_state.h
index 7f5c835484e..93edf6acc10 100644
--- a/engines/glk/scott/restore_state.h
+++ b/engines/glk/scott/restore_state.h
@@ -22,7 +22,7 @@
 #ifndef GLK_SCOTT_RESTORESTATE
 #define GLK_SCOTT_RESTORESTATE
 
-#include <stdint.h>
+#include "glk/scott/types.h"
 
 namespace Glk {
 namespace Scott {
diff --git a/engines/glk/scott/ring_buffer.h b/engines/glk/scott/ring_buffer.h
index 8c0c68ff973..b8a3e2e8157 100644
--- a/engines/glk/scott/ring_buffer.h
+++ b/engines/glk/scott/ring_buffer.h
@@ -22,8 +22,7 @@
 #ifndef GLK_SCOTT_RINGBUFFER
 #define GLK_SCOTT_RINGBUFFER
 
-#include <stddef.h>
-#include <stdint.h>
+#include "glk/scott/types.h"
 
 namespace Glk {
 namespace Scott {
diff --git a/engines/glk/scott/saga_draw.h b/engines/glk/scott/saga_draw.h
index b9319b7d8d0..11fa5be0f6e 100644
--- a/engines/glk/scott/saga_draw.h
+++ b/engines/glk/scott/saga_draw.h
@@ -23,8 +23,7 @@
 #define GLK_SCOTT_SAGADRAW
 
 #include "glk/glk_types.h"
-#include <stddef.h>
-#include <stdint.h>
+#include "glk/scott/types.h"
 
 namespace Glk {
 namespace Scott {
diff --git a/engines/glk/scott/scott.h b/engines/glk/scott/scott.h
index 4d5b1c96185..f81b5fa8ccf 100644
--- a/engines/glk/scott/scott.h
+++ b/engines/glk/scott/scott.h
@@ -32,8 +32,7 @@
 #include "glk/glk_api.h"
 #include "glk/scott/definitions.h"
 #include "glk/scott/globals.h"
-#include <stddef.h>
-#include <stdint.h>
+#include "glk/scott/types.h"
 
 namespace Glk {
 namespace Scott {


Commit: a50d09aa31c1b1a17a07bce369f4f416f7f4c546
    https://github.com/scummvm/scummvm/commit/a50d09aa31c1b1a17a07bce369f4f416f7f4c546
Author: Avijeet (am388488 at gmail.com)
Date: 2022-04-12T20:35:24-07:00

Commit Message:
GLK: SCOTT: Define SIZE_MAX.

Changed paths:
    engines/glk/scott/types.h


diff --git a/engines/glk/scott/types.h b/engines/glk/scott/types.h
index 43abcde29d0..2bc5f4427f2 100644
--- a/engines/glk/scott/types.h
+++ b/engines/glk/scott/types.h
@@ -27,6 +27,8 @@
 namespace Glk {
 namespace Scott {
 
+#define SIZE_MAX 0xFFFFFFFF
+
 typedef uint8 uint8_t;
 typedef uint16 uint16_t;
 typedef uint32 uint32_t;




More information about the Scummvm-git-logs mailing list