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

dreammaster paulfgilbert at gmail.com
Sat May 11 04:46:14 CEST 2019


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

Summary:
6b75eff73c GLK: HUGO: Initial sub-engine skeleton
cfd19b1a84 GLK: HUGO: First detection entry
e3e631e297 GLK: HUGO: Added engine feilds
19a9b9aa31 GLK: HUGO: heglk fields & initialization
2765ba9afa GLK: HUGO: Added Glk interface methods
8830063635 GLK: HUGO: Added hemisc, htokens, and stringfn files
0b2346c1f1 GLK: HUGO: Added heobject
c1f8bd8823 GLK: HUGO: Replace contents of hemisc


Commit: 6b75eff73cb22181c924acf5e105316394d093e8
    https://github.com/scummvm/scummvm/commit/6b75eff73cb22181c924acf5e105316394d093e8
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-05-10T12:11:04+10:00

Commit Message:
GLK: HUGO: Initial sub-engine skeleton

Changed paths:
  A engines/glk/hugo/detection.cpp
  A engines/glk/hugo/detection.h
  A engines/glk/hugo/detection_tables.h
  A engines/glk/hugo/hugo.cpp
  A engines/glk/hugo/hugo.h
  A engines/glk/hugo/hugo_types.h
    engines/glk/detection.cpp
    engines/glk/module.mk


diff --git a/engines/glk/detection.cpp b/engines/glk/detection.cpp
index 193d439..fe2ea0b 100644
--- a/engines/glk/detection.cpp
+++ b/engines/glk/detection.cpp
@@ -28,6 +28,8 @@
 #include "glk/frotz/frotz.h"
 #include "glk/glulxe/detection.h"
 #include "glk/glulxe/glulxe.h"
+#include "glk/hugo/detection.h"
+#include "glk/hugo/hugo.h"
 #include "glk/magnetic/detection.h"
 #include "glk/magnetic/magnetic.h"
 #include "glk/scott/detection.h"
@@ -114,6 +116,7 @@ Common::Error GlkMetaEngine::createInstance(OSystem *syst, Engine **engine) cons
 	else if ((*engine = create<Glk::Scott::ScottMetaEngine, Glk::Scott::Scott>(syst, gameDesc)) != nullptr) {}
 #ifndef RELEASE_BUILD
 	else if ((*engine = create<Glk::Alan2::Alan2MetaEngine, Glk::Alan2::Alan2>(syst, gameDesc)) != nullptr) {}
+	else if ((*engine = create<Glk::Hugo::HugoMetaEngine, Glk::Hugo::Hugo>(syst, gameDesc)) != nullptr) {}
 	else if ((*engine = create<Glk::Magnetic::MagneticMetaEngine, Glk::Magnetic::Magnetic>(syst, gameDesc)) != nullptr) {}
 	else if ((td = Glk::TADS::TADSMetaEngine::findGame(gameDesc._gameId.c_str()))._description) {
 		if (td._options & Glk::TADS::OPTION_TADS3)
@@ -158,6 +161,7 @@ PlainGameList GlkMetaEngine::getSupportedGames() const {
 	Glk::Scott::ScottMetaEngine::getSupportedGames(list);
 #ifndef RELEASE_BUILD
 	Glk::Alan2::Alan2MetaEngine::getSupportedGames(list);
+	Glk::Hugo::HugoMetaEngine::getSupportedGames(list);
 	Glk::Magnetic::MagneticMetaEngine::getSupportedGames(list);
 	Glk::TADS::TADSMetaEngine::getSupportedGames(list);
 #endif
@@ -179,6 +183,9 @@ PlainGameDescriptor GlkMetaEngine::findGame(const char *gameId) const {
 	gd = Glk::Alan2::Alan2MetaEngine::findGame(gameId);
 	if (gd._description) return gd;
 
+	gd = Glk::Hugo::HugoMetaEngine::findGame(gameId);
+	if (gd._description) return gd;
+
 	gd = Glk::Magnetic::MagneticMetaEngine::findGame(gameId);
 	if (gd._description) return gd;
 
@@ -199,6 +206,7 @@ DetectedGames GlkMetaEngine::detectGames(const Common::FSList &fslist) const {
 
 #ifndef RELEASE_BUILD
 	Glk::Alan2::Alan2MetaEngine::detectGames(fslist, detectedGames);
+	Glk::Hugo::HugoMetaEngine::detectGames(fslist, detectedGames);
 	Glk::Magnetic::MagneticMetaEngine::detectGames(fslist, detectedGames);
 	Glk::TADS::TADSMetaEngine::detectGames(fslist, detectedGames);
 #endif
@@ -214,6 +222,7 @@ void GlkMetaEngine::detectClashes() const {
 
 #ifndef RELEASE_BUILD
 	Glk::Alan2::Alan2MetaEngine::detectClashes(map);
+	Glk::Hugo::HugoMetaEngine::detectClashes(map);
 	Glk::Magnetic::MagneticMetaEngine::detectClashes(map);
 	Glk::TADS::TADSMetaEngine::detectClashes(map);
 #endif
diff --git a/engines/glk/hugo/detection.cpp b/engines/glk/hugo/detection.cpp
new file mode 100644
index 0000000..6a8203b
--- /dev/null
+++ b/engines/glk/hugo/detection.cpp
@@ -0,0 +1,113 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "glk/hugo/detection.h"
+#include "glk/hugo/detection_tables.h"
+#include "common/debug.h"
+#include "common/file.h"
+#include "common/md5.h"
+#include "engines/game.h"
+
+namespace Glk {
+namespace Hugo {
+
+void HugoMetaEngine::getSupportedGames(PlainGameList &games) {
+	for (const PlainGameDescriptor *pd = HUGO_GAME_LIST; pd->gameId; ++pd) {
+		games.push_back(*pd);
+	}
+}
+
+GameDescriptor HugoMetaEngine::findGame(const char *gameId) {
+	for (const PlainGameDescriptor *pd = HUGO_GAME_LIST; pd->gameId; ++pd) {
+		if (!strcmp(gameId, pd->gameId))
+			return *pd;
+	}
+
+	return PlainGameDescriptor();
+}
+
+bool HugoMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &gameList) {
+	const char *const EXTENSIONS[] = { ".mag", ".rsc", nullptr };
+
+	// Loop through the files of the folder
+	for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
+		// Check for a recognised filename
+		if (file->isDirectory())
+			continue;
+		Common::String filename = file->getName();
+		bool hasExt = false;
+		for (const char *const *ext = &EXTENSIONS[0]; *ext && !hasExt; ++ext)
+			hasExt = filename.hasSuffixIgnoreCase(*ext);
+		if (!hasExt)
+			continue;
+
+		// Open up the file and calculate the md5
+		Common::File gameFile;
+		if (!gameFile.open(*file))
+			continue;
+		if (gameFile.readUint32BE() != MKTAG('M', 'a', 'S', 'c')) {
+			gameFile.close();
+			continue;
+		}
+
+		gameFile.seek(0);
+		Common::String md5 = Common::computeStreamMD5AsString(gameFile, 5000);
+		size_t filesize = gameFile.size();
+		gameFile.close();
+
+		// Check for known games
+		const HugoGameDescription *p = HUGO_GAMES;
+		while (p->_gameId && (md5 != p->_md5 || filesize != p->_filesize))
+			++p;
+
+		DetectedGame gd;
+		if (!p->_gameId) {
+			if (gDebugLevel > 0) {
+				// Print an entry suitable for putting into the detection_tables.h
+				debug("ENTRY0(\"%s\", \"%s\", %u),", filename.c_str(), md5.c_str(), (uint)filesize);
+			}
+
+			const PlainGameDescriptor &desc = HUGO_GAME_LIST[0];
+			gd = DetectedGame(desc.gameId, desc.description, Common::UNK_LANG, Common::kPlatformUnknown);
+		} else {
+			PlainGameDescriptor gameDesc = findGame(p->_gameId);
+			gd = DetectedGame(p->_gameId, gameDesc.description, p->_language, Common::kPlatformUnknown, p->_extra);
+			gd.setGUIOptions(GUIO4(GUIO_NOSPEECH, GUIO_NOSFX, GUIO_NOMUSIC, GUIO_NOSUBTITLES));
+		}
+
+		gd.addExtraEntry("filename", filename);
+		gameList.push_back(gd);
+	}
+
+	return !gameList.empty();
+}
+
+void HugoMetaEngine::detectClashes(Common::StringMap &map) {
+	for (const PlainGameDescriptor *pd = HUGO_GAME_LIST; pd->gameId; ++pd) {
+		if (map.contains(pd->gameId))
+			error("Duplicate game Id found - %s", pd->gameId);
+		map[pd->gameId] = "";
+	}
+}
+
+} // End of namespace Hugo
+} // End of namespace Glk
diff --git a/engines/glk/hugo/detection.h b/engines/glk/hugo/detection.h
new file mode 100644
index 0000000..221a2ed
--- /dev/null
+++ b/engines/glk/hugo/detection.h
@@ -0,0 +1,63 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef GLK_HUGO_DETECTION
+#define GLK_HUGO_DETECTION
+
+#include "common/fs.h"
+#include "common/hash-str.h"
+#include "engines/game.h"
+#include "glk/detection.h"
+
+namespace Glk {
+namespace Hugo {
+
+/**
+ * Meta engine for Hugo interpreter
+ */
+class HugoMetaEngine {
+public:
+	/**
+	 * Get a list of supported games
+	 */
+	static void getSupportedGames(PlainGameList &games);
+
+	/**
+	 * Returns a game description for the given game Id, if it's supported
+	 */
+	static GameDescriptor findGame(const char *gameId);
+
+	/**
+	 * Detect supported games
+	 */
+	static bool detectGames(const Common::FSList &fslist, DetectedGames &gameList);
+
+	/**
+	 * Check for game Id clashes with other sub-engines
+	 */
+	static void detectClashes(Common::StringMap &map);
+};
+
+} // End of namespace Hugo
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/hugo/detection_tables.h b/engines/glk/hugo/detection_tables.h
new file mode 100644
index 0000000..8afe7c1
--- /dev/null
+++ b/engines/glk/hugo/detection_tables.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 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/game.h"
+#include "common/gui_options.h"
+#include "common/language.h"
+
+namespace Glk {
+namespace Hugo {
+
+/**
+ * Game description
+ */
+struct HugoGameDescription {
+	const char *const _gameId;
+	const char *const _extra;
+	const char *const _md5;
+	size_t _filesize;
+	Common::Language _language;
+};
+
+const PlainGameDescriptor HUGO_GAME_LIST[] = {
+	{ "hugo", "Hugo IF Game" },
+
+	{ nullptr, nullptr }
+};
+
+#define ENTRY0(ID, MD5, FILESIZE) { ID, nullptr, MD5, FILESIZE, Common::EN_ANY }
+#define ENTRY1(ID, EXTRA, MD5, FILESIZE) { ID, EXTRA, MD5, FILESIZE, Common::EN_ANY }
+#define TABLE_END_MARKER { nullptr, nullptr, nullptr, 0, Common::EN_ANY }
+
+const HugoGameDescription HUGO_GAMES[] = {
+	TABLE_END_MARKER
+};
+
+} // End of namespace Hugo
+} // End of namespace Glk
diff --git a/engines/glk/hugo/hugo.cpp b/engines/glk/hugo/hugo.cpp
new file mode 100644
index 0000000..5945d81
--- /dev/null
+++ b/engines/glk/hugo/hugo.cpp
@@ -0,0 +1,46 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "glk/hugo/hugo.h"
+
+namespace Glk {
+namespace Hugo {
+
+Hugo::Hugo(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gameDesc) {
+}
+
+void Hugo::runGame() {
+
+}
+
+Common::Error Hugo::loadGameData(strid_t file) {
+	// TODO
+	return Common::kNoError;
+}
+
+Common::Error Hugo::saveGameData(strid_t file, const Common::String &desc) {
+	// TODO
+	return Common::kNoError;
+}
+
+} // End of namespace Hugo
+} // End of namespace Glk
diff --git a/engines/glk/hugo/hugo.h b/engines/glk/hugo/hugo.h
new file mode 100644
index 0000000..7032bba
--- /dev/null
+++ b/engines/glk/hugo/hugo.h
@@ -0,0 +1,67 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef GLK_HUGO_HUGO
+#define GLK_HUGO_HUGO
+
+#include "common/scummsys.h"
+#include "glk/glk_api.h"
+#include "glk/hugo/hugo_types.h"
+
+namespace Glk {
+namespace Hugo {
+
+/**
+ * Hugo game interpreter
+ */
+class Hugo : public GlkAPI {
+public:
+	/**
+	 * Constructor
+	 */
+	Hugo(OSystem *syst, const GlkGameDescription &gameDesc);
+
+	/**
+	 * Run the game
+	 */
+	void runGame();
+
+	/**
+	 * Returns the running interpreter type
+	 */
+	virtual InterpreterType getInterpreterType() const override { return INTERPRETER_HUGO; }
+
+	/**
+	 * Load a savegame from the passed stream
+	 */
+	virtual Common::Error loadGameData(strid_t file) override;
+
+	/**
+	 * Save the game to the passed stream
+	 */
+	virtual Common::Error saveGameData(strid_t file, const Common::String &desc) override;
+};
+
+} // End of namespace Hugo
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/hugo/hugo_types.h b/engines/glk/hugo/hugo_types.h
new file mode 100644
index 0000000..19e5396
--- /dev/null
+++ b/engines/glk/hugo/hugo_types.h
@@ -0,0 +1,136 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef GLK_HUGO_TYPES
+#define GLK_HUGO_TYPES
+
+#include "common/scummsys.h"
+
+namespace Glk {
+namespace Hugo {
+
+#define MAX_HINTS 260
+#define MAX_HCONTENTS 30000
+
+#define MAX_POSITIONS 20
+#define MAX_ANIMS 200
+#define MAX_FRAMES 20
+#define MAX_STRING_SIZE  0xFF00
+#define MAX_PICTURE_SIZE 0xC800
+#define MAX_MUSIC_SIZE   0x4E20
+#define MAX_HITEMS 25
+
+struct lookup {
+	int16 flag;
+	int16 count;
+
+	lookup() : flag(0), count(0) {}
+};
+
+struct picture {
+	byte * data;
+	uint32 data_size;
+	uint16 width;
+	uint16 height;
+	uint16 wbytes;
+	uint16 plane_step;
+	byte *mask;
+
+	picture() : data(nullptr), data_size(0), width(0), height(0), wbytes(0), plane_step(0),
+		mask(nullptr) {}
+};
+
+/**
+ * Hugo animated pictures support
+ *
+ * Note: Some of the pictures for Wonderland and the Collection Volume 1 games
+ * are animations. To detect these, pass a pointer to a type8 as the is_anim
+ * argument to ms_extract().
+ *
+ * There are two types of animated images, however almost all images are type1.
+ * A type1 image consists of four main elements:
+ * 1) A static picture which is loaded straight at the beginning
+ * 2) A set of frames with a mask. These frames are just "small pictures", which
+ *    are coded like the normal static pictures. The image mask determines
+ *    how the frame is removed after it has been displayed. A mask is exactly
+ *    1/8 the size of the image and holds 1 bit per pixel, saying "remove pixel"
+ *    or leave pixel set when frame gets removed. It might be a good idea to check
+ *    your system documentation for masking operations as your system might be
+ *    able to use this mask data directly.
+ * 3) Positioning tables. These hold animation sequences consisting of commands
+ *    like "Draw frame 12 at (123,456)"
+ * 4) A playback script, which determines how to use the positioning tables.
+ *    These scripts are handled inside Hugo, so no need to worry about.
+ *    However, details can be found in the ms_animate() function.
+ *
+ * A type2 image is like a type1 image, but it does not have a static
+ * picture, nor does it have frame masking. It just consists of frames.
+ *
+ * How to support animations?
+ * After getting is_anim == 1 you should call ms_animate() immediately, and at
+ * regular intervals until ms_animate() returns 0. An appropriate interval
+ * between calls is about 100 milliseconds.
+ * Each call to ms_animate() will fill in the arguments with the address
+ * and size of an array of ms_position structures (see below), each of
+ * which holds an an animation frame number and x and y co-ordinates. To
+ * display the animation, decode all the animation frames (discussed below)
+ * from a single call to ms_animate() and display each one over the main picture.
+ * If your port does not support animations, define NO_ANIMATION.
+ */
+struct ms_position {
+	int16 x, y;
+	int16 number;
+
+	ms_position() : x(0), y(0), number(0) {}
+};
+
+/**
+ * Hugo Windows hint support
+ *
+ * The windowed Hugo Scolls games included online hints. To add support
+ * for the hints to your hugo port, you should implement the ms_showhints
+ * function. It retrieves a pointer to an array of ms_hint structs
+ * The root element is always hints[0]. The elcount determines the number
+ * of items in this topic. You probably want to display those in some kind
+ * of list interface. The content pointer points to the actual description of
+ * the items, separated by '\0' terminators. The nodetype is 1 if the items are
+ * "folders" and 2 if the items are hints. Hints should be displayed one after
+ * another. For "folder" items, the links array holds the index of the hint in
+ * the array which is to be displayed on selection. One hint block has exactly
+ * one type. The parent element determines the "back" target.
+ */
+struct ms_hint {
+	uint16 elcount;
+	uint16 nodetype;
+	byte *content;
+	uint16 links[MAX_HITEMS];
+	uint16 parent;
+
+	ms_hint() : elcount(0), nodetype(0), content(nullptr), parent(0) {
+		Common::fill(&links[0], &links[MAX_HITEMS], 0);
+	}
+};
+
+} // End of namespace Hugo
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/module.mk b/engines/glk/module.mk
index f8367c7..ea85350 100644
--- a/engines/glk/module.mk
+++ b/engines/glk/module.mk
@@ -71,6 +71,8 @@ MODULE_OBJS := \
 	glulxe/serial.o \
 	glulxe/string.o \
 	glulxe/vm.o \
+	hugo/detection.o \
+	hugo/hugo.o \
 	magnetic/detection.o \
 	magnetic/emu.o \
 	magnetic/graphics.o \


Commit: cfd19b1a841814d93784af7472cabbc04680cf11
    https://github.com/scummvm/scummvm/commit/cfd19b1a841814d93784af7472cabbc04680cf11
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-05-10T12:11:04+10:00

Commit Message:
GLK: HUGO: First detection entry

Changed paths:
    engines/glk/hugo/detection.cpp
    engines/glk/hugo/detection_tables.h


diff --git a/engines/glk/hugo/detection.cpp b/engines/glk/hugo/detection.cpp
index 6a8203b..299c837 100644
--- a/engines/glk/hugo/detection.cpp
+++ b/engines/glk/hugo/detection.cpp
@@ -46,7 +46,7 @@ GameDescriptor HugoMetaEngine::findGame(const char *gameId) {
 }
 
 bool HugoMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &gameList) {
-	const char *const EXTENSIONS[] = { ".mag", ".rsc", nullptr };
+	const char *const EXTENSIONS[] = { ".hex", nullptr };
 
 	// Loop through the files of the folder
 	for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
@@ -64,12 +64,7 @@ bool HugoMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &ga
 		Common::File gameFile;
 		if (!gameFile.open(*file))
 			continue;
-		if (gameFile.readUint32BE() != MKTAG('M', 'a', 'S', 'c')) {
-			gameFile.close();
-			continue;
-		}
 
-		gameFile.seek(0);
 		Common::String md5 = Common::computeStreamMD5AsString(gameFile, 5000);
 		size_t filesize = gameFile.size();
 		gameFile.close();
diff --git a/engines/glk/hugo/detection_tables.h b/engines/glk/hugo/detection_tables.h
index 8afe7c1..b7f3a10 100644
--- a/engines/glk/hugo/detection_tables.h
+++ b/engines/glk/hugo/detection_tables.h
@@ -41,6 +41,8 @@ struct HugoGameDescription {
 const PlainGameDescriptor HUGO_GAME_LIST[] = {
 	{ "hugo", "Hugo IF Game" },
 
+	{ "annoyotron2", "Aggravatron: Annoyotron II" },
+
 	{ nullptr, nullptr }
 };
 
@@ -49,6 +51,7 @@ const PlainGameDescriptor HUGO_GAME_LIST[] = {
 #define TABLE_END_MARKER { nullptr, nullptr, nullptr, 0, Common::EN_ANY }
 
 const HugoGameDescription HUGO_GAMES[] = {
+	ENTRY0("annoyotron2", "944056721054fd1c9af9d1e95e63ce52", 66762),
 	TABLE_END_MARKER
 };
 


Commit: e3e631e2977a42388678e445f142254236c8a951
    https://github.com/scummvm/scummvm/commit/e3e631e2977a42388678e445f142254236c8a951
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-05-10T12:11:05+10:00

Commit Message:
GLK: HUGO: Added engine feilds

Changed paths:
  A engines/glk/hugo/heglk.cpp
    engines/glk/hugo/hugo.cpp
    engines/glk/hugo/hugo.h
    engines/glk/hugo/hugo_types.h
    engines/glk/module.mk


diff --git a/engines/glk/hugo/heglk.cpp b/engines/glk/hugo/heglk.cpp
new file mode 100644
index 0000000..1dd0935
--- /dev/null
+++ b/engines/glk/hugo/heglk.cpp
@@ -0,0 +1,55 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "glk/hugo/hugo.h"
+
+namespace Glk {
+namespace Hugo {
+
+void Hugo::hugo_init_screen() {
+	// Open the main window...
+	mainwin = currentwin = glk_window_open(0, 0, 0, wintype_TextBuffer, 1);
+	assert(mainwin);
+
+	// ...and set it up for default output
+	glk_set_window(mainwin);
+
+	// By setting the width and height so high, we're basically forcing the Glk library
+	// to deal with text-wrapping and page ends
+	SCREENWIDTH = 0x7fff;
+	SCREENHEIGHT = 0x7fff;
+	FIXEDCHARWIDTH = 1;
+	FIXEDLINEHEIGHT = 1;
+
+	hugo_settextwindow(1, 1,
+		SCREENWIDTH/FIXEDCHARWIDTH, SCREENHEIGHT/FIXEDLINEHEIGHT);
+}
+
+void Hugo::hugo_cleanup_screen() {
+}
+
+void Hugo::hugo_settextwindow(int left, int top, int right, int bottom) {
+	// TODO
+}
+
+} // End of namespace Hugo
+} // End of namespace Glk
diff --git a/engines/glk/hugo/hugo.cpp b/engines/glk/hugo/hugo.cpp
index 5945d81..7884bc4 100644
--- a/engines/glk/hugo/hugo.cpp
+++ b/engines/glk/hugo/hugo.cpp
@@ -25,11 +25,57 @@
 namespace Glk {
 namespace Hugo {
 
-Hugo::Hugo(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gameDesc) {
+Hugo::Hugo(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gameDesc),
+		mainwin(nullptr), currentwin(nullptr), address_scale(16),
+		SCREENWIDTH(0), SCREENHEIGHT(0), FIXEDCHARWIDTH(0), FIXEDLINEHEIGHT(0),
+		game_version(0), object_size(0), game(nullptr), script(nullptr), save(nullptr),
+		playback(nullptr), record(nullptr), io(nullptr), ioblock('\0'), ioerror('\0'),
+		codestart(0), objtable(0), eventtable(0), proptable(0), arraytable(0), dicttable(0), 
+		syntable(0), initaddr(0), mainaddr(0), parseaddr(0), parseerroraddr(0), 
+		findobjectaddr(0), endgameaddr(0), speaktoaddr(0), performaddr(0), 
+		objects(0), events(0), dictcount(0), syncount(0), mem(nullptr), loaded_in_memory(0),
+		defseg(0), gameseg(0), codeptr(0), codeend(0), currentpos(0), currentline(0), full(0),
+		def_fcolor(0), def_bgcolor(0), def_slfcolor(0), def_slbgcolor(0), fcolor(0), bgcolor(0),
+		icolor(0), default_bgcolor(0), currentfont(0), capital(0), textto(0),
+		physical_windowwidth(0), physical_windowheight(0), physical_windowtop(0),
+		physical_windowleft(0), physical_windowbottom(0), physical_windowright(0),
+		inwindow(0), charwidth(0), lineheight(0), current_text_x(0), current_text_y(0),
+		undoptr(0), undoturn(0), undoinvalid(0), undorecord(0) {
+#if !defined (COMPILE_V25)
+	Common::fill(&context_command[0][0], &context_command[MAX_CONTEXT_COMMANDS][64], 0);
+	context_commands = 0;
+#endif
+
+	Common::fill(&id[0], &id[3], '\0');
+	Common::fill(&serial[0], &serial[9], '\0');
+	Common::fill(&pbuffer[0], &pbuffer[MAXBUFFER * 2 + 1], 0);
+	Common::fill(&undostack[0][0], &undostack[MAXUNDO][5], 0);
 }
 
+// TODO: Proper method implementations
+void SetupDisplay() {}
+void LoadGame() {}
+void PlayGame() {}
+void hugo_closefiles() {}
+
 void Hugo::runGame() {
+	hugo_init_screen();
+
+	SetupDisplay();
+
+	strcpy(pbuffer, "");
+
+	gameseg = 0;
+
+	LoadGame();
+
+	PlayGame();
+
+	hugo_cleanup_screen();
 
+	hugo_blockfree(mem);
+	mem = nullptr;
+	hugo_closefiles();
 }
 
 Common::Error Hugo::loadGameData(strid_t file) {
diff --git a/engines/glk/hugo/hugo.h b/engines/glk/hugo/hugo.h
index 7032bba..15d1711 100644
--- a/engines/glk/hugo/hugo.h
+++ b/engines/glk/hugo/hugo.h
@@ -34,6 +34,103 @@ namespace Hugo {
  * Hugo game interpreter
  */
 class Hugo : public GlkAPI {
+private:
+	winid_t mainwin, currentwin;
+
+	/**
+	 * address_scale refers to the factor by which addresses are multiplied to
+	 * get the "real" address.  In this way, a 16-bit integer can reference
+	 * 64K * 16 = 1024K of memory.
+	 */
+	int address_scale;
+
+	int game_version;
+	int object_size;
+	HUGO_FILE game;
+	HUGO_FILE script;
+	HUGO_FILE save;
+	HUGO_FILE playback;
+	HUGO_FILE record;
+	HUGO_FILE io; char ioblock; char ioerror;
+
+	char id[3];
+	char serial[9];
+	unsigned int codestart;
+	unsigned int objtable;
+	unsigned int eventtable;
+	unsigned int proptable;
+	unsigned int arraytable;
+	unsigned int dicttable;
+	unsigned int syntable;
+	unsigned int initaddr;
+	unsigned int mainaddr;
+	unsigned int parseaddr;
+	unsigned int parseerroraddr;
+	unsigned int findobjectaddr;
+	unsigned int endgameaddr;
+	unsigned int speaktoaddr;
+	unsigned int performaddr;
+	int objects;
+	int events;
+	int dictcount;
+	int syncount;
+#if !defined (COMPILE_V25)
+	char context_command[MAX_CONTEXT_COMMANDS][64];
+	int context_commands;
+#endif
+	unsigned char *mem;
+	int loaded_in_memory;
+	unsigned int defseg;
+	unsigned int gameseg;
+	long codeptr;
+	long codeend;
+	char pbuffer[MAXBUFFER * 2 + 1];
+	int currentpos;
+	int currentline;
+	int full;
+	signed char def_fcolor, def_bgcolor, def_slfcolor, def_slbgcolor;
+	signed char fcolor, bgcolor, icolor, default_bgcolor;
+	int currentfont;
+	char capital;
+	unsigned int textto;
+	int SCREENWIDTH, SCREENHEIGHT;
+	int physical_windowwidth, physical_windowheight,
+		physical_windowtop, physical_windowleft,
+		physical_windowbottom, physical_windowright;
+	int inwindow;
+	int charwidth, lineheight, FIXEDCHARWIDTH, FIXEDLINEHEIGHT;
+	int current_text_x, current_text_y;
+	int undostack[MAXUNDO][5];
+	int undoptr;
+	int undoturn;
+	char undoinvalid;
+	char undorecord;
+private:
+	/**
+	 * \defgroup heglk
+	 * @{
+	 */
+
+	 /**
+	  * Does whatever has to be done to initially set up the display
+	  */
+	void hugo_init_screen();
+
+	/**
+	 * Does whatever has to be done to clean up the display pre-termination
+	 */
+	void hugo_cleanup_screen();
+
+	void hugo_settextwindow(int left, int top, int right, int bottom);
+
+	/**@}*/
+private:
+	/**
+	 * Allocate memory block
+	 */
+	void *hugo_blockalloc(size_t num) { return new byte[num]; }
+
+	void hugo_blockfree(void *block) { delete[] block; }
 public:
 	/**
 	 * Constructor
diff --git a/engines/glk/hugo/hugo_types.h b/engines/glk/hugo/hugo_types.h
index 19e5396..3c06293 100644
--- a/engines/glk/hugo/hugo_types.h
+++ b/engines/glk/hugo/hugo_types.h
@@ -28,106 +28,56 @@
 namespace Glk {
 namespace Hugo {
 
-#define MAX_HINTS 260
-#define MAX_HCONTENTS 30000
+#define HUGO_FILE	strid_t
+#define MAX_CONTEXT_COMMANDS	32
+#define MAXBUFFER 255
+#define MAXUNDO 1024
 
-#define MAX_POSITIONS 20
-#define MAX_ANIMS 200
-#define MAX_FRAMES 20
-#define MAX_STRING_SIZE  0xFF00
-#define MAX_PICTURE_SIZE 0xC800
-#define MAX_MUSIC_SIZE   0x4E20
-#define MAX_HITEMS 25
-
-struct lookup {
-	int16 flag;
-	int16 count;
-
-	lookup() : flag(0), count(0) {}
-};
-
-struct picture {
-	byte * data;
-	uint32 data_size;
-	uint16 width;
-	uint16 height;
-	uint16 wbytes;
-	uint16 plane_step;
-	byte *mask;
-
-	picture() : data(nullptr), data_size(0), width(0), height(0), wbytes(0), plane_step(0),
-		mask(nullptr) {}
+/**
+ * Library/engine globals
+ */
+enum EngineGlobals {
+	object = 0,
+	xobject = 1,
+	self = 2,
+	wordcount = 3,
+	player = 4,
+	actor = 5,
+	location = 6,
+	verbroutine = 7,
+	endflag = 8,
+	prompt = 9,
+	objectcount = 10,
+	system_status = 11
 };
 
 /**
- * Hugo animated pictures support
- *
- * Note: Some of the pictures for Wonderland and the Collection Volume 1 games
- * are animations. To detect these, pass a pointer to a type8 as the is_anim
- * argument to ms_extract().
- *
- * There are two types of animated images, however almost all images are type1.
- * A type1 image consists of four main elements:
- * 1) A static picture which is loaded straight at the beginning
- * 2) A set of frames with a mask. These frames are just "small pictures", which
- *    are coded like the normal static pictures. The image mask determines
- *    how the frame is removed after it has been displayed. A mask is exactly
- *    1/8 the size of the image and holds 1 bit per pixel, saying "remove pixel"
- *    or leave pixel set when frame gets removed. It might be a good idea to check
- *    your system documentation for masking operations as your system might be
- *    able to use this mask data directly.
- * 3) Positioning tables. These hold animation sequences consisting of commands
- *    like "Draw frame 12 at (123,456)"
- * 4) A playback script, which determines how to use the positioning tables.
- *    These scripts are handled inside Hugo, so no need to worry about.
- *    However, details can be found in the ms_animate() function.
- *
- * A type2 image is like a type1 image, but it does not have a static
- * picture, nor does it have frame masking. It just consists of frames.
- *
- * How to support animations?
- * After getting is_anim == 1 you should call ms_animate() immediately, and at
- * regular intervals until ms_animate() returns 0. An appropriate interval
- * between calls is about 100 milliseconds.
- * Each call to ms_animate() will fill in the arguments with the address
- * and size of an array of ms_position structures (see below), each of
- * which holds an an animation frame number and x and y co-ordinates. To
- * display the animation, decode all the animation frames (discussed below)
- * from a single call to ms_animate() and display each one over the main picture.
- * If your port does not support animations, define NO_ANIMATION.
+ * Library/engine properties
  */
-struct ms_position {
-	int16 x, y;
-	int16 number;
-
-	ms_position() : x(0), y(0), number(0) {}
+enum EngineProperties {
+	before = 1,
+	after = 2,
+	noun = 3,
+	adjective = 4,
+	article = 5
 };
 
 /**
- * Hugo Windows hint support
- *
- * The windowed Hugo Scolls games included online hints. To add support
- * for the hints to your hugo port, you should implement the ms_showhints
- * function. It retrieves a pointer to an array of ms_hint structs
- * The root element is always hints[0]. The elcount determines the number
- * of items in this topic. You probably want to display those in some kind
- * of list interface. The content pointer points to the actual description of
- * the items, separated by '\0' terminators. The nodetype is 1 if the items are
- * "folders" and 2 if the items are hints. Hints should be displayed one after
- * another. For "folder" items, the links array holds the index of the hint in
- * the array which is to be displayed on selection. One hint block has exactly
- * one type. The parent element determines the "back" target.
+ * "display" object properties
  */
-struct ms_hint {
-	uint16 elcount;
-	uint16 nodetype;
-	byte *content;
-	uint16 links[MAX_HITEMS];
-	uint16 parent;
-
-	ms_hint() : elcount(0), nodetype(0), content(nullptr), parent(0) {
-		Common::fill(&links[0], &links[MAX_HITEMS], 0);
-	}
+enum ObjectProperties {
+	screenwidth = 1,
+	screenheight = 2,
+	linelength = 3,
+	windowlines = 4,
+	cursor_column = 5,
+	cursor_row = 6,
+	hasgraphics = 7,
+	title_caption = 8,
+	hasvideo = 9,
+	needs_repaint = 10,
+	pointer_x = 11,
+	pointer_y = 12
 };
 
 } // End of namespace Hugo
diff --git a/engines/glk/module.mk b/engines/glk/module.mk
index ea85350..dd12d23 100644
--- a/engines/glk/module.mk
+++ b/engines/glk/module.mk
@@ -72,6 +72,7 @@ MODULE_OBJS := \
 	glulxe/string.o \
 	glulxe/vm.o \
 	hugo/detection.o \
+	hugo/heglk.o \
 	hugo/hugo.o \
 	magnetic/detection.o \
 	magnetic/emu.o \


Commit: 19a9b9aa31cad3920bc258ebe79d1e1c9a7b537c
    https://github.com/scummvm/scummvm/commit/19a9b9aa31cad3920bc258ebe79d1e1c9a7b537c
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-05-10T12:11:05+10:00

Commit Message:
GLK: HUGO: heglk fields & initialization

Changed paths:
    engines/glk/hugo/heglk.cpp
    engines/glk/hugo/hugo.cpp
    engines/glk/hugo/hugo.h
    engines/glk/hugo/hugo_types.h


diff --git a/engines/glk/hugo/heglk.cpp b/engines/glk/hugo/heglk.cpp
index 1dd0935..8b56f32 100644
--- a/engines/glk/hugo/heglk.cpp
+++ b/engines/glk/hugo/heglk.cpp
@@ -45,6 +45,7 @@ void Hugo::hugo_init_screen() {
 }
 
 void Hugo::hugo_cleanup_screen() {
+	// No implementation
 }
 
 void Hugo::hugo_settextwindow(int left, int top, int right, int bottom) {
diff --git a/engines/glk/hugo/hugo.cpp b/engines/glk/hugo/hugo.cpp
index 7884bc4..3f63d3b 100644
--- a/engines/glk/hugo/hugo.cpp
+++ b/engines/glk/hugo/hugo.cpp
@@ -40,7 +40,9 @@ Hugo::Hugo(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gam
 		physical_windowwidth(0), physical_windowheight(0), physical_windowtop(0),
 		physical_windowleft(0), physical_windowbottom(0), physical_windowright(0),
 		inwindow(0), charwidth(0), lineheight(0), current_text_x(0), current_text_y(0),
-		undoptr(0), undoturn(0), undoinvalid(0), undorecord(0) {
+		undoptr(0), undoturn(0), undoinvalid(0), undorecord(0),
+		in_valid_window(false), glk_fcolor(DEF_FCOLOR), glk_bgcolor(DEF_BGCOLOR),
+		mainwin_bgcolor(0), glk_current_font(0), just_cleared_screen(false) {
 #if !defined (COMPILE_V25)
 	Common::fill(&context_command[0][0], &context_command[MAX_CONTEXT_COMMANDS][64], 0);
 	context_commands = 0;
diff --git a/engines/glk/hugo/hugo.h b/engines/glk/hugo/hugo.h
index 15d1711..f94931f 100644
--- a/engines/glk/hugo/hugo.h
+++ b/engines/glk/hugo/hugo.h
@@ -105,6 +105,12 @@ private:
 	int undoturn;
 	char undoinvalid;
 	char undorecord;
+
+	bool in_valid_window;
+	int glk_fcolor, glk_bgcolor;
+	int mainwin_bgcolor;
+	int glk_current_font;
+	bool just_cleared_screen;
 private:
 	/**
 	 * \defgroup heglk
diff --git a/engines/glk/hugo/hugo_types.h b/engines/glk/hugo/hugo_types.h
index 3c06293..8082842 100644
--- a/engines/glk/hugo/hugo_types.h
+++ b/engines/glk/hugo/hugo_types.h
@@ -28,10 +28,23 @@
 namespace Glk {
 namespace Hugo {
 
-#define HUGO_FILE	strid_t
 #define MAX_CONTEXT_COMMANDS	32
 #define MAXBUFFER 255
 #define MAXUNDO 1024
+#define CHARWIDTH 1
+
+#define HUGO_FILE	strid_t
+#define MAXPATH         256
+#define MAXFILENAME     256
+#define MAXDRIVE        256
+#define MAXDIR          256
+#define MAXEXT          256
+
+#define DEF_PRN         ""
+#define DEF_FCOLOR      0
+#define DEF_BGCOLOR     15
+#define DEF_SLFCOLOR	15
+#define DEF_SLBGCOLOR	1
 
 /**
  * Library/engine globals


Commit: 2765ba9afa4918e46ab0835c0f6e9183c57b612a
    https://github.com/scummvm/scummvm/commit/2765ba9afa4918e46ab0835c0f6e9183c57b612a
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-05-10T12:11:05+10:00

Commit Message:
GLK: HUGO: Added Glk interface methods

Changed paths:
  A engines/glk/hugo/hugo_defines.h
    engines/glk/hugo/heglk.cpp
    engines/glk/hugo/hugo.cpp
    engines/glk/hugo/hugo.h
    engines/glk/hugo/hugo_types.h


diff --git a/engines/glk/hugo/heglk.cpp b/engines/glk/hugo/heglk.cpp
index 8b56f32..86be90c 100644
--- a/engines/glk/hugo/heglk.cpp
+++ b/engines/glk/hugo/heglk.cpp
@@ -44,13 +44,578 @@ void Hugo::hugo_init_screen() {
 		SCREENWIDTH/FIXEDCHARWIDTH, SCREENHEIGHT/FIXEDLINEHEIGHT);
 }
 
-void Hugo::hugo_cleanup_screen() {
-	// No implementation
+void Hugo::hugo_getline(const char *prmpt) {
+	event_t ev;
+	char gotline = 0;
+
+	/* Just in case we try to get line input from a Glk-illegal
+	window that hasn't been created, switch as a failsafe
+	to mainwin
+	*/
+	if (currentwin == NULL)
+		glk_set_window(currentwin = mainwin);
+
+	/* Print prompt */
+	glk_put_string(prmpt);
+
+	/* Request line input */
+	glk_request_line_event(currentwin, buffer, MAXBUFFER, 0);
+
+	while (!gotline)
+	{
+		/* Grab an event */
+		glk_select(&ev);
+
+		switch (ev.type)
+		{
+		case evtype_LineInput:
+			/* (Will always be currentwin, but anyway) */
+			if (ev.window == currentwin) {
+				gotline = true;
+			}
+			break;
+		}
+	}
+
+	/* The line we have received in commandbuf is not null-terminated */
+	buffer[ev.val1] = '\0';	/* i.e., the length */
+
+							/* Copy the input to the script file (if open) */
+	if (script) {
+		Common::String line = Common::String::format("%s%s\n", prompt, buffer);
+		script->putBuffer(line.c_str(), line.size());
+	}
+}
+
+int Hugo::hugo_waitforkey() {
+	event_t ev;
+	char gotchar = 0;
+
+	/* Just in case we try to get key input from a Glk-illegal
+	window that hasn't been created, switch as a failsafe
+	to mainwin
+	*/
+	if (currentwin == NULL)
+		glk_set_window(currentwin = mainwin);
+
+#if defined (NO_KEYPRESS_CURSOR)
+	if (currentwin != mainwin)
+	{
+		glk_window_move_cursor(currentwin, currentpos / CHARWIDTH, currentline - 1);
+		hugo_print("*");
+		glk_window_move_cursor(currentwin, currentpos / CHARWIDTH, currentline - 1);
+	}
+#endif
+
+	glk_request_char_event(currentwin);
+
+	while (!gotchar)
+	{
+		/* Grab an event */
+		glk_select(&ev);
+
+		switch (ev.type)
+		{
+		case evtype_CharInput:
+			/* (Will always be mainwin, but anyway) */
+			if (ev.window == currentwin) {
+				gotchar = true;
+			}
+			break;
+		}
+	}
+
+	/* Convert Glk special keycodes: */
+	switch (ev.val1)
+	{
+	case keycode_Left:	ev.val1 = 8;	break;
+	case keycode_Right:	ev.val1 = 21;	break;
+	case keycode_Up:	ev.val1 = 11;	break;
+	case keycode_Down:	ev.val1 = 10;	break;
+	case keycode_Return:	ev.val1 = 13;	break;
+	case keycode_Escape:	ev.val1 = 27;	break;
+	}
+
+#if defined (NO_KEYPRESS_CURSOR)
+	if (currentwin != mainwin)
+	{
+		glk_window_move_cursor(currentwin, currentpos / CHARWIDTH, currentline - 1);
+		hugo_print(" ");
+		glk_window_move_cursor(currentwin, currentpos / CHARWIDTH, currentline - 1);
+	}
+#endif
+
+	return ev.val1;
+}
+
+int Hugo::hugo_iskeywaiting() {
+	var[system_status] = STAT_UNAVAILABLE;
+	return 0;
+}
+
+int Hugo::hugo_timewait(int n) {
+	uint32 millisecs;
+	event_t ev;
+
+	if (!glk_gestalt(gestalt_Timer, 0))
+		return false;
+	if (n == 0) return true;
+
+
+	millisecs = 1000 / n;
+	if (millisecs == 0)
+		millisecs = 1;
+
+	// For the time being, we're going to disallow
+	// millisecond delays in Glk (1) because there's no
+	// point, and (2) so that we can tell we're running
+	// under Glk.
+	if (millisecs < 1000) return false;
+
+	glk_request_timer_events(millisecs);
+	while (1)
+	{
+		glk_select(&ev);
+		if (ev.type == evtype_Timer)
+			break;
+	}
+	glk_request_timer_events(0);
+	return true;
+}
+
+void Hugo::hugo_clearfullscreen() {
+	glk_window_clear(mainwin);
+	if (secondwin) glk_window_clear(secondwin);
+	if (auxwin) glk_window_clear(auxwin);
+
+	/* See hugo_print() for the need for this */
+	if (currentwin == mainwin) mainwin_bgcolor = glk_bgcolor;
+
+	/* Must be set: */
+	currentpos = 0;
+	currentline = 1;
+
+	if (!inwindow) just_cleared_screen = true;
+}
+
+void Hugo::hugo_clearwindow() {
+	/* Clears the currently defined window, moving the cursor to the top-left
+	corner of the window */
+
+	/* If the engine thinks we're in a window, but Glk was
+	unable to comply, don't clear the window, because it's
+	not really a window
+	*/
+	if (inwindow && currentwin == mainwin) return;
+	if (currentwin == NULL) return;
+
+	glk_window_clear(currentwin);
+
+	/* See hugo_print() for the need for this */
+	if (currentwin == mainwin) mainwin_bgcolor = glk_bgcolor;
+
+	/* If we're in a fixed-font (i.e., textgrid) auxiliary
+	window when we call for a clear, close auxwin and reset
+	the current window to mainwin
+	*/
+	if (auxwin)
+	{
+		stream_result_t sr;
+
+		glk_window_close(auxwin, &sr);
+		auxwin = NULL;
+		glk_set_window(currentwin = mainwin);
+	}
+
+	/* Must be set: */
+	currentpos = 0;
+	currentline = 1;
+
+	if (!inwindow) just_cleared_screen = true;
+}
+
+void Hugo::hugo_settextmode() {
+	charwidth = FIXEDCHARWIDTH;
+	lineheight = FIXEDLINEHEIGHT;
 }
 
 void Hugo::hugo_settextwindow(int left, int top, int right, int bottom) {
-	// TODO
+	/* Hugo's arbitrarily positioned windows don't currently
+	mesh with what Glk has to offer, so we have to ignore any
+	non-Glk-ish Windows and just maintain the current
+	parameters
+	*/
+	if ((top != 1 || bottom >= physical_windowbottom / FIXEDLINEHEIGHT + 1)
+		/* Pre-v2.4 didn't support proper windowing */
+		&& (game_version >= 24 || !inwindow))
+	{
+		in_valid_window = false;
+
+		/* Glk-illegal floating window; setting currentwin
+		to NULL will tell hugo_print() not to print in it:
+		*/
+		if (bottom<physical_windowbottom / FIXEDLINEHEIGHT + 1)
+		{
+			currentwin = NULL;
+			glk_set_window(mainwin);
+			return;
+		}
+		else
+			glk_set_window(currentwin = mainwin);
+	}
+
+	/* Otherwise this is a valid window (positioned along the
+	top of the screen a la a status window), so... */
+	else
+	{
+		/* Arbitrary height of 4 lines for pre-v2.4 windows */
+		if (game_version < 24) bottom = 4;
+
+		/* ...either create a new window if none exists... */
+		if (!secondwin)
+		{
+			glk_stylehint_set(wintype_TextGrid, style_Normal, stylehint_ReverseColor, 1);
+			glk_stylehint_set(wintype_TextGrid, style_Subheader, stylehint_ReverseColor, 1);
+			glk_stylehint_set(wintype_TextGrid, style_Emphasized, stylehint_ReverseColor, 1);
+
+			winid_t p;
+
+			p = glk_window_get_parent(mainwin);
+			secondwin = glk_window_open(mainwin,//p,
+				winmethod_Above | winmethod_Fixed,
+				bottom,
+				wintype_TextGrid,
+				0);
+		}
+
+		/* ...or resize the existing one if necessary */
+		else if (bottom != secondwin_bottom)
+		{
+			winid_t p;
+
+			p = glk_window_get_parent(secondwin);
+			glk_window_set_arrangement(p,
+				winmethod_Above | winmethod_Fixed,
+				bottom,
+				secondwin);
+		}
+
+		if (secondwin)
+		{
+			if (game_version < 24)
+				glk_window_clear(secondwin);
+
+			glk_set_window(currentwin = secondwin);
+			in_valid_window = true;
+			secondwin_bottom = bottom;
+		}
+		else
+		{
+			currentwin = NULL;
+			glk_set_window(mainwin);
+			secondwin_bottom = 0;
+			return;
+		}
+	}
+
+	physical_windowleft = (left - 1)*FIXEDCHARWIDTH;
+	physical_windowtop = (top - 1)*FIXEDLINEHEIGHT;
+	physical_windowright = right*FIXEDCHARWIDTH - 1;
+	physical_windowbottom = bottom*FIXEDLINEHEIGHT - 1;
+	physical_windowwidth = (right - left + 1)*FIXEDCHARWIDTH;
+	physical_windowheight = (bottom - top + 1)*FIXEDLINEHEIGHT;
+}
+
+int Hugo::heglk_get_linelength() {
+	static uint32 width;
+
+	// Try to use whatever fixed-width linelength is available
+	if (secondwin)
+		glk_window_get_size(secondwin, &width, NULL);
+	else if (auxwin)
+		glk_window_get_size(auxwin, &width, NULL);
+
+	// Otherwise try to approximate it by the proportionally spaced linelength
+	else
+		glk_window_get_size(mainwin, &width, NULL);
+
+	// -1 to override automatic line wrapping
+	return width - 1;
+}
+
+int Hugo::heglk_get_screenheight() {
+	static uint32 height = 0, mainheight = 0;
+
+	if (secondwin)
+		glk_window_get_size(secondwin, NULL, &height);
+	else if (auxwin)
+		glk_window_get_size(auxwin, NULL, &height);
+
+	glk_window_get_size(mainwin, NULL, &mainheight);
+
+	return height + mainheight;
+}
+
+void Hugo::hugo_settextpos(int x, int y) {
+	if (currentwin == NULL) return;
+
+	// Try to determine if we're trying to position fixed-width text in the main window,
+	// as in a menu, for example
+	if (!just_cleared_screen && !inwindow &&
+		!(glk_current_font & PROP_FONT)
+		&& y != 1			/* not just cls */
+		&& y < SCREENHEIGHT - 0x0f)	/* 0x0f is arbitrary */
+	{
+		/* See if we're already in the auxiliary window */
+		if (currentwin != auxwin)
+		{
+			/* If not, create it, making it 100% of
+			mainwin's height
+			*/
+			if (auxwin == NULL)
+			{
+				auxwin = glk_window_open(mainwin,
+					winmethod_Below | winmethod_Proportional,
+					100,
+					wintype_TextGrid,
+					0);
+			}
+			else
+				glk_window_clear(auxwin);
+
+			glk_set_window(currentwin = auxwin);
+		}
+	}
+
+	/* On the other hand, if we were in a textgrid window and
+	no longer need to be, get out
+	*/
+	else if (auxwin)
+	{
+		stream_result_t sr;
+
+		/* Close auxwin */
+		glk_window_close(auxwin, &sr);
+		auxwin = NULL;
+
+		/* Clear the screen (both windows) */
+		glk_window_clear(mainwin);
+		glk_window_clear(secondwin);
+
+		glk_set_window(currentwin = mainwin);
+	}
+
+	just_cleared_screen = false;
+
+	/* Can only move the Glk cursor in a textgrid window */
+	if (currentwin != mainwin)
+		glk_window_move_cursor(currentwin, x - 1, y - 1);
+
+	/* Must be set: */
+	currentline = y;
+	currentpos = (x - 1)*CHARWIDTH;   /* Note:  zero-based */
+}
+
+void Hugo::hugo_print(const char *a) {
+	static char just_printed_linefeed = false;
+	/* static already_modified_style = false; */
+
+	/* Can't print in a Glk-illegal window since it hasn't been
+	created
+	*/
+	if (currentwin == NULL) return;
+
+	/* In lieu of colors, in case we're highlighting something
+	such as a menu selection:
+	*/
+	/*
+	if (!inwindow and glk_bgcolor!=mainwin_bgcolor)
+	{
+	if (!already_modified_style)
+	{
+	if (glk_current_font & BOLD_FONT)
+	glk_set_style(style_Normal);
+	else
+	glk_set_style(style_Emphasized);
+	}
+	already_modified_style = true;
+	}
+	else
+	already_modified_style = false;
+	*/
+
+	if (a[0] == '\n')
+	{
+		if (!just_printed_linefeed)
+		{
+			glk_put_string("\n");
+		}
+		else
+			just_printed_linefeed = false;
+	}
+	else if (a[0] == '\r')
+	{
+		if (!just_printed_linefeed)
+		{
+			glk_put_string("\n");
+			just_printed_linefeed = true;
+		}
+		else
+			just_printed_linefeed = false;
+	}
+	else
+	{
+		glk_put_string(a);
+		just_printed_linefeed = false;
+	}
+}
+
+void Hugo::hugo_font(int f) {
+	static char using_prop_font = false;
+
+	glk_current_font = f;
+
+	glk_set_style(style_Normal);
+
+	if (f & BOLD_FONT)
+		glk_set_style(style_Subheader);
+
+	if (f & UNDERLINE_FONT)
+		glk_set_style(style_Emphasized);
+
+	if (f & ITALIC_FONT)
+		glk_set_style(style_Emphasized);
+
+	if (f & PROP_FONT)
+		using_prop_font = true;
+
+	/* Have to comment this out, it seems, because it will mess up the
+	alignment of the input in the main window
+	if (!(f & PROP_FONT))
+	glk_set_style(style_Preformatted);
+	*/
+
+	/* Workaround to decide if we have to open auxwin for positioned
+	non-proportional text:
+	*/
+	if (!(f & PROP_FONT))
+	{
+		/* If at top of screen, and changing to a fixed-
+		width font (a situation which wouldn't normally
+		be adjusted for by hugo_settextpos())
+		*/
+		if (!inwindow && currentline == 1 && currentpos == 0 && using_prop_font)
+		{
+			just_cleared_screen = false;
+			hugo_settextpos(1, 2);
+			glk_window_move_cursor(currentwin, 0, 0);
+		}
+	}
+}
+
+void Hugo::hugo_settextcolor(int c) {
+	// Set the foreground color to hugo_color(c)
+	glk_fcolor = hugo_color(c);
 }
 
+void Hugo::hugo_setbackcolor(int c) {
+	// Set the background color to hugo_color(c)
+	glk_bgcolor = hugo_color(c);
+}
+
+int Hugo::hugo_color(int c) {
+	if (c == 16)      c = DEF_FCOLOR;
+	else if (c == 17) c = DEF_BGCOLOR;
+	else if (c == 18) c = DEF_SLFCOLOR;
+	else if (c == 19) c = DEF_SLBGCOLOR;
+	else if (c == 20) c = hugo_color(fcolor);	/* match foreground */
+
+												/* Uncomment this block of code and change "c = ..." values if the system
+												palette differs from the Hugo palette.
+
+												If colors are unavailable on the system in question, it may suffice
+												to have black, white, and brightwhite (i.e. boldface).  It is expected
+												that colored text will be visible on any other-colored background.
+
+												switch (c)
+												{
+												case HUGO_BLACK:	 c = 0;  break;
+												case HUGO_BLUE:		 c = 1;  break;
+												case HUGO_GREEN:	 c = 2;  break;
+												case HUGO_CYAN:		 c = 3;  break;
+												case HUGO_RED:		 c = 4;  break;
+												case HUGO_MAGENTA:	 c = 5;  break;
+												case HUGO_BROWN:	 c = 6;  break;
+												case HUGO_WHITE:	 c = 7;  break;
+												case HUGO_DARK_GRAY:	 c = 8;  break;
+												case HUGO_LIGHT_BLUE:	 c = 9;  break;
+												case HUGO_LIGHT_GREEN:	 c = 10; break;
+												case HUGO_LIGHT_CYAN:	 c = 11; break;
+												case HUGO_LIGHT_RED:	 c = 12; break;
+												case HUGO_LIGHT_MAGENTA: c = 13; break;
+												case HUGO_YELLOW:	 c = 14; break;
+												case HUGO_BRIGHT_WHITE:	 c = 15; break;
+												*/
+	return c;
+}
+
+int Hugo::hugo_charwidth(char a) const {
+	if (a == FORCED_SPACE)
+		return CHARWIDTH;         /* same as ' ' */
+
+	else if ((unsigned char)a >= ' ') /* alphanumeric characters */
+
+		return CHARWIDTH;         /* for non-proportional */
+
+	return 0;
+}
+
+int Hugo::hugo_textwidth(const char *a) const {
+	int i, slen, len = 0;
+
+	slen = (int)strlen(a);
+
+	for (i = 0; i<slen; i++)
+	{
+		if (a[i] == COLOR_CHANGE) i += 2;
+		else if (a[i] == FONT_CHANGE) i++;
+		else
+			len += hugo_charwidth(a[i]);
+	}
+
+	return len;
+}
+
+int Hugo::hugo_strlen(const char *a) const {
+	int i, slen, len = 0;
+
+	slen = (int)strlen(a);
+
+	for (i = 0; i<slen; i++)
+	{
+		if (a[i] == COLOR_CHANGE) i += 2;
+		else if (a[i] == FONT_CHANGE) i++;
+		else len++;
+	}
+
+	return len;
+}
+
+/*
+ * Replacements for things the Glk port doesn't support:
+ *
+ */
+
+void Hugo::hugo_setgametitle(const char *t) {}
+
+int Hugo::hugo_hasvideo() const { return false; }
+
+int Hugo::hugo_playvideo(HUGO_FILE infile, long reslength, char loop_flag, char background, int volume) {
+	delete infile;
+	return true;
+}
+
+void Hugo::hugo_stopvideo(void) {}
+
 } // End of namespace Hugo
 } // End of namespace Glk
diff --git a/engines/glk/hugo/hugo.cpp b/engines/glk/hugo/hugo.cpp
index 3f63d3b..afc788e 100644
--- a/engines/glk/hugo/hugo.cpp
+++ b/engines/glk/hugo/hugo.cpp
@@ -26,7 +26,7 @@ namespace Glk {
 namespace Hugo {
 
 Hugo::Hugo(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gameDesc),
-		mainwin(nullptr), currentwin(nullptr), address_scale(16),
+		mainwin(nullptr), currentwin(nullptr), secondwin(nullptr), auxwin(nullptr), address_scale(16),
 		SCREENWIDTH(0), SCREENHEIGHT(0), FIXEDCHARWIDTH(0), FIXEDLINEHEIGHT(0),
 		game_version(0), object_size(0), game(nullptr), script(nullptr), save(nullptr),
 		playback(nullptr), record(nullptr), io(nullptr), ioblock('\0'), ioerror('\0'),
@@ -40,18 +40,16 @@ Hugo::Hugo(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gam
 		physical_windowwidth(0), physical_windowheight(0), physical_windowtop(0),
 		physical_windowleft(0), physical_windowbottom(0), physical_windowright(0),
 		inwindow(0), charwidth(0), lineheight(0), current_text_x(0), current_text_y(0),
-		undoptr(0), undoturn(0), undoinvalid(0), undorecord(0),
+		undoptr(0), undoturn(0), undoinvalid(0), undorecord(0), context_commands(0),
 		in_valid_window(false), glk_fcolor(DEF_FCOLOR), glk_bgcolor(DEF_BGCOLOR),
-		mainwin_bgcolor(0), glk_current_font(0), just_cleared_screen(false) {
-#if !defined (COMPILE_V25)
+		mainwin_bgcolor(0), glk_current_font(0), just_cleared_screen(false), secondwin_bottom(0) {
 	Common::fill(&context_command[0][0], &context_command[MAX_CONTEXT_COMMANDS][64], 0);
-	context_commands = 0;
-#endif
-
 	Common::fill(&id[0], &id[3], '\0');
 	Common::fill(&serial[0], &serial[9], '\0');
 	Common::fill(&pbuffer[0], &pbuffer[MAXBUFFER * 2 + 1], 0);
 	Common::fill(&undostack[0][0], &undostack[MAXUNDO][5], 0);
+	Common::fill(&buffer[0], &buffer[MAXBUFFER + MAXWORDS], '\0');
+	Common::fill(&var[0], &var[MAXLOCALS + MAXGLOBALS], 0);
 }
 
 // TODO: Proper method implementations
diff --git a/engines/glk/hugo/hugo.h b/engines/glk/hugo/hugo.h
index f94931f..7d12646 100644
--- a/engines/glk/hugo/hugo.h
+++ b/engines/glk/hugo/hugo.h
@@ -25,6 +25,7 @@
 
 #include "common/scummsys.h"
 #include "glk/glk_api.h"
+#include "glk/hugo/hugo_defines.h"
 #include "glk/hugo/hugo_types.h"
 
 namespace Glk {
@@ -36,6 +37,7 @@ namespace Hugo {
 class Hugo : public GlkAPI {
 private:
 	winid_t mainwin, currentwin;
+	winid_t secondwin, auxwin;
 
 	/**
 	 * address_scale refers to the factor by which addresses are multiplied to
@@ -74,10 +76,8 @@ private:
 	int events;
 	int dictcount;
 	int syncount;
-#if !defined (COMPILE_V25)
 	char context_command[MAX_CONTEXT_COMMANDS][64];
 	int context_commands;
-#endif
 	unsigned char *mem;
 	int loaded_in_memory;
 	unsigned int defseg;
@@ -111,6 +111,13 @@ private:
 	int mainwin_bgcolor;
 	int glk_current_font;
 	bool just_cleared_screen;
+	int secondwin_bottom;
+
+	// heparse
+	char buffer[MAXBUFFER + MAXWORDS];
+
+	// heexpr
+	int var[MAXLOCALS + MAXGLOBALS];
 private:
 	/**
 	 * \defgroup heglk
@@ -125,10 +132,133 @@ private:
 	/**
 	 * Does whatever has to be done to clean up the display pre-termination
 	 */
-	void hugo_cleanup_screen();
+	void hugo_cleanup_screen() {
+		// No implementation
+	}
+
+	int hugo_getkey() const {
+		// Not needed here--single-character events are handled solely by hugo_waitforkey(), below
+		return 0;
+	}
+
+	/**
+	 * Gets a line of input from the keyboard, storing it in <buffer>.
+	 */
+	void hugo_getline(const char *prmpt);
+
+	/**
+	 * Provided to be replaced by multitasking systems where cycling while waiting
+	 * for a keystroke may not be such a hot idea.
+	 */
+	int hugo_waitforkey();
+
+	/**
+	 * Returns true if a keypress is waiting to be retrieved.
+	 */
+	int hugo_iskeywaiting();
+
+	/**
+	 * Waits for 1/n seconds.  Returns false if waiting is unsupported.
+	 */
+	int hugo_timewait(int n);
+
+	/**
+	 * Clears everything on the screen, moving the cursor to the top-left corner of the screen
+	 */
+	void hugo_clearfullscreen();
+
+	/**
+	 * Clears the currently defined window, moving the cursor to the top-left corner of the window
+	 */
+	void hugo_clearwindow();
+
+	/**
+	 * This function does whatever is necessary to set the system up for a standard text display
+	 */
+	void hugo_settextmode();
 
 	void hugo_settextwindow(int left, int top, int right, int bottom);
 
+	/**
+	 * Specially accommodated in GetProp() While the engine thinks that the linelength is 0x7fff,
+	this tells things like the display object the actual length.  (Defined as ACTUAL_LINELENGTH)
+	*/
+	int heglk_get_linelength();
+
+	/**
+	 * Similar to heglk_get_linelength().  (Defined as ACTUAL_SCREENHEIGHT)
+	 */
+	int heglk_get_screenheight();
+
+	void hugo_settextpos(int x, int y);
+
+	/**
+	 * Essentially the same as printf() without formatting, since printf() generally doesn't take
+	 * into account color setting, font changes, windowing, etc.
+	 *
+	 * The newline character '\n' must be explicitly included at the end of a line in order to
+	 * produce a linefeed.  The new cursor position is set to the end of this printed text.
+	 * Upon hitting the right edge of the screen, the printing position wraps to the start
+	 * of the next line.
+	 */
+	void hugo_print(const char *a);
+
+	/**
+	 * Scroll the text window
+	 */
+	void hugo_scrollwindowup() {
+		// No implementation. Glk takes care of it
+	}
+
+	/**
+	 * Set the font
+	 * @param f		The <f> argument is a mask containing any or none of:
+	 *				BOLD_FONT, UNDERLINE_FONT, ITALIC_FONT, PROP_FONT.
+	 */
+	void hugo_font(int f);
+
+	/**
+	 * Set the foreground (print) color
+	 */
+	void hugo_settextcolor(int c);
+
+	/**
+	 * Set the background color
+	 */
+	void hugo_setbackcolor(int c);
+
+	/**
+	 * Color-setting functions should always pass the color through hugo_color()
+	 * in order to properly set default fore/background colors
+	 */
+	int hugo_color(int c);
+
+	/**
+	 * Get the width of a character
+	 * @remarks		As given here, this function works only for non-proportional printing.
+	 * For proportional printing, hugo_charwidth() should return the width of the supplied
+	 * character in the current font and style.
+	*/
+	int hugo_charwidth(char a) const;
+
+	/**
+	 * Return the width of a string
+	 */
+	int hugo_textwidth(const char *a) const;
+
+	/**
+	 * Return the length of a string
+	 */
+	int hugo_strlen(const char *a) const;
+
+	void hugo_setgametitle(const char *t);
+
+	int hugo_hasvideo() const;
+
+	int hugo_playvideo(HUGO_FILE infile, long reslength, char loop_flag, char background, int volume);
+
+	void hugo_stopvideo();
+
 	/**@}*/
 private:
 	/**
diff --git a/engines/glk/hugo/hugo_defines.h b/engines/glk/hugo/hugo_defines.h
new file mode 100644
index 0000000..caa1755
--- /dev/null
+++ b/engines/glk/hugo/hugo_defines.h
@@ -0,0 +1,127 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef GLK_HUGO_DEFINES
+#define GLK_HUGO_DEFINES
+
+#include "common/scummsys.h"
+
+namespace Glk {
+namespace Hugo {
+
+#define MAX_CONTEXT_COMMANDS	32
+#define MAXBUFFER 255
+#define MAXUNDO 1024
+#define CHARWIDTH 1
+#define STAT_UNAVAILABLE (-1)
+
+#define HUGO_FILE	strid_t
+#define MAXPATH         256
+#define MAXFILENAME     256
+#define MAXDRIVE        256
+#define MAXDIR          256
+#define MAXEXT          256
+
+#define DEF_PRN         ""
+#define DEF_FCOLOR      0
+#define DEF_BGCOLOR     15
+#define DEF_SLFCOLOR	15
+#define DEF_SLBGCOLOR	1
+
+/* These static values are not changeable--they depend largely on internals of the Engine. */
+#define MAXATTRIBUTES    128
+#define MAXGLOBALS       240
+#define MAXLOCALS         16
+#define MAXPOBJECTS      256    /* contenders for disambiguation */
+#define MAXWORDS          32    /* in an input line              */
+#define MAXSTACKDEPTH    256	/* for nesting {...}		 */
+
+
+/* The positions of various data in the header: */
+#define H_GAMEVERSION	0x00
+#define H_ID		0x01
+#define H_SERIAL	0x03
+#define H_CODESTART	0x0B
+
+#define H_OBJTABLE	0x0D           /* data tables */
+#define H_PROPTABLE	0x0F
+#define H_EVENTTABLE	0x11
+#define H_ARRAYTABLE	0x13
+#define H_DICTTABLE	0x15
+#define H_SYNTABLE	0x17
+
+#define H_INIT		0x19           /* junction routines */
+#define H_MAIN		0x1B
+#define H_PARSE		0x1D
+#define H_PARSEERROR	0x1F
+#define H_FINDOBJECT	0x21
+#define H_ENDGAME	0x23
+#define H_SPEAKTO	0x25
+#define H_PERFORM	0x27
+
+#define H_TEXTBANK	0x29
+
+/* additional debugger header information */
+#define H_DEBUGGABLE     0x3A
+#define H_DEBUGDATA      0x3B
+#define H_DEBUGWORKSPACE 0x3E
+
+/* Printing control codes--embedded in strings printed by AP(). */
+#define FONT_CHANGE       1
+#define COLOR_CHANGE      2
+#define NO_CONTROLCHAR    3
+#define NO_NEWLINE        30
+#define FORCED_SPACE      31	/* Can't be <= # colors/font codes + 1
+(See AP() for the reason) */
+
+/* Font control codes--these bitmasks follow FONT_CHANGE codes. */
+#define NORMAL_FONT	  0
+#define BOLD_FONT         1
+#define ITALIC_FONT       2
+#define UNDERLINE_FONT    4
+#define PROP_FONT         8
+
+/* CHAR_TRANSLATION is simply a value that is added to an ASCII character
+in order to encode the text, i.e., make it unreadable to casual
+browsing.
+*/
+#define CHAR_TRANSLATION  0x14
+
+/* Passed to GetWord() */
+#define PARSE_STRING_VAL  0xFFF0
+#define SERIAL_STRING_VAL 0xFFF1
+
+/* Returned by FindWord() */
+#define UNKNOWN_WORD      0xFFFF
+
+/* Bitmasks for certain qualities of properties */
+#define ADDITIVE_FLAG   1
+#define COMPLEX_FLAG    2
+
+/* Property-table indicators */
+#define PROP_END          255
+#define PROP_ROUTINE      255
+
+} // End of namespace Hugo
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/hugo/hugo_types.h b/engines/glk/hugo/hugo_types.h
index 8082842..43b2899 100644
--- a/engines/glk/hugo/hugo_types.h
+++ b/engines/glk/hugo/hugo_types.h
@@ -28,24 +28,6 @@
 namespace Glk {
 namespace Hugo {
 
-#define MAX_CONTEXT_COMMANDS	32
-#define MAXBUFFER 255
-#define MAXUNDO 1024
-#define CHARWIDTH 1
-
-#define HUGO_FILE	strid_t
-#define MAXPATH         256
-#define MAXFILENAME     256
-#define MAXDRIVE        256
-#define MAXDIR          256
-#define MAXEXT          256
-
-#define DEF_PRN         ""
-#define DEF_FCOLOR      0
-#define DEF_BGCOLOR     15
-#define DEF_SLFCOLOR	15
-#define DEF_SLBGCOLOR	1
-
 /**
  * Library/engine globals
  */


Commit: 88300636350e18eced9d27691980705f47d2a43c
    https://github.com/scummvm/scummvm/commit/88300636350e18eced9d27691980705f47d2a43c
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-05-10T12:11:06+10:00

Commit Message:
GLK: HUGO: Added hemisc, htokens, and stringfn files

Changed paths:
  A engines/glk/hugo/hemisc.cpp
  A engines/glk/hugo/htokens.cpp
  A engines/glk/hugo/htokens.h
  A engines/glk/hugo/stringfn.cpp
  A engines/glk/hugo/stringfn.h
    engines/glk/hugo/heglk.cpp
    engines/glk/hugo/hugo.cpp
    engines/glk/hugo/hugo.h
    engines/glk/hugo/hugo_defines.h
    engines/glk/hugo/hugo_types.h
    engines/glk/module.mk


diff --git a/engines/glk/hugo/heglk.cpp b/engines/glk/hugo/heglk.cpp
index 86be90c..1d4586c 100644
--- a/engines/glk/hugo/heglk.cpp
+++ b/engines/glk/hugo/heglk.cpp
@@ -82,8 +82,8 @@ void Hugo::hugo_getline(const char *prmpt) {
 
 							/* Copy the input to the script file (if open) */
 	if (script) {
-		Common::String line = Common::String::format("%s%s\n", prompt, buffer);
-		script->putBuffer(line.c_str(), line.size());
+		Common::String text = Common::String::format("%s%s\n", prompt, buffer);
+		script->putBuffer(text.c_str(), text.size());
 	}
 }
 
diff --git a/engines/glk/hugo/hemisc.cpp b/engines/glk/hugo/hemisc.cpp
new file mode 100644
index 0000000..ac3a0d4
--- /dev/null
+++ b/engines/glk/hugo/hemisc.cpp
@@ -0,0 +1,1899 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "glk/hugo/hugo.h"
+
+namespace Glk {
+namespace Hugo {
+
+void Hugo::AP(const char *a) {
+		char sticky = false, skipspchar = false, startofline = 0;
+		int i, alen, plen, cwidth;
+		char c = 0;			/* current character */
+#ifdef USE_SMARTFORMATTING
+		char lastc = 0;			/* for smart formatting */
+#endif
+
+		static int lastfcolor = 16, lastbgcolor = 17;
+		static int lastfont = NORMAL_FONT;
+		static int thisline = 0;	/* width in pixels or characters */
+		static int linebreaklen = 0, linebreak = 0;
+		int tempfont;
+		char printed_something = false;
+#ifdef USE_TEXTBUFFER
+		int bufferfont = currentfont;
+#endif
+
+		/* Shameless little trick to override control characters in engine-
+		printed text, such as MS-DOS filenames that contain '\'s:
+		*/
+		if (a[0] == NO_CONTROLCHAR)
+		{
+			skipspchar = true;
+			a++;
+		}
+
+		/* Semi-colon overrides LF */
+		if ((strlen(a) >= 2) && a[strlen(a) - 1] == ';' && a[strlen(a) - 2] == '\\')
+		{
+			sticky = true;
+		}
+
+		if (hugo_strlen(pbuffer))
+			printed_something = true;
+
+		plen = strlen(pbuffer);
+		if (plen == 0)
+		{
+			thisline = 0;
+			linebreak = 0;
+			linebreaklen = 0;
+			lastfont = currentfont;
+			startofline = true;
+#ifdef USE_TEXTBUFFER
+			bufferbreak = 0;
+			bufferbreaklen = 0;
+#endif
+#ifdef USE_SMARTFORMATTING
+			leftquote = true;
+#endif
+		}
+
+		/* Check for color changes */
+		if ((a[0]) && (lastfcolor != fcolor || lastbgcolor != bgcolor || startofline))
+		{
+			if (plen >= MAXBUFFER * 2 - 3) FatalError(OVERFLOW_E);
+			pbuffer[plen++] = COLOR_CHANGE;
+			pbuffer[plen++] = (char)(fcolor + 1);
+			pbuffer[plen++] = (char)(bgcolor + 1);
+			pbuffer[plen] = '\0';
+			lastfcolor = fcolor;
+			lastbgcolor = bgcolor;
+		}
+
+		/* Check for font changes--since fonts can only get changed
+		by printing, we don't check lastfont */
+		if ((a[0]) && startofline)
+		{
+			if (plen >= MAXBUFFER * 2 - 2) FatalError(OVERFLOW_E);
+			pbuffer[plen++] = FONT_CHANGE;
+			pbuffer[plen++] = (char)(currentfont + 1);
+			pbuffer[plen] = '\0';
+			lastfont = currentfont;
+		}
+
+		/* Begin by looping through the entire provided string: */
+
+		alen = (int)strlen(a);
+		if (sticky)
+			alen -= 2;
+
+		/* Not printing any actual text, so we won't need to go through
+		queued font changes
+		*/
+		if (alen == 0)
+			lastfont = currentfont;
+
+		for (i = 0; i<alen; i++)
+		{
+			c = a[i];
+
+			/* Left-justification */
+			if (thisline == 0 && c == ' ' && !textto && currentpos == 0)
+				continue;
+
+			/* First check control characters */
+			if (c == '\\' && !skipspchar)
+			{
+				c = a[++i];
+
+				switch (c)
+				{
+				case 'n':
+				{
+					c = '\n';
+					break;
+				}
+				case 'B':
+				{
+					currentfont |= BOLD_FONT;
+					goto AddFontCode;
+				}
+				case 'b':
+				{
+					currentfont &= ~BOLD_FONT;
+					goto AddFontCode;
+				}
+				case 'I':
+				{
+					currentfont |= ITALIC_FONT;
+					goto AddFontCode;
+				}
+				case 'i':
+				{
+					currentfont &= ~ITALIC_FONT;
+					goto AddFontCode;
+				}
+				case 'P':
+				{
+					currentfont |= PROP_FONT;
+					goto AddFontCode;
+				}
+				case 'p':
+				{
+					currentfont &= ~PROP_FONT;
+					goto AddFontCode;
+				}
+				case 'U':
+				{
+					currentfont |= UNDERLINE_FONT;
+					goto AddFontCode;
+				}
+				case 'u':
+				{
+					currentfont &= ~UNDERLINE_FONT;
+				AddFontCode:
+					if (!textto)
+					{
+						int m, n;
+						int newfull;
+						double ratio;
+
+						if (!printed_something)
+						{
+							for (m = 0; m<plen;)
+							{
+								if (pbuffer[m] == FONT_CHANGE)
+								{
+									for (n = m; n<plen - 2; n++)
+									{
+										pbuffer[n] = pbuffer[n + 2];
+									}
+									plen -= 2;
+									pbuffer[plen] = '\0';
+									lastfont = currentfont;
+								}
+								else if (pbuffer[m] == COLOR_CHANGE)
+									m += 3;
+								else
+									break;
+							}
+						}
+#ifdef USE_TEXTBUFFER
+						if (hugo_strlen(pbuffer + bufferbreak) == 0)
+							bufferfont = currentfont;
+#endif
+						if (plen >= MAXBUFFER * 2 - 2) FatalError(OVERFLOW_E);
+						pbuffer[plen + 2] = '\0';
+						pbuffer[plen + 1] = (char)(currentfont + 1);
+						pbuffer[plen] = FONT_CHANGE;
+						plen += 2;
+
+						/* Convert full if font height changes, since
+						the amount of used screen real estate for
+						this particular font (in terms of lines)
+						will have changed:
+						*/
+						ratio = (double)lineheight;
+						hugo_font(currentfont);
+						ratio /= (double)lineheight;
+						newfull = (int)(((double)full)*ratio + 0.5);
+						if (newfull) full = newfull;
+					}
+					continue;
+				}
+				case '_':       /* forced space */
+				{
+					if (textto) c = ' ';
+					else c = FORCED_SPACE;
+					break;
+				}
+				default:
+					c = SpecialChar(a, &i);
+				}
+			}
+			else if (game_version <= 22)
+			{
+				if (c == '~')
+					c = '\"';
+				else if (c == '^')
+					c = '\n';
+			}
+
+			/* Add the new character */
+
+			/* Text may be sent to an address in the array table instead
+			of being output to the screen
+			*/
+			if (textto)
+			{
+				/* space for array length */
+				int n = (game_version>23) ? 2 : 0;
+
+				if (c == '\n')
+				{
+					SETMEM(arraytable * 16L + textto * 2 + n, 0);
+					textto++;
+				}
+				else if ((unsigned char)c >= ' ')
+				{
+					SETMEM(arraytable * 16L + textto * 2 + n, c);
+					textto++;
+				}
+				/* Add a terminating zero in case we don't
+				print any more to the array */
+				SETMEM(arraytable * 16L + textto * 2 + n, 0);
+
+				if (i >= alen) return;
+
+				continue;       /* back to for (i=0; i<slen; i++) */
+			}
+
+			printed_something = true;
+
+			/* Handle in-text newlines */
+			if (c == '\n')
+			{
+				hugo_font(currentfont = lastfont);
+#ifdef USE_TEXTBUFFER
+				TB_AddWord(pbuffer + bufferbreak,
+					current_text_x + bufferbreaklen,
+					current_text_y,
+					current_text_x + thisline - 1,
+					current_text_y + lineheight - 1);
+#endif
+				Printout(pbuffer);
+				lastfont = currentfont;
+#ifdef USE_TEXTBUFFER
+				bufferfont = currentfont;
+				bufferbreak = 0;
+				bufferbreaklen = 0;
+#endif
+				strcpy(pbuffer, "");
+				plen = 0;
+				linebreak = 0;
+				linebreaklen = 0;
+				thisline = 0;
+#ifdef USE_SMARTFORMATTING
+				leftquote = true;
+				lastc = '\n';
+#endif
+				pbuffer[plen++] = COLOR_CHANGE;
+				pbuffer[plen++] = (char)(fcolor + 1);
+				pbuffer[plen++] = (char)(bgcolor + 1);
+				pbuffer[plen] = '\0';
+
+
+				continue;
+			}
+
+#ifdef USE_SMARTFORMATTING
+			/* Smart formatting only for non-fixed fonts */
+			if ((currentfont & PROP_FONT) && smartformatting)
+			{
+				if ((!strncmp(a + i, "--", 2)) && lastc != '-' && strncmp(a + i, "---", 3))
+				{
+					lastc = '-';
+					c = (char)151;
+					i++;
+					leftquote = false;
+				}
+				else if (c == '\"')
+				{
+					if (leftquote)
+						c = (char)147;
+					else
+						c = (char)148;
+					leftquote = false;
+					lastc = c;
+				}
+				else if (c == '\'')
+				{
+					if (leftquote)
+						c = (char)145;
+					else
+						c = (char)146;
+					leftquote = false;
+					lastc = c;
+				}
+				else
+				{
+					if (c == ' ')
+						leftquote = true;
+					else
+						leftquote = false;
+					lastc = c;
+				}
+			}
+#endif
+
+			/* Add the new character to the printing buffer */
+			if (plen >= MAXBUFFER * 2 - 1) FatalError(OVERFLOW_E);
+			pbuffer[plen + 1] = '\0';
+			pbuffer[plen] = c;
+			plen++;
+
+			cwidth = hugo_charwidth(c);
+
+			/* Check to see if we've overrun the current line */
+
+			if (thisline + cwidth + currentpos > physical_windowwidth)
+			{
+				char t;
+
+				if (!linebreak)
+				{
+					linebreak = plen - 1;
+					linebreaklen = thisline;
+				}
+
+				t = pbuffer[linebreak];
+				pbuffer[linebreak] = '\0';
+
+				tempfont = currentfont;
+				hugo_font(currentfont = lastfont);
+				Printout(pbuffer);
+				lastfont = currentfont;
+				hugo_font(currentfont = tempfont);
+
+				pbuffer[linebreak] = t;
+				plen = strlen(pbuffer + linebreak);
+				memmove(pbuffer, pbuffer + linebreak, plen + 1);
+				thisline = thisline - linebreaklen;
+				linebreak = 0;
+				linebreaklen = 0;
+				startofline = 0;
+#ifdef USE_TEXTBUFFER
+				bufferbreak = 0;
+				bufferbreaklen = 0;
+#endif
+			}
+
+			thisline += cwidth;
+
+#ifdef USE_TEXTBUFFER
+			if ((c == ' ' || c == FORCED_SPACE) ||
+				(c == '/' && a[i + 1] != '/') || (c == '-' && a[i + 1] != '-'))
+			{
+				TB_AddWord(pbuffer + bufferbreak,
+					current_text_x + bufferbreaklen,
+					current_text_y,
+					current_text_x + thisline - 1,
+					current_text_y + lineheight - 1);
+
+				bufferbreak = plen;
+				bufferbreaklen = thisline;
+				bufferfont = currentfont;
+			}
+#endif
+			if ((c == ' ') || (c == '/' && a[i + 1] != '/') || (c == '-' && a[i + 1] != '-'))
+			{
+				linebreak = plen, linebreaklen = thisline;
+			}
+		}
+
+#ifdef USE_TEXTBUFFER
+		if (!sticky || alen > 1)
+		{
+			tempfont = currentfont;
+			currentfont = bufferfont;
+
+			TB_AddWord(pbuffer + bufferbreak,
+				current_text_x + bufferbreaklen,
+				current_text_y,
+				current_text_x + thisline - 1,
+				current_text_y + lineheight - 1);
+
+			bufferbreak = plen;
+			bufferbreaklen = thisline;
+			currentfont = tempfont;
+		}
+#endif
+		if (!sticky)
+		{
+			hugo_font(currentfont = lastfont);
+			Printout(pbuffer);
+			lastfont = currentfont;
+			strcpy(pbuffer, "");
+			linebreak = 0;
+			linebreaklen = 0;
+			thisline = 0;
+			plen = 0;
+#ifdef USE_TEXTBUFFER
+			bufferbreak = 0;
+			bufferbreaklen = 0;
+#endif
+#ifdef USE_SMARTFORMATTING
+			leftquote = true;
+#endif
+		}
+	}
+
+int Hugo::CallRoutine(unsigned int addr) {
+		int arg, i;
+		int val;
+		int templocals[MAXLOCALS], temppass[MAXLOCALS];
+		int temp_stack_depth;
+		long tempptr;
+		int potential_tail_recursion = tail_recursion;
+#if defined (DEBUGGER)
+		int tempdbnest;
+#endif
+		arg = 0;
+		tail_recursion = 0;
+
+		/* Pass local variables to routine, if specified */
+		if (MEM(codeptr) == OPEN_BRACKET_T)
+		{
+			codeptr++;
+			while (MEM(codeptr) != CLOSE_BRACKET_T)
+			{
+				if (arg)
+				{
+					for (i = 0; i<arg; i++)
+						temppass[i] = passlocal[i];
+				}
+
+				passlocal[arg++] = GetValue();
+
+				if (arg > 1)
+				{
+					for (i = 0; i<arg - 1; i++)
+						passlocal[i] = temppass[i];
+				}
+
+				if (MEM(codeptr) == COMMA_T) codeptr++;
+			}
+			codeptr++;
+		}
+
+		/* TAIL_RECURSION_ROUTINE if we came from a routine call immediately
+		following a 'return' statement...
+		*/
+		tail_recursion = potential_tail_recursion;
+		if (tail_recursion == TAIL_RECURSION_ROUTINE && MEM(codeptr) == EOL_T)
+		{
+			tail_recursion_addr = (long)addr*address_scale;
+			PassLocals(arg);
+			return 0;
+		}
+		/* ...but if we're not immediately followed by and end-of-line marker,
+		cancel the pending tail-recursion
+		*/
+		else
+		{
+			tail_recursion = 0;
+		}
+
+		for (i = 0; i<MAXLOCALS; i++)
+			templocals[i] = var[MAXGLOBALS + i];
+		PassLocals(arg);
+
+		temp_stack_depth = stack_depth;
+
+		SetStackFrame(stack_depth, RUNROUTINE_BLOCK, 0, 0);
+
+		tempptr = codeptr;      /* store calling address */
+		ret = 0;
+
+#if defined (DEBUGGER)
+		tempdbnest = dbnest;
+		DebugRunRoutine((long)addr*address_scale);
+		dbnest = tempdbnest;
+#else
+		RunRoutine((long)addr*address_scale);
+#endif
+		retflag = 0;
+		val = ret;
+		codeptr = tempptr;
+
+		stack_depth = temp_stack_depth;
+
+		for (i = 0; i<MAXLOCALS; i++)
+			var[MAXGLOBALS + i] = templocals[i];
+
+		return val;
+	}
+
+void Hugo::ContextCommand() {
+		unsigned int n;
+
+	ContextCommandLoop:
+
+		codeptr++;
+
+		n = GetValue();
+#if !defined (COMPILE_V25)
+		if (n == 0)
+		{
+			context_commands = 0;
+		}
+		else if (context_commands < MAX_CONTEXT_COMMANDS)
+		{
+			char *cc;
+
+			strncpy(context_command[context_commands], cc = GetWord(n), 64);
+			context_command[context_commands][63] = '\0';
+			if (strlen(cc) >= 64)
+				sprintf(context_command[context_commands] + 60, "...");
+			context_commands++;
+		}
+#endif
+		if (Peek(codeptr) == COMMA_T) goto ContextCommandLoop;
+		codeptr++;
+	}
+
+unsigned int Hugo::Dict() {
+		int i, len = 256;
+		unsigned int arr;
+		unsigned int pos = 2, loc;
+
+		codeptr += 2;                           /* "(" */
+
+		if (MEM(codeptr) == PARSE_T || MEM(codeptr) == WORD_T)
+			strcpy(line, GetWord(GetValue()));
+		else
+		{
+			/* Get the array address to read the to-be-
+			created dictionary entry from:
+			*/
+			arr = GetValue();
+			if (game_version >= 22)
+			{
+				/* Convert the address to a word
+				value:
+				*/
+				arr *= 2;
+
+				if (game_version >= 23)
+					/* space for array length */
+					arr += 2;
+			}
+
+			defseg = arraytable;
+			for (i = 0; i<len && PeekWord(arr + i * 2) != 0; i++)
+				line[i] = (char)PeekWord(arr + i * 2);
+			defseg = gameseg;
+			line[i] = '\0';
+		}
+
+		if (Peek(codeptr) == COMMA_T) codeptr++;
+		len = GetValue();
+
+		if ((loc = FindWord(line)) != UNKNOWN_WORD) return loc;
+
+		defseg = dicttable;
+
+		for (i = 1; i <= dictcount; i++)
+			pos += Peek(pos) + 1;
+
+		loc = pos - 2;
+
+		if ((long)(pos + strlen(line)) > (long)(codeend - dicttable * 16L))
+		{
+#ifdef DEBUGGER
+			sprintf(debug_line, "$MAXDICTEXTEND dictionary space exceeded");
+			RuntimeWarning(debug_line);
+#endif
+			defseg = gameseg;
+			return 0;
+		}
+
+		Poke(pos++, (unsigned char)strlen(line));
+		for (i = 0; i<(int)strlen(line) && i<len; i++)
+			Poke(pos++, (unsigned char)(line[i] + CHAR_TRANSLATION));
+		PokeWord(0, ++dictcount);
+
+		defseg = gameseg;
+
+		SaveUndo(DICT_T, strlen(line), 0, 0, 0);
+
+		return loc;
+	}
+
+void Hugo::FatalError(int n){
+	char fatalerrorline[64];
+
+	hugo_cleanup_screen();
+
+	switch (n)
+	{
+	case MEMORY_E:
+	{sprintf(line, "Out of memory\n");
+	break; }
+
+	case OPEN_E:
+	{sprintf(line, "Cannot open file\n");
+	break; }
+
+	case READ_E:
+	{sprintf(line, "Cannot read from file\n");
+	break; }
+
+	case WRITE_E:
+	{sprintf(line, "Cannot write to save file\n");
+	break; }
+
+	case EXPECT_VAL_E:
+	{sprintf(line, "Expecting value at $%s\n", PrintHex(codeptr));
+	break; }
+
+	case UNKNOWN_OP_E:
+	{sprintf(line, "Unknown operation at $%s\n", PrintHex(codeptr));
+	break; }
+
+	case ILLEGAL_OP_E:
+	{sprintf(line, "Illegal operation at $%s\n", PrintHex(codeptr));
+	break; }
+
+	case OVERFLOW_E:
+	{sprintf(line, "Overflow at $%s\n", PrintHex(codeptr));
+	break; }
+
+	case DIVIDE_E:
+	{sprintf(line, "Divide by zero at $%s\n", PrintHex(codeptr));
+	break; }
+	}
+
+	sprintf(fatalerrorline, "\nFatal Error:  %s", line);
+	error(fatalerrorline);
+}
+
+void Hugo::FileIO() {
+		char fileiopath[MAXPATH];
+		char iotype;
+		unsigned int fnameval;
+		long skipaddr;
+		int i, temp_stack_depth = stack_depth;
+
+		iotype = MEM(codeptr++);
+		skipaddr = (long)PeekWord(codeptr)*address_scale;
+		codeptr += 2;
+		fnameval = GetValue();
+		if (game_version >= 23) codeptr++; /* eol */
+
+		ioerror = 0;
+
+		/* Make sure the filename is legal, 8 alphanumeric characters or less */
+		strcpy(line, GetWord(fnameval));
+		if (strlen(line) > 8) goto LeaveFileIO;
+		for (i = 0; i<(int)strlen(line); i++)
+		{
+			if ((line[i] >= '0' && line[i] <= '9') || (line[i] >= 'A' && line[i] <= 'Z') ||
+				(line[i] >= 'a' && line[i] <= 'z'))
+			{
+				continue;
+			}
+			else
+				goto LeaveFileIO;
+		}
+
+		if (ioblock) goto LeaveFileIO;  /* can't nest file operations */
+
+		strcpy(fileiopath, GetWord(fnameval));
+
+		if (iotype == WRITEFILE_T)        /* "writefile" */
+		{
+			/* Glk implementation */
+			frefid_t fref = nullptr;
+
+			fref = glk_fileref_create_by_name(fileusage_Data | fileusage_BinaryMode,
+				fileiopath, 0);
+			io = glk_stream_open_file(fref, filemode_Write, 0);
+			glk_fileref_destroy(fref);
+			if (io == nullptr) goto LeaveFileIO;
+
+			ioblock = 1;
+		}
+		else                            /* "readfile"  */
+		{
+			/* Glk implementation */
+			frefid_t fref = nullptr;
+
+			fref = glk_fileref_create_by_name(fileusage_Data | fileusage_BinaryMode,
+				fileiopath, 0);
+			if (glk_fileref_does_file_exist(fref))
+				io = glk_stream_open_file(fref, filemode_Read, 0);
+			else
+				io = nullptr;
+			glk_fileref_destroy(fref);
+			if (io == nullptr) goto LeaveFileIO;
+
+			ioblock = 2;
+		}
+
+		SetStackFrame(stack_depth, RUNROUTINE_BLOCK, 0, 0);
+
+		RunRoutine(codeptr);
+
+		stack_depth = temp_stack_depth;
+
+		if (ioerror) retflag = 0;
+
+		delete io;
+		io = nullptr;
+		ioblock = 0;
+
+	LeaveFileIO:
+		ioerror = 0;
+		codeptr = skipaddr;
+	}
+
+void Hugo::Flushpbuffer() {
+		if (pbuffer[0] == '\0') return;
+
+#ifdef USE_TEXTBUFFER
+		/* For (single) characters left over from AP(), when Flushpbuffer() gets
+		called, say, from RunWindow()
+		*/
+		if (bufferbreaklen && pbuffer + bufferbreaklen && !(currentfont&PROP_FONT))
+		{
+			TB_AddWord(pbuffer + bufferbreak,
+				current_text_x + bufferbreaklen,
+				current_text_y,
+				current_text_x + bufferbreaklen + FIXEDCHARWIDTH,
+				current_text_y + lineheight - 1);
+		}
+#endif
+
+		pbuffer[strlen(pbuffer) + 1] = '\0';
+		pbuffer[strlen(pbuffer)] = (char)NO_NEWLINE;
+		Printout(Ltrim(pbuffer));
+		currentpos = hugo_textwidth(pbuffer);	/* -charwidth; */
+		strcpy(pbuffer, "");
+	}
+
+void Hugo::GetCommand() {
+		char a[256];
+#ifdef USE_TEXTBUFFER
+		int start, width, i, y;
+#endif
+		Flushpbuffer();
+		AP("");
+
+		hugo_settextcolor(fcolor);
+		hugo_setbackcolor(bgcolor);
+		if (icolor == -1)
+			icolor = fcolor;	/* check unset input color */
+
+		strncpy(a, GetWord(var[prompt]), 255);
+		during_player_input = true;
+		full = 0;
+#ifdef USE_TEXTBUFFER
+		/* Add the prompt to the textbuffer (using TB_Add()) */
+		y = current_text_y;
+		width = hugo_textwidth(GetWord(var[prompt]));
+		TB_AddWord(GetWord(var[prompt]), physical_windowleft, y,
+			physical_windowleft + width, y + lineheight - 1);
+
+		hugo_getline(a);
+
+		/* If hugo_scrollwindowup() called by hugo_getline() shifted things */
+		if (current_text_y > y)
+		{
+			y += (current_text_y - y);
+		}
+
+		/* Add each word in the input buffer */
+		start = 0;
+		for (i = 0; i<(int)strlen(buffer); i++)
+		{
+			if (buffer[i] == ' ')
+			{
+				buffer[i] = '\0';
+				TB_AddWord(buffer + start, physical_windowleft + width, y - lineheight,
+					physical_windowleft + width + hugo_textwidth(buffer + start), y - 1);
+				width += hugo_textwidth(buffer + start) + hugo_textwidth(" ");
+				start = i + 1;
+				buffer[i] = ' ';
+			}
+		}
+		/* Add the final word */
+		TB_AddWord(buffer + start, physical_windowleft + width, y - lineheight,
+			physical_windowleft + width + hugo_textwidth(buffer + start), y - 1);
+#else
+		hugo_getline(a);
+#endif
+		during_player_input = false;
+		strcpy(buffer, Rtrim(buffer));
+
+		strcpy(parseerr, "");
+
+		full = 1;
+		remaining = 0;
+	}
+
+char *Hugo::GetString(long addr) {
+	static char a[256];
+	int i, length;
+
+	length = Peek(addr);
+
+	for (i = 1; i <= length; i++)
+		a[i - 1] = (char)(Peek(addr + i) - CHAR_TRANSLATION);
+	a[i - 1] = '\0';
+
+	return a;
+}
+
+char *Hugo::GetText(long textaddr) {
+	static char g[1025];
+	int i, a;
+	int tdatal, tdatah, tlen;       /* low byte, high byte, length */
+
+
+									/* Read the string from memory... */
+	if (loaded_in_memory)
+	{
+		tlen = MEM(codeend + textaddr) + MEM(codeend + textaddr + 1) * 256;
+		for (i = 0; i<tlen; i++)
+		{
+			g[i] = (char)(MEM(codeend + textaddr + 2 + i) - CHAR_TRANSLATION);
+		}
+		g[i] = '\0';
+
+		return g;
+	}
+
+	/* ...Or load the string from disk */
+	if (game->seek(codeend + textaddr)) FatalError(READ_E);
+
+	tdatal = game->readByte();
+	tdatah = game->readByte();
+	if (tdatal == EOF || tdatah == EOF || game->err()) FatalError(READ_E);
+
+	tlen = tdatal + tdatah * 256;
+
+	for (i = 0; i<tlen; i++)
+	{
+		if ((a = game->readByte()) == EOF) FatalError(READ_E);
+		g[i] = (char)(a - CHAR_TRANSLATION);
+	}
+	g[i] = '\0';
+
+	return g;
+}
+
+char *Hugo::GetWord(unsigned int w) {
+	static char *b;
+	unsigned short a;
+
+	a = w;
+
+	if (a == 0) return "";
+
+	if (a == PARSE_STRING_VAL) return parseerr;
+	if (a == SERIAL_STRING_VAL) return serial;
+
+	/* bounds-checking to avoid some sort of memory arena error */
+	if ((long)(a + dicttable * 16L) > codeend)
+	{
+		b = "";
+		return b;
+	}
+
+	defseg = dicttable;
+	b = GetString((long)a + 2);
+	defseg = gameseg;
+
+	return b;
+}
+
+void Hugo::HandleTailRecursion(long addr) {
+	codeptr = addr;
+
+	/* Set up proper default return value for property or routine */
+	if (tail_recursion == TAIL_RECURSION_PROPERTY)
+		ret = 1;
+	else
+		ret = 0;
+
+	/* Unstack until we get to any routine call that got us here */
+	while (stack_depth)
+	{
+		if (code_block[stack_depth].type == RUNROUTINE_BLOCK)
+			break;
+		stack_depth--;
+	}
+
+#ifdef DEBUGGER
+	currentroutine = (unsigned int)(codeptr / address_scale);
+	call[window[VIEW_CALLS].count - 1].addr = currentroutine;
+	call[window[VIEW_CALLS].count - 1].param = true;
+
+	sprintf(debug_line, "Calling:  %s", RoutineName(currentroutine));
+	/* Don't duplicate blank separator line in code window */
+	if (codeline[window[CODE_WINDOW].count - 1][0] != 0)
+		AddStringtoCodeWindow("");
+	AddStringtoCodeWindow(debug_line);
+
+	/* Adjust for very long runs */
+	dbnest--;
+#endif
+
+	tail_recursion = 0;
+	tail_recursion_addr = 0;
+}
+
+void Hugo::InitGame() {
+	int i;
+
+	/* Stop any audio if this is a restart */
+	hugo_stopsample();
+	hugo_stopmusic();
+
+#if !defined (COMPILE_V25)
+	hugo_stopvideo();
+	context_commands = 0;
+#endif
+	game_reset = false;
+
+	SetStackFrame(stack_depth, RUNROUTINE_BLOCK, 0, 0);
+
+	/* Figure out which objects have either a noun or an adjective property;
+	store it in obj_parselist, one bit per object */
+	if ((!obj_parselist) && (obj_parselist = (char *)hugo_blockalloc(sizeof(char)*((objects + 7) / 8))))
+	{
+
+		for (i = 0; i<objects; i++)
+		{
+			if (i % 8 == 0) obj_parselist[i / 8] = 0;
+
+			if (PropAddr(i, adjective, 0) || PropAddr(i, noun, 0))
+				obj_parselist[i / 8] |= 1 << (i % 8);
+			else
+				obj_parselist[i / 8] &= ~(1 << (i % 8));
+		}
+	}
+
+#if defined (DEBUGGER)
+	for (i = 0; i<MAXLOCALS; i++) strcpy(localname[i], "");
+	window[VIEW_LOCALS].count = current_locals = 0;
+
+	PassLocals(0);
+	DebugRunRoutine((long)initaddr*address_scale);
+#else
+	PassLocals(0);
+	RunRoutine((long)initaddr*address_scale);
+#endif
+
+	ret = 0;
+	retflag = 0;
+	var[actor] = var[player];
+}
+
+void Hugo::LoadGame() {
+	int i, data;
+	unsigned int synptr;
+	size_t ccount;
+	int check_version;
+	long textbank, filelength;
+#ifndef LOADGAMEDATA_REPLACED
+	long c;
+#endif
+	game = &_gameFile;
+	filelength = game->size();
+	game->seek(0);
+
+	if (game->err()) FatalError(READ_E);
+	if ((game_version = game->readByte()) == EOF) FatalError(OPEN_E);
+
+	/* Earlier versions of the compiler wrote the version code as
+	1 or 2 instead of 10 or 20.
+	*/
+	if (game_version == 1 || game_version == 2)
+		game_version *= 10;
+
+	if (game_version < 21) object_size = 12;
+
+	if (game_version < 31) address_scale = 4;
+
+	check_version = HEVERSION * 10 + HEREVISION;
+
+	defseg = gameseg;
+
+	if (game_version < HEVERSION)
+	{
+		sprintf(line, "Hugo Compiler v%d.%d or later required.\n", HEVERSION, HEREVISION);
+		if (game_version>0)
+			sprintf(line + strlen(line), "File \"%s\" is v%d.%d.\n", gamefile, game_version / 10, game_version % 10);
+
+		error(line);
+	}
+	else if (game_version > check_version)
+	{
+		error("File \"%s\" is incorrect or unknown version.\n", gamefile);
+	}
+
+	hugo_settextpos(1, physical_windowheight / lineheight);
+
+	if (game_version >= 25)
+		game->seek(H_TEXTBANK, SEEK_SET);
+	else
+		/* Because pre-v2.5 didn't have performaddr in the header */
+		game->seek(H_TEXTBANK - 2L, SEEK_SET);
+
+	data = game->readByte();
+	textbank = game->readByte();
+	if (data == EOF || textbank == EOF || game->err()) FatalError(READ_E);
+	textbank = (textbank * 256L + (long)data) * 16L;
+	codeend = textbank;
+
+	/* Use a 1024-byte read block */
+	ccount = 1024;
+
+	if (game->seek(0, SEEK_SET)) FatalError(READ_E);
+
+#ifndef LOADGAMEDATA_REPLACED
+	/* Allocate as much memory as is required */
+	if ((!loaded_in_memory) || (mem = (unsigned char *)hugo_blockalloc(filelength)) == nullptr)
+	{
+		loaded_in_memory = 0;
+		if ((mem = (unsigned char *)hugo_blockalloc(codeend)) == nullptr)
+			FatalError(MEMORY_E);
+	}
+
+	c = 0;
+
+	/* Load either the entire file or just up to the start of the
+	text bank
+	*/
+	while (c < (loaded_in_memory ? filelength : codeend))
+	{
+		/* Complicated, but basically just makes sure that
+		the last read (whether loaded_in_memory or not)
+		doesn't override the end of the file.  Shouldn't
+		normally be a problem for fread(), but it caused
+		a crash under MSVC++.
+		*/
+		i = game->read((unsigned char *)&mem[c],
+			(loaded_in_memory) ?
+			((filelength - c>(long)ccount) ? ccount : (size_t)(filelength - c)) :
+			((codeend - c>(long)ccount) ? ccount : (size_t)(codeend - c)));
+
+		if (!i) break;
+		c += i;
+	}
+#else
+	if (!LoadGameData(false)) FatalError(READ_E);
+#endif
+
+	if (game->err()) FatalError(READ_E);
+
+	defseg = gameseg;
+
+	/* Read header: */
+
+	id[0] = Peek(H_ID);
+	id[1] = Peek(H_ID + 1);
+	id[2] = '\0';
+
+	for (i = 0; i<8; i++)
+		serial[i] = Peek(H_SERIAL + i);
+	serial[8] = '\0';
+
+	codestart = PeekWord(H_CODESTART);
+	objtable = PeekWord(H_OBJTABLE) + gameseg;
+	proptable = PeekWord(H_PROPTABLE) + gameseg;
+	eventtable = PeekWord(H_EVENTTABLE) + gameseg;
+	arraytable = PeekWord(H_ARRAYTABLE) + gameseg;
+	dicttable = PeekWord(H_DICTTABLE) + gameseg;
+	syntable = PeekWord(H_SYNTABLE) + gameseg;
+
+	initaddr = PeekWord(H_INIT);
+	mainaddr = PeekWord(H_MAIN);
+	parseaddr = PeekWord(H_PARSE);
+	parseerroraddr = PeekWord(H_PARSEERROR);
+	findobjectaddr = PeekWord(H_FINDOBJECT);
+	endgameaddr = PeekWord(H_ENDGAME);
+	speaktoaddr = PeekWord(H_SPEAKTO);
+	performaddr = PeekWord(H_PERFORM);
+
+
+	/* Read totals: */
+
+	defseg = objtable;
+	objects = PeekWord(0);
+
+	defseg = eventtable;
+	events = PeekWord(0);
+
+	defseg = dicttable;
+	dictcount = PeekWord(0);
+
+	defseg = syntable;
+	syncount = PeekWord(0);
+
+
+	/* Additional information to be found: */
+
+	/* display object */
+	if (game_version >= 24)
+	{
+		data = FindWord("(display)");
+
+		for (i = 0; i<objects; i++)
+		{
+			if (GetProp(i, 0, 1, true) == data)
+			{
+				display_object = i;
+				break;
+			}
+		}
+	}
+
+	/* build punctuation string (additional user-specified punctuation) */
+	synptr = 2;
+	strcpy(punc_string, "");
+	for (i = 1; i <= syncount; i++)
+	{
+		defseg = syntable;
+		if (Peek(synptr) == 3)	/* 3 = punctuation */
+		{
+			strcpy(line, GetWord(PeekWord(synptr + 1)));
+			if (strlen(line) + strlen(punc_string) > 63) break;
+			strcat(punc_string, line);
+		}
+		synptr += 5;
+	}
+}
+
+void Hugo::PassLocals(int n) {
+	int i;
+
+	for (i = 0; i<MAXLOCALS; i++)
+	{
+		var[MAXGLOBALS + i] = passlocal[i];
+		passlocal[i] = 0;
+	}
+	arguments_passed = n;
+}
+
+const char *Hugo::PrintHex(long a) {
+	static char hex[7];
+	int h = 0;
+
+	strcpy(hex, "");
+
+	if (a < 0L) a = 0;
+
+	hex[h++] = '0';
+	if (a < 65536L) hex[h++] = '0';
+	if (a < 4096L) hex[h++] = '0';
+	if (a < 256L) hex[h++] = '0';
+	if (a < 16L) hex[h++] = '0';
+
+	sprintf(hex + h, "%lX", a);
+
+	return hex;
+}
+
+void Hugo::Printout(char *a) {
+	char b[2], sticky = 0, trimmed = 0;
+	char tempfcolor;
+	int i, l;
+	int n;
+	int last_printed_font = currentfont;
+
+	/* hugo_font() should do this if necessary, but just in case */
+	if (lineheight < FIXEDLINEHEIGHT)
+		lineheight = FIXEDLINEHEIGHT;
+
+	tempfcolor = fcolor;
+
+	/* The before-check of the linecount: */
+	if (full)
+	{
+		/* -1 here since it's before printing */
+		if (full >= physical_windowheight / lineheight - 1)
+			PromptMore();
+	}
+
+	if ((a[0] != '\0') && a[strlen(a) - 1] == (char)NO_NEWLINE)
+	{
+		a[strlen(a) - 1] = '\0';
+		sticky = true;
+	}
+
+	b[0] = b[1] = '\0';
+
+
+	/* The easy part is just skimming <a> and processing each code
+	or printed character, as the case may be:
+	*/
+
+	l = 0;	/* physical length of string */
+
+	for (i = 0; i<(int)strlen(a); i++)
+	{
+		if ((a[i] == ' ') && !trimmed && currentpos == 0)
+		{
+			continue;
+		}
+
+		if ((unsigned char)a[i] > ' ' || a[i] == FORCED_SPACE)
+		{
+			trimmed = true;
+			last_printed_font = currentfont;
+		}
+
+		switch (b[0] = a[i])
+		{
+		case FONT_CHANGE:
+			n = (int)(a[++i] - 1);
+			if (currentfont != n)
+				hugo_font(currentfont = n);
+			break;
+
+		case COLOR_CHANGE:
+			fcolor = (char)(a[++i] - 1);
+			hugo_settextcolor((int)fcolor);
+			hugo_setbackcolor((int)(a[++i] - 1));
+			hugo_font(currentfont);
+			break;
+
+		default:
+			if (b[0] == FORCED_SPACE) b[0] = ' ';
+			l += hugo_charwidth(b[0]);
+
+			/* A minor adjustment for font changes and RunWindow() to make
+			sure we're not printing unnecessarily downscreen
+			*/
+			if ((just_left_window) && current_text_y > physical_windowbottom - lineheight)
+			{
+				current_text_y = physical_windowbottom - lineheight;
+			}
+			just_left_window = false;
+
+			hugo_print(b);
+		}
+
+		if (script && (unsigned char)b[0] >= ' ')
+			script->putBuffer(b, strlen(b));
+
+#if defined (SCROLLBACK_DEFINED)
+		if (!inwindow && (unsigned char)b[0] >= ' ')
+		{
+#ifdef USE_SMARTFORMATTING
+			/* Undo smart-formatting for ASCII scrollback */
+			switch ((unsigned char)b[0])
+			{
+			case 151:
+				hugo_sendtoscrollback("--");
+				continue;
+			case 145:
+			case 146:
+				b[0] = '\'';
+				break;
+			case 147:
+			case 148:
+				b[0] = '\"';
+			}
+#endif
+			hugo_sendtoscrollback(b);
+		}
+#endif
+	}
+
+	/* If we've got a linefeed and didn't hit the right edge of the
+	window
+	*/
+#ifdef NO_TERMINAL_LINEFEED
+	if (!sticky)
+#else
+	if (!sticky && currentpos + l < physical_windowwidth)
+#endif
+	{
+		/* The background color may have to be temporarily set if we're
+		not in a window--the reason is that full lines of the
+		current background color might be printed by the OS-specific
+		scrolling function.  (This behavior is overridden by the
+		Hugo Engine for in-window printing, which always adds new
+		lines in the current background color when scrolling.)
+		*/
+		hugo_setbackcolor((inwindow) ? bgcolor : default_bgcolor);
+		hugo_print("\r");
+
+		i = currentfont;
+		hugo_font(currentfont = last_printed_font);
+
+#ifndef GLK
+		if (currentline > physical_windowheight / lineheight)
+		{
+			int full_limit = physical_windowheight / lineheight;
+
+			hugo_scrollwindowup();
+
+			if ((current_text_y)
+				&& full >= full_limit - 3
+				&& physical_windowbottom - current_text_y - lineheight > lineheight / 2)
+			{
+				PromptMore();
+			}
+			currentline = full_limit;
+		}
+
+		/* Don't scroll single-line windows before PromptMore() */
+		else if (physical_windowheight / lineheight > 1)
+#endif
+		{
+			hugo_print("\n");
+		}
+
+		hugo_font(currentfont = i);
+		hugo_setbackcolor(bgcolor);
+	}
+
+#if defined (AMIGA)
+	else
+	{
+		if (currentpos + l >= physical_windowwidth)
+			AmigaForceFlush();
+	}
+#endif
+	just_left_window = false;
+
+	/* If no newline is to be printed after the current line: */
+	if (sticky)
+	{
+		currentpos += l;
+	}
+
+	/* Otherwise, take care of all the line-feeding, line-counting,
+	etc.
+	*/
+	else
+	{
+		currentpos = 0;
+		if (currentline++ > physical_windowheight / lineheight)
+			currentline = physical_windowheight / lineheight;
+
+		if (!playback) skipping_more = false;
+
+		++full;
+
+		/* The after-check of the linecount: */
+		if ((full) && full >= physical_windowheight / lineheight)
+		{
+			PromptMore();
+		}
+
+		if (script)
+		{
+			script->putBuffer("\n", 1);
+		}
+
+#if defined (SCROLLBACK_DEFINED)
+		if (!inwindow) hugo_sendtoscrollback("\n");
+#endif
+	}
+
+	fcolor = tempfcolor;
+}
+
+int Hugo::RecordCommands() {
+	remaining = 0;
+	skipping_more = false;
+
+	switch (Peek(codeptr))
+	{
+	case RECORDON_T:
+	{
+		if (!record && !playback)
+		{
+			/* Glk implementation */
+			frefid_t fref = nullptr;
+
+			fref = glk_fileref_create_by_prompt(fileusage_Transcript | fileusage_TextMode,
+				filemode_Write, 0);
+			record = glk_stream_open_file(fref, filemode_Write, 0);
+			glk_fileref_destroy(fref);
+			if (!record)
+				return 0;
+
+			return 1;
+		}
+		break;
+	}
+
+	case RECORDOFF_T:
+	{
+		if (playback) return 1;
+
+		if (record)
+		{
+			delete record;
+			record = nullptr;
+			return 1;
+		}
+		break;
+	}
+
+	case PLAYBACK_T:
+	{
+		if (!playback)
+		{
+			/* Glk implementation */
+			frefid_t fref = nullptr;
+
+			fref = glk_fileref_create_by_prompt(fileusage_InputRecord | fileusage_TextMode,
+				filemode_Read, 0);
+			if (glk_fileref_does_file_exist(fref))
+				playback = glk_stream_open_file(fref, filemode_Read, 0);
+			else
+				playback = nullptr;
+			glk_fileref_destroy(fref);
+			if (!playback)
+				return 0;
+
+			return 1;
+		}
+		break;
+	}
+	}
+	return 0;
+}
+
+void Hugo::SaveUndo(int a, int b, int c, int d, int e) {
+	int tempptr;
+
+	if (undorecord)
+	{
+		undostack[undoptr][0] = a;      /* save the operation */
+		undostack[undoptr][1] = b;
+		undostack[undoptr][2] = c;
+		undostack[undoptr][3] = d;
+		undostack[undoptr][4] = e;
+
+		/* Put zeroes at end of this operation in case
+		the stack wraps around */
+		tempptr = undoptr;
+		if (++undoptr == MAXUNDO) undoptr = 0;
+		undostack[undoptr][0] = 0;
+		undostack[undoptr][1] = 0;
+		undoptr = tempptr;
+
+		if (++undoturn == MAXUNDO)        /* turn too complex */
+		{
+			undoptr = 0;
+			undoturn = MAXUNDO;
+			undoinvalid = 1;
+		}
+
+		if (++undoptr == MAXUNDO) undoptr = 0;
+	}
+}
+
+void Hugo::SetStackFrame(int depth, int type, long brk, long returnaddr) {
+	if (depth == RESET_STACK_DEPTH) stack_depth = 0;
+	else if (++stack_depth >= MAXSTACKDEPTH) FatalError(MEMORY_E);
+
+	code_block[stack_depth].type = type;
+	code_block[stack_depth].brk = brk;
+	code_block[stack_depth].returnaddr = returnaddr;
+}
+
+void Hugo::SetupDisplay() {
+	hugo_settextmode();
+
+	hugo_settextwindow(1, 1,
+		SCREENWIDTH / FIXEDCHARWIDTH, SCREENHEIGHT / FIXEDLINEHEIGHT);
+
+	last_window_left = 1;
+	last_window_top = 1;
+	last_window_right = SCREENWIDTH / FIXEDCHARWIDTH;
+	last_window_bottom = SCREENHEIGHT / FIXEDLINEHEIGHT;
+
+	hugo_settextcolor(16);
+	hugo_setbackcolor(17);
+	hugo_clearfullscreen();
+}
+
+char Hugo::SpecialChar(const char *a, int *i) {
+	char r, s, skipbracket = 0;
+
+	r = a[*i];
+	s = r;
+
+	if (r == '\"') return r;
+
+	/* For a couple of versions, Hugo allowed Inform-style
+	punctuation control characters; I don't remember
+	exactly why.
+	*/
+	if (game_version <= 22)
+		if (r == '~' || r == '^') return r;
+
+	if (r == '(')
+	{
+		r = a[++*i];
+		skipbracket = true;
+	}
+
+	switch (r)
+	{
+	case '`':               /* accent grave */
+	{
+		/* Note that the "s = '...'" characters are
+		Latin-1 and may not display properly under,
+		e.g., DOS */
+
+		s = a[++*i];
+#ifndef NO_LATIN1_CHARSET
+		switch (s)
+		{
+		case 'a':  s = (char)0xe0; break; /* ? */
+		case 'e':  s = (char)0xe8; break; /* ? */
+		case 'i':  s = (char)0xec; break; /* ? */
+		case 'o':  s = (char)0xf2; break; /* ? */
+		case 'u':  s = (char)0xf9; break; /* ? */
+		case 'A':  s = (char)0xc0; break; /* ? */
+		case 'E':  s = (char)0xc8; break; /* ? */
+		case 'I':  s = (char)0xcc; break; /* ? */
+		case 'O':  s = (char)0xd2; break; /* ? */
+		case 'U':  s = (char)0xd9; break; /* ? */
+		}
+#endif
+		break;
+	}
+	case '\'':              /* accent acute */
+	{
+		s = a[++*i];
+#ifndef NO_LATIN1_CHARSET
+		switch (s)
+		{
+		case 'a':  s = (char)0xe1; break; /* ? */
+		case 'e':  s = (char)0xe9; break; /* ? */
+		case 'i':  s = (char)0xed; break; /* ? */
+		case 'o':  s = (char)0xf3; break; /* ? */
+		case 'u':  s = (char)0xfa; break; /* ? */
+		case 'y':  s = (char)0xfd; break;
+		case 'A':  s = (char)0xc1; break; /* ? */
+		case 'E':  s = (char)0xc9; break; /* ? */
+		case 'I':  s = (char)0xcd; break; /* ? */
+		case 'O':  s = (char)0xd3; break; /* ? */
+		case 'U':  s = (char)0xda; break; /* ? */
+		case 'Y':  s = (char)0xdd; break; /* ? */
+		}
+#endif
+		break;
+	}
+	case '~':               /* tilde */
+	{
+		s = a[++*i];
+#ifndef NO_LATIN1_CHARSET
+		switch (s)
+		{
+		case 'a':  s = (char)0xe3; break; /* ? */
+		case 'n':  s = (char)0xf1; break; /* ? */
+		case 'o':  s = (char)0xf5; break; /* ? */
+		case 'A':  s = (char)0xc3; break; /* ? */
+		case 'N':  s = (char)0xd1; break; /* ? */
+		case 'O':  s = (char)0xd5; break; /* ? */
+		}
+#endif
+		break;
+	}
+	case '^':               /* circumflex */
+	{
+		s = a[++*i];
+#ifndef NO_LATIN1_CHARSET
+		switch (s)
+		{
+		case 'a':  s = (char)0xe2; break; /* ? */
+		case 'e':  s = (char)0xea; break; /* ? */
+		case 'i':  s = (char)0xee; break; /* ? */
+		case 'o':  s = (char)0xf4; break; /* ? */
+		case 'u':  s = (char)0xfb; break; /* ? */
+		case 'A':  s = (char)0xc2; break; /* ? */
+		case 'E':  s = (char)0xca; break; /* ? */
+		case 'I':  s = (char)0xce; break; /* ? */
+		case 'O':  s = (char)0xd4; break; /* ? */
+		case 'U':  s = (char)0xdb; break; /* ? */
+		}
+#endif
+		break;
+	}
+	case ':':               /* umlaut */
+	{
+		s = a[++*i];
+#ifndef NO_LATIN1_CHARSET
+		switch (s)
+		{
+		case 'a':  s = (char)0xe4; break; /* ? */
+		case 'e':  s = (char)0xeb; break; /* ? */
+		case 'i':  s = (char)0xef; break; /* ? */
+		case 'o':  s = (char)0xf6; break; /* ? */
+		case 'u':  s = (char)0xfc; break; /* ? */
+											/* case 'y':  s = (char)0xff; break; */ /* ? */
+		case 'A':  s = (char)0xc4; break; /* ? */
+		case 'E':  s = (char)0xcb; break; /* ? */
+		case 'I':  s = (char)0xcf; break; /* ? */
+		case 'O':  s = (char)0xd6; break; /* ? */
+		case 'U':  s = (char)0xdc; break; /* ? */
+		}
+#endif
+		break;
+	}
+	case ',':               /* cedilla */
+	{
+		s = a[++*i];
+#ifndef NO_LATIN1_CHARSET
+		switch (s)
+		{
+		case 'C':  s = (char)0xc7; break; /* ? */
+		case 'c':  s = (char)0xe7; break; /* ? */
+		}
+#endif
+		break;
+	}
+	case '<':               /* Spanish left quotation marks */
+#ifndef NO_LATIN1_CHARSET
+		s = (char)0xab; /* ? */
+#endif
+		break;
+	case '>':               /* Spanish right quotation marks */
+#ifndef NO_LATIN1_CHARSET
+		s = (char)0xbb; /* ? */
+		break;
+#endif
+	case '!':               /* upside-down exclamation mark */
+#ifndef NO_LATIN1_CHARSET
+		s = (char)0xa1; /* ? */
+#endif
+		break;
+	case '?':               /* upside-down question mark */
+#ifndef NO_LATIN1_CHARSET
+		s = (char)0xbf; /* ? */
+#endif
+		break;
+	case 'a':               /* ae ligature */
+#ifndef NO_LATIN1_CHARSET
+		s = (char)0xe6; ++*i; /* ? */
+#else
+		s = 'e'; ++*i;
+#endif
+		break;
+	case 'A':               /* AE ligature */
+#ifndef NO_LATIN1_CHARSET
+		s = (char)0xc6; ++*i; /* ? */
+#else
+		s = 'E'; ++*i;
+#endif
+		break;
+	case 'c':               /* cents symbol */
+#ifndef NO_LATIN1_CHARSET
+		s = (char)0xa2; /* ? */
+#endif
+		break;
+	case 'L':               /* British pound */
+#ifndef NO_LATIN1_CHARSET
+		s = (char)0xa3; /* ? */
+#endif
+		break;
+	case 'Y':               /* Japanese Yen */
+#ifndef NO_LATIN1_CHARSET
+		s = (char)0xa5; /* ? */
+#endif
+		break;
+	case '-':               /* em dash */
+#ifndef NO_LATIN1_CHARSET
+							/* s = (char)0x97; */ /* ? */
+#endif
+		break;
+	case '#':               /* 3-digit decimal code */
+	{
+		s = (char)((a[++*i] - '0') * 100);
+		s += (a[++*i] - '0') * 10;
+		s += (a[++*i] - '0');
+#ifdef NO_LATIN1_CHARSET
+		if ((unsigned)s>127) s = '?';
+#endif
+	}
+	}
+
+	if (skipbracket)
+	{
+		++*i;
+		if (a[*i + 1] == ')')++*i;
+		if (s == ')') s = r;
+	}
+
+	return s;
+}
+
+int Hugo::Undo() {
+	int count = 0, n;
+	int turns, turncount, tempptr;
+	int obj, prop, attr, v;
+	unsigned int addr;
+
+	if (--undoptr < 0) undoptr = MAXUNDO - 1;
+
+	if (undostack[undoptr][1] != 0)
+	{
+		/* Get the number of operations to be undone for
+		the last turn.
+		*/
+		if ((turns = undostack[undoptr][1]) >= MAXUNDO)
+			goto CheckUndoFailed;
+
+		turns--;
+
+		turncount = 0;
+		tempptr = undoptr;
+
+		/* Count the number of operations available to see if there
+		are enough to undo the last turn (as per the number
+		required in <turns>.
+		*/
+		do
+		{
+			if (--undoptr < 0) undoptr = MAXUNDO - 1;
+			turncount++;
+
+			/* if end of turn */
+			if (undostack[undoptr][0] == 0)
+				break;
+		} while (true);
+
+		if (turncount<turns) goto CheckUndoFailed;
+
+		undoptr = tempptr;
+
+		if (--undoptr < 0) undoptr = MAXUNDO - 1;
+
+		while (undostack[undoptr][0] != 0)
+		{
+			switch (undostack[undoptr][0])
+			{
+			case MOVE_T:
+			{
+				MoveObj(undostack[undoptr][1], undostack[undoptr][2]);
+				count++;
+				break;
+			}
+
+			case PROP_T:
+			{
+				obj = undostack[undoptr][1];
+				prop = undostack[undoptr][2];
+				n = undostack[undoptr][3];
+				v = undostack[undoptr][4];
+
+				if ((addr = PropAddr(obj, prop, 0)) != 0)
+				{
+					defseg = proptable;
+
+					if (n == PROP_ROUTINE)
+					{
+						Poke(addr + 1, PROP_ROUTINE);
+						n = 1;
+					}
+
+					/* Use this new prop count number if the
+					existing one is too low or a prop routine
+					*/
+					else if (Peek(addr + 1) == PROP_ROUTINE || Peek(addr + 1)<(unsigned char)n)
+						Poke(addr + 1, (unsigned char)n);
+
+					/* property length */
+					if (n <= (int)Peek(addr + 1))
+						PokeWord(addr + 2 + (n - 1) * 2, v);
+				}
+				count++;
+				break;
+			}
+
+			case ATTR_T:
+			{
+				obj = undostack[undoptr][1];
+				attr = undostack[undoptr][2];
+				n = undostack[undoptr][3];
+				SetAttribute(obj, attr, n);
+				count++;
+				break;
+			}
+
+			case VAR_T:
+			{
+				n = undostack[undoptr][1];
+				v = undostack[undoptr][2];
+				var[n] = v;
+				count++;
+				break;
+			}
+
+			case ARRAYDATA_T:
+			{
+				defseg = arraytable;
+				addr = undostack[undoptr][1];
+				n = undostack[undoptr][2];
+				v = undostack[undoptr][3];
+
+				/* The array length was already accounted for before calling
+				SaveUndo(), so there is no adjustment of
+				+2 here.
+				*/
+				PokeWord(addr + n * 2, v);
+				count++;
+				break;
+			}
+
+			case DICT_T:
+			{
+				defseg = dicttable;
+				PokeWord(0, --dictcount);
+				count++;
+				break;
+			}
+			case WORD_T:
+			{
+				n = undostack[undoptr][1];
+				v = undostack[undoptr][2];
+				wd[n] = v;
+				word[n] = GetWord(wd[n]);
+				count++;
+			}
+			}
+			defseg = gameseg;
+
+			if (--undoptr < 0) undoptr = MAXUNDO - 1;
+		}
+	}
+
+CheckUndoFailed:
+	if (!count)
+	{
+		undoinvalid = 1;
+		game_reset = false;
+		return 0;
+	}
+
+	game_reset = true;
+
+	undoptr++;
+	return 1;
+}
+
+} // End of namespace Hugo
+} // End of namespace Glk
diff --git a/engines/glk/hugo/htokens.cpp b/engines/glk/hugo/htokens.cpp
new file mode 100644
index 0000000..fd913b2
--- /dev/null
+++ b/engines/glk/hugo/htokens.cpp
@@ -0,0 +1,79 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "glk/hugo/htokens.h"
+#include "Common/algorithm.h"
+
+namespace Glk {
+namespace Hugo {
+
+const char *const HTokens::token[] = {
+	/* 0x00 - 0x0f */
+	"",  "(", ")", ".", ":", "=", "-", "+",
+	"*", "/", "|", ";", "{", "}", "[", "]",
+
+	/* 0x10 - 0x1f */
+	"#", "~", ">=", "<=", "~=", "&", ">", "<",
+	"if", ",", "else", "elseif", "while", "do", "select", "case",
+
+	/* 0x20 - 0x2f */
+	"for", "return", "break",  "and", "or", "jump", "run", "is",
+	"not", "true", "false", "local", "verb", "xverb", "held", "multi",
+
+	/* 0x30 - 0x3f */
+	"multiheld", "newline", "anything", "print",
+	"number", "capital", "text", "graphics",
+	"color", "remove", "move", "to",
+	"parent", "sibling", "child", "youngest",
+
+	/* 0x40 - 0x4f */
+	"eldest", "younger", "elder", "prop#",
+	"attr#", "var#", "dictentry#", "textdata#",
+	"routine#","debugdata#","objectnum#", "value#",
+	"eol#", "system", "notheld", "multinotheld",
+
+	/* 0x50 - 0x5f */
+	"window", "random", "word", "locate",
+	"parse$", "children", "in", "pause",
+	"runevents", "arraydata#", "call", "stringdata#",
+	"save", "restore", "quit", "input",
+
+	/* 0x60 - 0x6f */
+	"serial$", "cls", "scripton", "scriptoff",
+	"restart", "hex", "object", "xobject",
+	"string", "array", "printchar", "undo",
+	"dict", "recordon", "recordoff", "writefile",
+
+	/* 0x70 - */
+	"readfile", "writeval", "readval", "playback",
+	"colour", "picture", "label#", "sound",
+	"music", "repeat", "addcontext", "video"
+};
+
+int HTokens::token_hash[TOKENS + 1];
+
+HTokens::HTokens() {
+	Common::fill(&token_hash[0], &token_hash[TOKENS + 1], 0);
+}
+
+} // End of namespace Hugo
+} // End of namespace Glk
diff --git a/engines/glk/hugo/htokens.h b/engines/glk/hugo/htokens.h
new file mode 100644
index 0000000..08961c7
--- /dev/null
+++ b/engines/glk/hugo/htokens.h
@@ -0,0 +1,100 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef GLK_HUGO_HTOKENS
+#define GLK_HUGO_HTOKENS
+
+namespace Glk {
+namespace Hugo {
+
+/*
+ * This file contains token definitions for the Hugo Compiler and Engine
+ * The enum constants of type TOKEN_T reflect the token names given in the token array.
+ * Token names followed by a # are for system use.
+ */
+
+/* i.e., highest numbered token */
+#define TOKENS 0x7B
+
+/* arbitrary */
+#define HASH_KEY 1023
+
+enum TOKEN_T {
+	/* 0x00 - 0x0f */
+	NULL_T, OPEN_BRACKET_T, CLOSE_BRACKET_T, DECIMAL_T,
+	COLON_T, EQUALS_T, MINUS_T, PLUS_T,
+	ASTERISK_T, FORWARD_SLASH_T, PIPE_T, SEMICOLON_T,
+	OPEN_BRACE_T, CLOSE_BRACE_T, OPEN_SQUARE_T, CLOSE_SQUARE_T,
+
+	/* 0x10 - 0x1f */
+	POUND_T, TILDE_T, GREATER_EQUAL_T, LESS_EQUAL_T,
+	NOT_EQUAL_T, AMPERSAND_T, GREATER_T, LESS_T,
+	IF_T, COMMA_T, ELSE_T, ELSEIF_T,
+	WHILE_T, DO_T, SELECT_T, CASE_T,
+	
+	/* 0x20 - 0x2f */
+	FOR_T, RETURN_T, BREAK_T, AND_T,
+	OR_T, JUMP_T, RUN_T, IS_T,
+	NOT_T, TRUE_T, FALSE_T, LOCAL_T,
+	VERB_T, XVERB_T, HELD_T, MULTI_T,
+	
+	/* 0x30 - 0x3f */
+	MULTIHELD_T, NEWLINE_T, ANYTHING_T, PRINT_T,
+	NUMBER_T, CAPITAL_T, TEXT_T, GRAPHICS_T, 
+	COLOR_T, REMOVE_T, MOVE_T, TO_T,
+	PARENT_T, SIBLING_T, CHILD_T, YOUNGEST_T,
+
+	/* 0x40 - 0x4f */
+	ELDEST_T, YOUNGER_T, ELDER_T, PROP_T,
+	ATTR_T, VAR_T, DICTENTRY_T, TEXTDATA_T,
+	ROUTINE_T, DEBUGDATA_T, OBJECTNUM_T, VALUE_T,
+	EOL_T, SYSTEM_T, NOTHELD_T, MULTINOTHELD_T,
+
+	/* 0x50 - 0x5f */
+	WINDOW_T, RANDOM_T, WORD_T, LOCATE_T,
+	PARSE_T, CHILDREN_T, IN_T, PAUSE_T,
+	RUNEVENTS_T, ARRAYDATA_T, CALL_T, STRINGDATA_T,
+	SAVE_T, RESTORE_T, QUIT_T, INPUT_T,
+
+	/* 0x60 - 0x6f */
+	SERIAL_T, CLS_T, SCRIPTON_T, SCRIPTOFF_T,
+	RESTART_T, HEX_T, OBJECT_T, XOBJECT_T,
+	STRING_T, ARRAY_T, PRINTCHAR_T, UNDO_T,
+	DICT_T, RECORDON_T, RECORDOFF_T, WRITEFILE_T,
+	
+	/* 0x70 - */
+	READFILE_T, WRITEVAL_T, READVAL_T, PLAYBACK_T,
+	COLOUR_T, PICTURE_T, LABEL_T, SOUND_T,
+	MUSIC_T, REPEAT_T, ADDCONTEXT_T, VIDEO_T
+};
+
+struct HTokens {
+	static const char *const token[];
+	static int token_hash[];
+
+	HTokens();
+};
+
+} // End of namespace Hugo
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/hugo/hugo.cpp b/engines/glk/hugo/hugo.cpp
index afc788e..8e81134 100644
--- a/engines/glk/hugo/hugo.cpp
+++ b/engines/glk/hugo/hugo.cpp
@@ -40,29 +40,54 @@ Hugo::Hugo(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gam
 		physical_windowwidth(0), physical_windowheight(0), physical_windowtop(0),
 		physical_windowleft(0), physical_windowbottom(0), physical_windowright(0),
 		inwindow(0), charwidth(0), lineheight(0), current_text_x(0), current_text_y(0),
-		undoptr(0), undoturn(0), undoinvalid(0), undorecord(0), context_commands(0),
-		in_valid_window(false), glk_fcolor(DEF_FCOLOR), glk_bgcolor(DEF_BGCOLOR),
-		mainwin_bgcolor(0), glk_current_font(0), just_cleared_screen(false), secondwin_bottom(0) {
+		skipping_more(false), undoptr(0), undoturn(0), undoinvalid(0), undorecord(0),
+		context_commands(0), in_valid_window(false), glk_fcolor(DEF_FCOLOR), glk_bgcolor(DEF_BGCOLOR),
+		mainwin_bgcolor(0), glk_current_font(0), just_cleared_screen(false), secondwin_bottom(0),
+		// heobject
+		display_object(-1), display_needs_repaint(0), display_pointer_x(0), display_pointer_y(0),
+		// heparse
+		words(0), parsed_number(0), remaining(0), xverb(0), starts_with_verb(0),
+		grammaraddr(0), obj_parselist(nullptr), domain(0), odomain(0), objcount(0),
+		parse_allflag(false), pobjcount(0), pobj(0), obj_match_state(0), object_is_number(0),
+		objgrammar(0), objstart(0), objfinish(0), addflag(0), speaking(0), oopscount(0),
+		parse_called_twice(0), reparse_everything(0), full_buffer(false), recursive_call(false),
+		parse_location(0),
+
+		// herun
+		arguments_passed(0), ret(0), retflag(0), during_player_input(false), override_full(0),
+		game_reset(false), stack_depth(0), tail_recursion(0), tail_recursion_addr(0),
+		last_window_top(0), last_window_bottom(0), last_window_left(0), last_window_right(0),
+		lowest_windowbottom(0), physical_lowest_windowbottom(0), just_left_window(false) {
 	Common::fill(&context_command[0][0], &context_command[MAX_CONTEXT_COMMANDS][64], 0);
 	Common::fill(&id[0], &id[3], '\0');
 	Common::fill(&serial[0], &serial[9], '\0');
 	Common::fill(&pbuffer[0], &pbuffer[MAXBUFFER * 2 + 1], 0);
 	Common::fill(&undostack[0][0], &undostack[MAXUNDO][5], 0);
-	Common::fill(&buffer[0], &buffer[MAXBUFFER + MAXWORDS], '\0');
 	Common::fill(&var[0], &var[MAXLOCALS + MAXGLOBALS], 0);
-}
+	
+	// heparse
+	Common::fill(&buffer[0], &buffer[MAXBUFFER + MAXWORDS], '\0');
+	Common::fill(&errbuf[0], &errbuf[MAXBUFFER + 1], 0);
+	Common::fill(&line[0], &line[1025], 0);
+	Common::fill(&word[0], &word[MAXWORDS + 1], nullptr);
+	Common::fill(&wd[0], &wd[MAXWORDS + 1], 0);
+	Common::fill(&parseerr[0], &parseerr[MAXBUFFER + 1], '\0');
+	Common::fill(&parsestr[0], &parsestr[MAXBUFFER + 1], '\0');
+	Common::fill(&objlist[0], &objlist[MAXOBJLIST], 0);
+	Common::fill(&objword_cache[0], &objword_cache[MAXWORDS], 0);
+	Common::fill(&oops[0], &oops[MAXBUFFER + 1], '\0');
+	Common::fill(&punc_string[0], &punc_string[64], '\0');
 
-// TODO: Proper method implementations
-void SetupDisplay() {}
-void LoadGame() {}
-void PlayGame() {}
-void hugo_closefiles() {}
+	// herun
+	Common::fill(&passlocal[0], &passlocal[MAXLOCALS], 0);
+}
 
 void Hugo::runGame() {
 	hugo_init_screen();
 
 	SetupDisplay();
 
+	strcpy(gamefile, getFilename().c_str());
 	strcpy(pbuffer, "");
 
 	gameseg = 0;
diff --git a/engines/glk/hugo/hugo.h b/engines/glk/hugo/hugo.h
index 7d12646..a33c599 100644
--- a/engines/glk/hugo/hugo.h
+++ b/engines/glk/hugo/hugo.h
@@ -25,8 +25,10 @@
 
 #include "common/scummsys.h"
 #include "glk/glk_api.h"
+#include "glk/hugo/htokens.h"
 #include "glk/hugo/hugo_defines.h"
 #include "glk/hugo/hugo_types.h"
+#include "glk/hugo/stringfn.h"
 
 namespace Glk {
 namespace Hugo {
@@ -34,7 +36,7 @@ namespace Hugo {
 /**
  * Hugo game interpreter
  */
-class Hugo : public GlkAPI {
+class Hugo : public GlkAPI, public HTokens, public StringFunctions {
 private:
 	winid_t mainwin, currentwin;
 	winid_t secondwin, auxwin;
@@ -46,9 +48,12 @@ private:
 	 */
 	int address_scale;
 
+	// hemisc
+	char gamefile[255];
 	int game_version;
 	int object_size;
-	HUGO_FILE game;
+	//HUGO_FILE game;
+	Common::SeekableReadStream *game;
 	HUGO_FILE script;
 	HUGO_FILE save;
 	HUGO_FILE playback;
@@ -100,6 +105,7 @@ private:
 	int inwindow;
 	int charwidth, lineheight, FIXEDCHARWIDTH, FIXEDLINEHEIGHT;
 	int current_text_x, current_text_y;
+	bool skipping_more;
 	int undostack[MAXUNDO][5];
 	int undoptr;
 	int undoturn;
@@ -113,11 +119,83 @@ private:
 	bool just_cleared_screen;
 	int secondwin_bottom;
 
+	// heexpr
+	int var[MAXLOCALS + MAXGLOBALS];
+
+	// heobject
+	int display_object;				///< i.e., non-existent (yet)
+	char display_needs_repaint;		///< for display object
+	int display_pointer_x, display_pointer_y;
+
 	// heparse
 	char buffer[MAXBUFFER + MAXWORDS];
+	char errbuf[MAXBUFFER + 1];				///< last invalid input
+	char line[1025];						///< line buffer
+
+	int words;								///< parsed word count
+	char *word[MAXWORDS + 1];				///< breakdown into words
+	unsigned int wd[MAXWORDS + 1];			///<     "      "   dict. entries
+	unsigned int parsed_number;				///< needed for numbers in input
+
+	signed char remaining;					///< multiple commands in input
+	char parseerr[MAXBUFFER + 1];			///< for passing to RunPrint, etc.
+	char parsestr[MAXBUFFER + 1];			///< for passing quoted string
+	char xverb;								///< flag; 0 = regular verb
+	char starts_with_verb;					///< input line; 0 = no verb word
+	unsigned int grammaraddr;				///< address in grammar
+	char *obj_parselist;					///< objects with noun/adjective
+	int domain, odomain;					///< of object(s)
+	int objlist[MAXOBJLIST];				///< for objects of verb
+	char objcount;							///< of objlist
+	char parse_allflag;						///< for "all" in MatchObject
+	pobject_structure pobjlist[MAXPOBJECTS];	///< for possible objects
+	int pobjcount;							///< of pobjlist
+	int pobj;								///< last remaining suspect
+	int obj_match_state;					///< see MatchCommand() for details
+	char objword_cache[MAXWORDS];			///< for MatchWord() xobject, etc.
+	char object_is_number;					///< number used in player command
+	unsigned int objgrammar;				///< for 2nd pass
+	int objstart;							///<  "   "   "
+	int objfinish;							///<  "   "   "
+	char addflag;							///< true if adding to objlist[]
+	int speaking;							///< if command is addressed to obj.
+
+	char oops[MAXBUFFER + 1];				///< illegal word
+	int oopscount;							///< # of corrections in a row
+
+	char parse_called_twice;
+	char reparse_everything;
+	char punc_string[64];					///< punctuation string
+
+	char full_buffer;
+	/**
+	 * to MatchObject()
+	 * Necessary for proper disambiguation when addressing a character;
+	 * i.e., when 'held' doesn't refer to held by the player, etc.
+	 */
+	char recursive_call;
+	int parse_location;						///< usually var[location]
+
+	// herun
+	int passlocal[MAXLOCALS];		///< locals passed to routine
+	int arguments_passed;			///< when calling routine
+	int ret;						///< return value and returning flag
+	char retflag;
+	bool during_player_input;
+	char override_full;
+	bool game_reset;				///< for restore, undo, etc.
+
+	CODE_BLOCK code_block[MAXSTACKDEPTH];
+	int stack_depth;
+	int tail_recursion;
+	long tail_recursion_addr;
+
+	// Used by RunWindow for setting current window dimensions:
+	int last_window_top, last_window_bottom, last_window_left, last_window_right;
+	int lowest_windowbottom,				///< in text lines
+		physical_lowest_windowbottom;		///< in pixels or text lines
+	bool just_left_window;
 
-	// heexpr
-	int var[MAXLOCALS + MAXGLOBALS];
 private:
 	/**
 	 * \defgroup heglk
@@ -260,6 +338,161 @@ private:
 	void hugo_stopvideo();
 
 	/**@}*/
+
+	/**
+	 * \defgroup hemisc
+	 * @{
+	 */
+
+	/**
+	 * The all-purpose printing routine that takes care of word-wrapping.
+	 */
+	void AP(const char *a);
+
+	/**
+	 * Used whenever a routine is called, assumes the routine address and begins
+	 * with the arguments (if any).
+	 */
+	int CallRoutine(unsigned int addr);
+
+	/**
+	 * Adds a command to the context command list.  A zero value (i.e., an empty string)
+	 * resets the list.
+	 */
+	void ContextCommand();
+
+	/**
+	 * Dynamically creates a new dictionary entry.
+	 */
+	unsigned int Dict();
+
+	/**
+	 * Generates a fatal error
+	 */
+	void FatalError(int n);
+
+	void FileIO();
+
+	void Flushpbuffer();
+
+	void GetCommand();
+
+	/**
+	 * From any address <addr>; the segment must be defined prior to calling the function.
+	 */
+	char *GetString(long addr);
+
+	/**
+	 * Get text block from position <textaddr> in the text bank.  If the game was not fully loaded
+	 * in memory, i.e., if loaded_in_memory is not true, the block is read from disk.
+	 */
+	char *GetText(long textaddr);
+
+	/**
+	 * From the dictionary table.
+	 */
+	char *GetWord(unsigned int w);
+
+	void HandleTailRecursion(long addr);
+
+	void InitGame();
+
+	void LoadGame();
+
+	/**
+	 * Must be called before running every new routine, i.e. before calling RunRoutine().
+	 * Unfortunately, the current locals must be saved in a temp array prior to calling.
+	 * The argument n gives the number of arguments passed.
+	 */
+	void PassLocals(int n);
+
+	inline unsigned char Peek(long a) {
+		return MEM(defseg * 16L + a);
+	}
+
+	inline unsigned int PeekWord(long a) {
+		return (unsigned char)MEM(defseg * 16L + a) + (unsigned char)MEM(defseg * 16L + a + 1) * 256;
+	}
+
+	inline void Poke(unsigned int a, unsigned char v) {
+		SETMEM(defseg * 16L + a, v);
+	}
+
+	inline void PokeWord(unsigned int a, unsigned int v) {
+		SETMEM(defseg * 16L + a, (char)(v % 256));
+		SETMEM(defseg * 16L + a + 1, (char)(v / 256));
+	}
+
+	/**
+	 * Returns <a> as a hex-number string in XXXXXX format.
+	 */
+	static const char *PrintHex(long a);
+
+	/**
+	 * Print to client display taking into account cursor relocation,
+	 * font changes, color setting, and window scrolling.
+	 */
+	void Printout(char *a);
+
+	int RecordCommands();
+
+	/**
+	 * Formats:
+	 *
+	 * end of turn:    (0, undoturn, 0, 0, 0)
+	 * move obj.:      (MOVE_T, obj., parent, 0, 0)
+	 * property:       (PROP_T, obj., prop., # or PROP_ROUTINE, val.)
+	 * attribute:      (ATTR_T, obj., attr., 0 or 1, 0)
+	 * variable:       (VAR_T, var., value, 0, 0)
+	 * array:          (ARRAYDATA_T, array addr., element, val., 0)
+	 * dict:           (DICT_T, entry length, 0, 0, 0)
+	 * word setting:   (WORD_T, word number, new word, 0, 0)
+	 */
+	void SaveUndo(int a, int b, int c, int d, int e);
+
+	/**
+	 * Properly sets up the code_block structure for the current stack depth depending
+	 * on if this is a called block (RUNROUTINE_BLOCK) or otherwise.
+	 */
+	void SetStackFrame(int depth, int type, long brk, long returnaddr);
+
+	void SetupDisplay();
+
+	/**
+	 * The method is passed <a> as the string and <*i> as the position in the string.
+	 * The character(s) at a[*i], a[*(i+1)], etc. are converted into a single Latin-1
+	 * (i.e., greater than 127) character value.
+	 *
+	 * Assume that the AP() has already encountered a control character ('\'),
+	 * and that a[*i]... is one of:
+	 *
+	 * `a	accent grave on following character (e.g., 'a')
+	 * 'a	accent acute on following character (e.g., 'a')
+	 * ~n	tilde on following (e.g., 'n' or 'N')
+	 * :a	umlaut on following (e.g., 'a')
+	 * ^a	circumflex on following (e.g., 'a')
+	 * ,c	cedilla on following (e.g., 'c' or 'C')
+	 * <	Spanish left quotation marks
+	 * >	Spanish right quotation marks
+	 * !	upside-down exclamation mark
+	 * ?	upside-down question mark
+	 * ae	ae ligature
+	 * AE	AE ligature
+	 * c	cents symbol
+	 * L	British pound
+	 * Y	Japanese Yen
+	 * -	em (long) dash
+	 * #nnn	character value given by nnn
+	 *
+	 * Note that the return value is a single character--which will be either unchanged
+	 * or a Latin-1 character value.
+	 */
+	char SpecialChar(const char *a, int *i);
+
+	int Undo();
+
+
+	/**@}*/
 private:
 	/**
 	 * Allocate memory block
@@ -292,6 +525,20 @@ public:
 	 * Save the game to the passed stream
 	 */
 	virtual Common::Error saveGameData(strid_t file, const Common::String &desc) override;
+
+	// TODO: Stubs to be Properly implemented
+	int GetValue() { return 0; }
+	void PlayGame() {}
+	void hugo_closefiles() {}
+	void RunRoutine(long v) {}
+	unsigned int FindWord(const char *a) { return 0; }
+	void PromptMore() {}
+	void hugo_stopsample() {}
+	void hugo_stopmusic() {}
+	void SetAttribute(int obj, int attr, int c) {}
+	unsigned int PropAddr(int obj, int p, unsigned int offset) { return 0; }
+	int GetProp(int obj, int p, int n, char s) { return 0; }
+	void MoveObj(int obj, int p) {}
 };
 
 } // End of namespace Hugo
diff --git a/engines/glk/hugo/hugo_defines.h b/engines/glk/hugo/hugo_defines.h
index caa1755..260586e 100644
--- a/engines/glk/hugo/hugo_defines.h
+++ b/engines/glk/hugo/hugo_defines.h
@@ -28,6 +28,11 @@
 namespace Glk {
 namespace Hugo {
 
+#define HEVERSION 3
+#define HEREVISION 3
+#define HEINTERIM ".0"
+
+#define MAXOBJLIST 32
 #define MAX_CONTEXT_COMMANDS	32
 #define MAXBUFFER 255
 #define MAXUNDO 1024
@@ -121,6 +126,20 @@ browsing.
 #define PROP_END          255
 #define PROP_ROUTINE      255
 
+#define MEM(addr) (mem[addr])
+#define SETMEM(addr, n) (mem[addr] = n)
+#define GETMEMADDR(addr) (&mem[addr])
+#define HUGO_PTR
+
+#define RESET_STACK_DEPTH (-1)
+
+#define RUNROUTINE_BLOCK  1
+#define CONDITIONAL_BLOCK 2
+#define DOWHILE_BLOCK     3
+
+#define TAIL_RECURSION_ROUTINE          (-1)
+#define TAIL_RECURSION_PROPERTY         (-2)
+
 } // End of namespace Hugo
 } // End of namespace Glk
 
diff --git a/engines/glk/hugo/hugo_types.h b/engines/glk/hugo/hugo_types.h
index 43b2899..9298510 100644
--- a/engines/glk/hugo/hugo_types.h
+++ b/engines/glk/hugo/hugo_types.h
@@ -75,6 +75,42 @@ enum ObjectProperties {
 	pointer_y = 12
 };
 
+/**
+ * Fatal errors
+ */
+enum ERROR_TYPE {
+	MEMORY_E = 1,   ///< out of memory
+	OPEN_E,         ///< error opening file
+	READ_E,         ///< error reading from file
+	WRITE_E,        ///< error writing to file
+	EXPECT_VAL_E,   ///< expecting value
+	UNKNOWN_OP_E,   ///< unknown operation
+	ILLEGAL_OP_E,   ///< illegal operation
+	OVERFLOW_E,     ///< overflow
+	DIVIDE_E		///< divide by zero
+};
+
+/**
+ * A structure used for disambiguation in MatchObject()
+ */
+struct pobject_structure {
+	int obj;		///< the actual object number
+	char type;		///< referred to by noun or adjective
+
+	pobject_structure() : obj(0), type(0) {}
+};
+
+/**
+ * Structure used for navigating {...} blocks:
+ */
+struct CODE_BLOCK {
+	int type;			///< see #defines, below
+	long brk;			///< break address, or 0 to indicate NOP
+	long returnaddr;	///< used only for do-while loops
+
+	CODE_BLOCK() : type(0), brk(0), returnaddr(0) {}
+};
+
 } // End of namespace Hugo
 } // End of namespace Glk
 
diff --git a/engines/glk/hugo/stringfn.cpp b/engines/glk/hugo/stringfn.cpp
new file mode 100644
index 0000000..79886ac
--- /dev/null
+++ b/engines/glk/hugo/stringfn.cpp
@@ -0,0 +1,110 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "glk/hugo/stringfn.h"
+
+namespace Glk {
+namespace Hugo {
+
+
+/* LEFT */
+
+char *StringFunctions::Left(char a[], int l) {
+	static char *temp;
+	int i;
+
+	temp = GetTempString();
+
+	if (l > (int)strlen(a))
+		l = strlen(a);
+	for (i = 0; i<l; i++)
+		temp[i] = a[i];
+	temp[i] = '\0';
+	return temp;
+}
+
+char *StringFunctions::Ltrim(char a[]) {
+	static char *temp;
+
+	temp = GetTempString();
+	strcpy(temp, a);
+	while (temp[0]==' ' || temp[0]=='\t')
+		strcpy(temp, temp+1);
+	return temp;
+}
+
+char *StringFunctions::Mid(char a[], int pos, int n) {
+	static char *temp;
+	int i;
+
+	temp = GetTempString();
+	pos--;
+	if (pos+n > (int)strlen(a))
+		n = strlen(a)-pos;
+	for (i = 0; i<n; i++)
+		temp[i] = a[pos+i];
+	temp[i] = '\0';
+	return temp;
+}
+
+char *StringFunctions::Right(char a[], int l) {
+	static char *temp;
+	int i;
+
+	temp = GetTempString();
+	if (l > (int)strlen(a))
+		l = strlen(a);
+	for (i = 0; i<l; i++)
+		temp[i] = a[strlen(a)-l+i];
+	temp[i] = '\0';
+	return temp;
+}
+
+char *StringFunctions::Rtrim(char a[]) {
+	static char *temp;
+	int len;
+
+	temp = GetTempString();
+	strcpy(temp, a);
+	while (((len = strlen(temp))) && (temp[len-1]==' ' || temp[len-1]=='\t'))
+		strcpy(temp, Left(temp, len-1));
+	return temp;
+}
+
+char *StringFunctions::hugo_strcpy(char *s, const char *t)  { 
+	char *r = s; 
+	while ((*s++ = *t++) != 0) ; 
+	return r; 
+}
+
+char *StringFunctions::GetTempString() {
+	static char *r;
+
+	r = &_tempString[_tempstringCount][0];
+	if (++_tempstringCount >= NUM_TEMPSTRINGS)
+		_tempstringCount = 0;
+
+	return r;
+}
+
+} // End of namespace Hugo
+} // End of namespace Glk
diff --git a/engines/glk/hugo/stringfn.h b/engines/glk/hugo/stringfn.h
new file mode 100644
index 0000000..bb339fc
--- /dev/null
+++ b/engines/glk/hugo/stringfn.h
@@ -0,0 +1,65 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef GLK_HUGO_STRINGFN
+#define GLK_HUGO_STRINGFN
+
+#include "common/algorithm.h"
+
+namespace Glk {
+namespace Hugo {
+
+#define NUM_TEMPSTRINGS 2
+
+/**
+ * The following string-manipulation functions closely mimic BASIC-language string functionality.
+ * They do not alter the provided string; instead, they return a pointer to a static (modified) copy.
+ */
+class StringFunctions {
+private:
+	char _tempString[NUM_TEMPSTRINGS][1025];
+	int _tempstringCount;
+
+	char *GetTempString();
+public:
+	StringFunctions() : _tempstringCount(0) {
+		Common::fill(&_tempString[0][0], &_tempString[NUM_TEMPSTRINGS][1025], '\0');
+	}
+
+	char *Left(char a[], int l);
+
+	char *Ltrim(char a[]);
+
+	char *Mid(char a[], int pos, int n);
+
+	char *Right(char a[], int l);
+
+	char *Rtrim(char a[]);
+
+	char *hugo_strcpy(char *s, const char *t);
+};
+
+
+} // End of namespace Hugo
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/module.mk b/engines/glk/module.mk
index dd12d23..74ab60f 100644
--- a/engines/glk/module.mk
+++ b/engines/glk/module.mk
@@ -73,7 +73,10 @@ MODULE_OBJS := \
 	glulxe/vm.o \
 	hugo/detection.o \
 	hugo/heglk.o \
+	hugo/hemisc.o \
+	hugo/htokens.o \
 	hugo/hugo.o \
+	hugo/stringfn.o \
 	magnetic/detection.o \
 	magnetic/emu.o \
 	magnetic/graphics.o \


Commit: 0b2346c1f14d94efa1264aadef5bf13c1f15ddef
    https://github.com/scummvm/scummvm/commit/0b2346c1f14d94efa1264aadef5bf13c1f15ddef
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-05-11T10:18:13+10:00

Commit Message:
GLK: HUGO: Added heobject

Changed paths:
  A engines/glk/hugo/heobject.cpp
    engines/glk/hugo/hugo.cpp
    engines/glk/hugo/hugo.h
    engines/glk/hugo/hugo_defines.h
    engines/glk/hugo/hugo_types.h
    engines/glk/module.mk


diff --git a/engines/glk/hugo/heobject.cpp b/engines/glk/hugo/heobject.cpp
new file mode 100644
index 0000000..cc1416d
--- /dev/null
+++ b/engines/glk/hugo/heobject.cpp
@@ -0,0 +1,671 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "glk/hugo/hugo.h"
+
+namespace Glk {
+namespace Hugo {
+
+#if defined (DEBUGGER)
+
+int Hugo::CheckObjectRange(int obj) {
+	if (runtime_warnings) {
+		return CheckinRange((unsigned)obj, (unsigned)objects, "object");
+	} else
+		return true;
+}
+
+#endif
+
+int Hugo::Child(int obj) {
+	int c;
+
+#if defined (DEBUGGER)
+	if (!CheckObjectRange(obj)) return 0;
+#endif
+	if (obj<0 || obj>=objects) return 0;
+
+	defseg = objtable;
+
+	c = PeekWord(2 + obj*object_size + object_size - 4);
+
+	defseg = gameseg;
+
+	return c;
+}
+
+int Hugo::Children(int obj) {
+	int count = 0;
+	int nextobj;
+
+	if (obj<0 || obj>=objects) return 0;
+
+	nextobj = Child(obj);
+	while (nextobj)
+		{count++;
+		nextobj = Sibling(nextobj);}
+	return count;
+}
+
+int Hugo::Elder(int obj) {
+	int lastobj;
+	int p, cp;
+
+	if (obj<0 || obj>=objects) return 0;
+
+	p = Parent(obj);
+	cp = Child(p);
+
+        if (p==0 || cp==obj)
+		return 0;
+
+	lastobj = cp;
+	while (Sibling(lastobj) != obj)
+		lastobj = Sibling(lastobj);
+
+	return lastobj;
+}
+
+unsigned long Hugo::GetAttributes(int obj, int attribute_set) {
+	unsigned long a;
+
+	defseg = objtable;
+
+#if defined (DEBUGGER)
+	if (!CheckObjectRange(obj)) return 0;
+#endif
+	if (obj<0 || obj>=objects) return 0;
+
+	a = (unsigned long)PeekWord(2 + obj*object_size + attribute_set*4)
+		+ (unsigned long)PeekWord(2 + obj*object_size + attribute_set*4 + 2)*65536L;
+
+	defseg = gameseg;
+
+	return a;
+}
+
+int Hugo::GetProp(int obj, int p, int n, char s) {
+	char objonly,			/* no verbroutine given in before, etc. */
+		isadditive = 0,		/* before, after, etc.			*/
+		gotone = 0,             /* when a match has been made           */
+		getpropaddress = 0;     /* when getting &object.property        */
+	int i;
+	int tempself,
+		objtype,		/* i.e., what we're matching to		*/
+		flag = 0;
+	int g = 0;
+	int templocals[MAXLOCALS];
+	int temp_stack_depth;
+	char tempinexpr = inexpr;
+	unsigned int pa,		/* property address			*/
+		offset = 0;
+	long inprop,			/* code position in complex property	*/
+		returnaddr;
+#if defined (DEBUGGER)
+	long orig_inprop;
+	int tempdbnest;
+
+	/* Don't check a possible non-existent display object (-1) */
+	if (obj!=-1 || display_object!=-1) CheckObjectRange(obj);
+#endif
+	/* This way either -1 (the non-existent display object) or a too-high
+	   object will fail
+	*/
+	if (obj<0 || obj>=objects) return 0;
+
+	/* The display object, which is automatically created by the compiler,
+	   did not exist pre-v2.4
+	*/
+	if ((obj==display_object) && game_version>=24)
+	{
+		/* There are no actual default "properties" per se on the
+		   display object--but reading them returns certain data about
+		   the current state of display affairs
+		*/
+
+		/* no display.<prop> #2, etc. */
+		if (n==1 && p<=pointer_y)
+		{
+			if (p==screenwidth)
+#if defined (GLK) && defined (ACTUAL_LINELENGTH)
+				g = ACTUAL_LINELENGTH();
+#else
+				g = SCREENWIDTH/FIXEDCHARWIDTH;
+#endif
+			else if (p==screenheight)
+/* ACTUAL_SCREENHEIGHT can be set to a non-portable function if
+   SCREENHEIGHT and SCREENHEIGHT have been set to large values in
+   order to force the non-portable layer to handle wrapping and
+   scrolling (as in the Glk port).
+*/
+#if defined (ACTUAL_SCREENHEIGHT)
+				g = ACTUAL_SCREENHEIGHT();
+#else
+				g = SCREENHEIGHT/FIXEDLINEHEIGHT;
+#endif
+			else if (p==linelength)
+/* ACTUAL_LINELENGTH functions similarly to ACTUAL_SCREENWIDTH,
+   above.
+*/
+#if defined (ACTUAL_LINELENGTH)
+				g = ACTUAL_LINELENGTH();
+#else
+				g = physical_windowwidth/FIXEDCHARWIDTH;
+#endif
+			else if (p==windowlines)
+				g = physical_windowheight/FIXEDLINEHEIGHT;
+			else if (p==cursor_column)
+				g = (currentpos+1+hugo_textwidth(pbuffer))/FIXEDCHARWIDTH;
+			else if (p==cursor_row)
+				g = currentline;
+			else if (p==hasgraphics)
+				g = hugo_hasgraphics();
+			else if (p==title_caption)
+				g = FindWord(game_title);
+			else if (p==hasvideo)
+#if !defined (COMPILE_V25)
+				g = hugo_hasvideo();
+#else
+				g = 0;
+#endif
+			else if (p==needs_repaint)
+				g = display_needs_repaint;
+			else if (p==pointer_x)
+				g = display_pointer_x;
+			else if (p==pointer_y)
+				g = display_pointer_y;
+			else
+				g = 0;
+
+			return g;
+		}
+	}
+
+	/* To avoid prematurely getting an address in &obj.prop.prop */
+	if (getaddress && MEM(codeptr)!=DECIMAL_T)
+		getpropaddress = true;
+
+        tempself = var[self];
+	if (!s) var[self] = obj;
+
+	temp_stack_depth = stack_depth;
+
+
+GetNextProp:
+
+	pa = PropAddr(obj, p, offset);
+
+	defseg = proptable;
+
+	/* If the object doesn't have property p, see if there's a
+	   default value.
+	*/
+	if (!pa)
+	{
+		if (offset) goto NoMorePropMatches;
+
+		if (getpropaddress)             /* if an &value */
+			g = 0;
+		else
+			g = PeekWord(p * 2 + 2);
+	}
+
+	else
+	{
+		/* Property is a value... */
+		if (Peek(pa+1) < PROP_ROUTINE)
+		{
+			if (getaddress || (int)Peek(pa+1) < n || n<=0)
+			{
+#if defined (DEBUGGER)
+				if (n!=1)
+					CheckinRange(n, (int)Peek(pa+1), "property element");
+#endif
+				g = 0;
+			}
+			else
+				g = PeekWord(pa + n * 2);
+		}
+
+		/* ...or a property routine */
+		else
+		{
+			/* Check if this is an additive property */
+			defseg = proptable;
+			if (Peek(2 + Peek(0)*2 + p)&ADDITIVE_FLAG)
+				isadditive = true;
+
+			/* If an &value, return the address of the
+			   property routine.
+			*/
+			if (getpropaddress)
+			{
+				g = PeekWord(pa+2);
+				goto NoMorePropMatches;
+			}
+			else
+			{
+#if defined (DEBUGGER)
+				if (debug_eval)
+				{
+					debug_eval_error = true;
+					DebugMessageBox("Expression Error",
+						"Property routine illegal in watch/assignment");
+					defseg = gameseg;
+					return 0;
+				}
+#endif
+				/* If not a complex property such as
+				   before or after:
+				*/
+				if ((game_version>=22 && (Peek(2 + Peek(0)*2 + p)&COMPLEX_FLAG)==0) || (game_version<22 && p!=before && p!=after))
+				{
+					ret = 1;
+					returnaddr = codeptr;
+
+					/* Check to see if this is a valid tail-recursive return... */
+					if (tail_recursion==TAIL_RECURSION_PROPERTY && MEM(codeptr)==EOL_T)
+					{
+						PassLocals(0);
+						tail_recursion_addr = (long)PeekWord(pa+2)*address_scale;
+						return 0;
+					}
+					/* ...but if we're not immediately followed by and end-of-line marker,
+					   or another property value, cancel the pending tail-recursion
+					*/
+					else if (MEM(codeptr)!=DECIMAL_T)
+					{
+						tail_recursion = 0;
+					}
+
+					for (i=0; i<MAXLOCALS; i++)
+						templocals[i] = var[MAXGLOBALS+i];
+
+					PassLocals(0);
+
+					SetStackFrame(stack_depth+1, RUNROUTINE_BLOCK, 0, 0);
+#if defined (DEBUGGER)
+					tempdbnest = dbnest;
+					DebugRunRoutine((long)PeekWord(pa+2)*address_scale);
+					dbnest = tempdbnest;
+#else
+					RunRoutine((long)PeekWord(pa+2)*address_scale);
+#endif
+
+					retflag = 0;
+					codeptr = returnaddr;
+					g = ret;
+					ret = 0;
+				}
+
+		                /* Complex property: */
+				else
+				{
+					for (i=0; i<MAXLOCALS; i++)
+						templocals[i] = var[MAXGLOBALS+i];
+
+					inprop = (long)PeekWord(pa + 2)*address_scale;
+#ifdef DEBUGGER
+					orig_inprop = inprop;
+#endif
+					defseg = gameseg;
+					while (Peek(inprop)!=CLOSE_BRACE_T)
+					{
+						returnaddr = codeptr;
+
+						codeptr = inprop;
+						objonly = false;
+						objtype = GetValue();
+						inprop = codeptr;
+						codeptr = returnaddr;
+
+						flag = 0;
+
+						/* If only an object (or other variable) is
+						   given, and no verbroutine
+						*/
+						if (Peek(inprop)==JUMP_T)
+						{
+							objonly = true;
+							if (!gotone && obj==objtype) flag = 1;
+						}
+
+						/* Otherwise, one or more verbroutines are
+						   specified
+						*/
+						else
+						{
+							while (Peek(inprop)!=JUMP_T)
+							{
+								if (PeekWord(inprop+1)==(unsigned int)var[verbroutine] ||
+
+									/* This is necessary because of the awkward way the pre-v2.2
+									   differentiated non-verbroutine blocks, i.e., with Parse
+									*/
+									((game_version<22) && PeekWord(inprop+1)==(unsigned int)parseaddr && !gotone))
+								{
+									if (obj==objtype) flag = 1;
+								}
+								inprop += 3;
+							}
+						}
+
+						if (flag==1)
+						{
+							gotone = true;
+							ret = 1;
+							returnaddr = codeptr;
+
+							SetStackFrame(stack_depth, RUNROUTINE_BLOCK, 0, 0);
+
+							PassLocals(0);
+#if defined (DEBUGGER)
+							/* Prevent premature stopping */
+							if (debugger_step_over && !debugger_finish)
+								debugger_run = true;
+							
+							if (IsBreakpoint(orig_inprop))
+								complex_prop_breakpoint = true;
+							
+							sprintf(debug_line, "Calling:  %s.%s", objectname[obj], propertyname[p]);
+							trace_complex_prop_routine = true;
+
+							tempdbnest = dbnest;
+							DebugRunRoutine(inprop+3);
+							dbnest = tempdbnest;
+#else
+							RunRoutine(inprop+3);
+#endif
+							retflag = 0;
+							codeptr = returnaddr;
+							g = ret;
+							ret = 0;
+						}
+
+						/* The following used to read "if (!flag || objonly..."
+						   meaning that any non-verbroutine related routines
+						   would fall through regardless of whether they returned
+						   true or false.  I don't recall the rationale for this,
+						   and have therefore removed it.
+						*/
+						if (!flag || (objonly && !g) || ((game_version<22) && PeekWord(inprop-2)==(unsigned int)parseaddr))
+                                                        inprop = (long)PeekWord(inprop+1)*address_scale;
+						else break;
+					}
+				}
+
+				for (i=0; i<MAXLOCALS; i++)
+					var[MAXGLOBALS+i] = templocals[i];
+
+				if (isadditive && !g)
+				{
+					offset = pa + 4;
+					gotone = false;
+					goto GetNextProp;
+				}
+			}
+		}
+	}
+
+NoMorePropMatches:
+
+	defseg = gameseg;
+
+	var[self] = tempself;
+	inexpr = tempinexpr;
+	stack_depth = temp_stack_depth;
+
+	return g;
+}
+
+int Hugo::GrandParent(int obj) {
+	int nextobj;
+
+#if defined (DEBUGGER)
+	if (!CheckObjectRange(obj)) return 0;
+#endif
+	if (obj<0 || obj>=objects) return 0;
+
+	defseg = objtable;
+	while ((nextobj = PeekWord(2 + obj*object_size + object_size-8)) != 0)
+		obj = nextobj;
+	defseg = gameseg;
+
+	return obj;
+}
+
+void Hugo::MoveObj(int obj, int p) {
+	int oldparent, prevobj, s;
+	unsigned int objaddr, parentaddr, lastobjaddr;
+
+	if (obj==p) return;
+	if (obj<0 || obj>=objects) return;
+
+	oldparent = Parent(obj);
+	/* if (oldparent==p) return; */
+
+	objaddr = 2 + obj*object_size;
+
+	/* First, detach the object from its old parent and siblings... */
+
+	prevobj = Elder(obj);
+	s = Sibling(obj);
+	defseg = objtable;
+	if (prevobj)                                    /* sibling */
+		PokeWord(2 + prevobj*object_size + object_size-6, s);
+	else                                            /* child */
+		PokeWord(2 + oldparent*object_size + object_size-4, s);
+
+
+	/* Then move it to the new parent... */
+
+	defseg = objtable;
+	PokeWord(objaddr + object_size-8, p);   /* new parent 		*/
+	PokeWord(objaddr + object_size-6, 0);   /* erase old sibling 	*/
+
+	/* Only operate on the new parent if it isn't object 0 */
+	if (p!=0)
+	{
+
+		/* Object is sole child, or... */
+		if (Child(p)==0)
+		{
+			parentaddr = 2 + p*object_size;
+			defseg = objtable;
+			PokeWord(parentaddr + object_size-4, obj);
+		}
+
+		/* ...object is next sibling. */
+		else
+		{
+			lastobjaddr = 2 + Youngest(p)*object_size;
+			defseg = objtable;
+			PokeWord(lastobjaddr + object_size-6, obj);
+		}
+	}
+}
+
+char *Hugo::Name(int obj) {
+	int p;
+
+	p = GetProp(obj, 0, 1, 0);
+
+	if (p)
+		return GetWord((unsigned int)p);
+	else
+		return 0;
+}
+
+int Hugo::Parent(int obj) {
+	int p;
+
+#if defined (DEBUGGER)
+	if (!CheckObjectRange(obj)) return 0;
+#endif
+	if (obj<0 || obj>=objects) return 0;
+
+	defseg = objtable;
+
+	p = PeekWord(2 + obj*object_size + object_size-8);
+
+	defseg = gameseg;
+
+	return p;
+}
+
+unsigned int Hugo::PropAddr(int obj, int p, unsigned int offset) {
+	unsigned char c;
+	int proplen;
+	unsigned int ptr;
+
+#if defined (DEBUGGER)
+	/* Don't check any non-existent display object (-1) */
+	if (p!=-1) CheckinRange(p, properties, "property");
+	CheckObjectRange(obj);
+#endif
+	/* This way either -1 (the non-existent display object) or a too-high
+	   object will fail
+	*/
+	if (obj<0 || obj>=objects) return 0;
+
+	defseg = objtable;
+
+	/* Position in the property table...
+
+	   i.e., ptr = PeekWord(2 + obj*object_size + (object_size-2));
+	*/
+	ptr = PeekWord(object_size*(obj+1));
+
+	/* ...unless a position has already been given */
+	if (offset) ptr = offset;
+
+	defseg = proptable;
+
+	c = Peek(ptr);
+	while (c != PROP_END && c != (unsigned char)p)
+	{
+		proplen = Peek(ptr + 1);
+
+		/* Property routine address is 1 word */
+		if (proplen==PROP_ROUTINE) proplen = 1;
+
+		ptr += proplen * 2 + 2;
+		c = Peek(ptr);
+	}
+
+	defseg = gameseg;
+
+	if (c==PROP_END)
+		return 0;
+	else
+		return ptr;
+}
+
+void Hugo::PutAttributes(int obj, unsigned long a, int attribute_set) {
+	unsigned int lword, hword;
+
+	hword = (unsigned int)(a/65536L);
+	lword = (unsigned int)(a%65536L);
+
+	defseg = objtable;
+
+	PokeWord(2 + obj*object_size + attribute_set*4, lword);
+	PokeWord(2 + obj*object_size + attribute_set*4 + 2, hword);
+
+	defseg = gameseg;
+}
+
+void Hugo::SetAttribute(int obj, int attr, int c) {
+	unsigned long a, mask;
+
+#if defined (DEBUGGER)
+	if (!CheckObjectRange(obj)) return;
+#endif
+	if (obj<0 || obj>=objects) return;
+
+	a = GetAttributes(obj, attr/32);
+
+	mask = 1L<<(long)(attr%32);
+
+	if (c==1)
+		a = a | mask;
+	else
+	{
+		if (a & mask)
+			a = a ^ mask;
+	}
+
+	PutAttributes(obj, a, attr/32);
+}
+
+int Hugo::Sibling(int obj) {
+	int s;
+
+#if defined (DEBUGGER)
+	if (!CheckObjectRange(obj)) return 0;
+#endif
+	if (obj<0 || obj>=objects) return 0;
+
+	defseg = objtable;
+
+	s = PeekWord(2+ obj*object_size + object_size-6);
+
+	defseg = gameseg;
+
+	return s;
+}
+
+int Hugo::TestAttribute(int obj, int attr, int nattr) {
+	unsigned long a, mask, ta;
+
+#if defined (DEBUGGER)
+	if (!CheckObjectRange(obj)) return 0;
+#endif
+	if (obj<0 || obj>=objects) return 0;
+
+	a = GetAttributes(obj, attr/32);
+
+	mask = 1L<<(attr%32);
+
+	ta = a & mask;
+	if (ta) ta = 1;
+
+	if (nattr) ta = ta ^ 1;
+
+	return (int)ta;
+}
+
+int Hugo::Youngest(int obj) {
+	int nextobj;
+
+	if (Child(obj)==0) return 0;
+
+	nextobj = Child(obj);
+
+	while (Sibling(nextobj))
+		nextobj = Sibling(nextobj);
+
+	return nextobj;
+}
+
+} // End of namespace Hugo
+} // End of namespace Glk
diff --git a/engines/glk/hugo/hugo.cpp b/engines/glk/hugo/hugo.cpp
index 8e81134..a5fbdb9 100644
--- a/engines/glk/hugo/hugo.cpp
+++ b/engines/glk/hugo/hugo.cpp
@@ -26,8 +26,12 @@ namespace Glk {
 namespace Hugo {
 
 Hugo::Hugo(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gameDesc),
-		mainwin(nullptr), currentwin(nullptr), secondwin(nullptr), auxwin(nullptr), address_scale(16),
+		mainwin(nullptr), currentwin(nullptr), secondwin(nullptr), auxwin(nullptr), 
+		runtime_warnings(false), dbnest(0), address_scale(16),
 		SCREENWIDTH(0), SCREENHEIGHT(0), FIXEDCHARWIDTH(0), FIXEDLINEHEIGHT(0),
+		// heexpr
+		evalcount(0), incdec(0), getaddress(0), inexpr(0), inobj(0),
+		// hemisc
 		game_version(0), object_size(0), game(nullptr), script(nullptr), save(nullptr),
 		playback(nullptr), record(nullptr), io(nullptr), ioblock('\0'), ioerror('\0'),
 		codestart(0), objtable(0), eventtable(0), proptable(0), arraytable(0), dicttable(0), 
@@ -52,18 +56,30 @@ Hugo::Hugo(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gam
 		objgrammar(0), objstart(0), objfinish(0), addflag(0), speaking(0), oopscount(0),
 		parse_called_twice(0), reparse_everything(0), full_buffer(false), recursive_call(false),
 		parse_location(0),
-
 		// herun
 		arguments_passed(0), ret(0), retflag(0), during_player_input(false), override_full(0),
 		game_reset(false), stack_depth(0), tail_recursion(0), tail_recursion_addr(0),
 		last_window_top(0), last_window_bottom(0), last_window_left(0), last_window_right(0),
-		lowest_windowbottom(0), physical_lowest_windowbottom(0), just_left_window(false) {
+		lowest_windowbottom(0), physical_lowest_windowbottom(0), just_left_window(false),
+		// heset
+		arrexpr(0), multiprop(0), set_value(0)
+#if defined (DEBUGGER)
+		, debug_eval(false), debug_eval_error(false), debugger_step_over(false),
+		debugger_finish(false), debugger_run(false), currentroutine(false),
+		complex_prop_breakpoint(false), trace_complex_prop_routine(false), properties(0),
+		current_locals(0)
+#endif
+		{
+	// heexpr
+	Common::fill(&eval[0], &eval[MAX_EVAL_ELEMENTS], 0);
+	Common::fill(&var[0], &var[MAXLOCALS + MAXGLOBALS], 0);
+
+	// hemisc		
 	Common::fill(&context_command[0][0], &context_command[MAX_CONTEXT_COMMANDS][64], 0);
 	Common::fill(&id[0], &id[3], '\0');
 	Common::fill(&serial[0], &serial[9], '\0');
 	Common::fill(&pbuffer[0], &pbuffer[MAXBUFFER * 2 + 1], 0);
 	Common::fill(&undostack[0][0], &undostack[MAXUNDO][5], 0);
-	Common::fill(&var[0], &var[MAXLOCALS + MAXGLOBALS], 0);
 	
 	// heparse
 	Common::fill(&buffer[0], &buffer[MAXBUFFER + MAXWORDS], '\0');
@@ -80,6 +96,17 @@ Hugo::Hugo(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gam
 
 	// herun
 	Common::fill(&passlocal[0], &passlocal[MAXLOCALS], 0);
+
+	// heset
+	game_title[0] = '\0';
+
+#ifdef DEBUGGER
+	debug_line[0] = '\0';
+	Common::fill(&objectname[0], &objectname[MAX_OBJECT], nullptr);
+	Common::fill(&propertyname[0], &propertyname[MAX_PROPERTY], nullptr);
+	Common::fill(&codeline[0][0], &codeline[9][100], 0);
+	Common::fill(&localname[0][0], &localname[9][100], 0);
+#endif
 }
 
 void Hugo::runGame() {
diff --git a/engines/glk/hugo/hugo.h b/engines/glk/hugo/hugo.h
index a33c599..de64744 100644
--- a/engines/glk/hugo/hugo.h
+++ b/engines/glk/hugo/hugo.h
@@ -40,6 +40,8 @@ class Hugo : public GlkAPI, public HTokens, public StringFunctions {
 private:
 	winid_t mainwin, currentwin;
 	winid_t secondwin, auxwin;
+	bool runtime_warnings;
+	int dbnest;
 
 	/**
 	 * address_scale refers to the factor by which addresses are multiplied to
@@ -48,6 +50,18 @@ private:
 	 */
 	int address_scale;
 
+	// heexpr
+	int eval[MAX_EVAL_ELEMENTS];		///< expression components
+	int evalcount;						///< # of expr. components
+	int var[MAXLOCALS + MAXGLOBALS];	///< variables
+	int incdec;							///< value is being incremented/dec.
+	char getaddress;					///< true when finding &routine
+	char inexpr;						///< true when in expression
+	char inobj;							///< true when in object compound
+
+	int last_precedence;
+
+
 	// hemisc
 	char gamefile[255];
 	int game_version;
@@ -119,9 +133,6 @@ private:
 	bool just_cleared_screen;
 	int secondwin_bottom;
 
-	// heexpr
-	int var[MAXLOCALS + MAXGLOBALS];
-
 	// heobject
 	int display_object;				///< i.e., non-existent (yet)
 	char display_needs_repaint;		///< for display object
@@ -196,6 +207,32 @@ private:
 		physical_lowest_windowbottom;		///< in pixels or text lines
 	bool just_left_window;
 
+	// heset
+	char game_title[MAX_GAME_TITLE];
+	char arrexpr;							///< true when assigning array
+	char multiprop;							///< true in multiple prop. assign.
+	int set_value;
+
+#if defined (DEBUGGER)
+	char debug_line[MAX_DEBUG_LINE];
+	bool debug_eval;
+	bool debug_eval_error;
+	bool debugger_step_over;
+	bool debugger_finish;
+	bool debugger_run;
+	int currentroutine;
+	bool complex_prop_breakpoint;
+	bool trace_complex_prop_routine;
+	char *objectname[MAX_OBJECT];
+	char *propertyname[MAX_PROPERTY];
+//	CODE code[999];
+	CALL call[999];
+	int properties;
+	WINDOW window[99];
+	int codeline[9][100];
+	char localname[9][100];
+	int current_locals;
+#endif
 private:
 	/**
 	 * \defgroup heglk
@@ -491,6 +528,88 @@ private:
 
 	int Undo();
 
+	/**@}*/
+
+	/**
+	 * \defgroup heobject - Object/property/attribute management functions
+	 * @{
+	 */
+
+#if defined (DEBUGGER)
+	int CheckinRange(uint v1, uint v2, const char *v3) {
+		// TODO: Where the heck is this actualy implemented in Gargoyle
+		return 1;
+	}
+
+	 /**
+	  * Shorthand since many of these object functions may call CheckinRange() if the debugger
+	  * is running and runtime_warnings is set.
+	 */
+	int CheckObjectRange(int obj);
+
+	void DebugRunRoutine(long addr) {}
+
+	void RuntimeWarning(const char *msg) {}
+
+	void DebugMessageBox(const char *title, const char *msg) {}
+
+	bool IsBreakpoint(long loc) const { return false; }
+
+	const char *RoutineName(long loc) { return "Routine"; }
+
+	void AddStringtoCodeWindow(const char *str) {}
+#endif
+
+	int Child(int obj);
+
+	int Children(int obj);
+
+	int Elder(int obj);
+
+	/**
+	 * Returns one of four sets of 32 attributes.
+	 */
+	unsigned long GetAttributes(int obj, int attribute_set);
+
+	/**
+	 * Returns the value of '<obj>.<p> #<n>'  If <s> is true, the self global
+	 * is not set to <obj> in order to facilitate <obj>..<p> calls.
+	 */
+	int GetProp(int obj, int p, int n, char s);
+
+	/**
+	 * Returns the value of the last object above <obj> in the tree before object 0.
+	 */
+	int GrandParent(int obj);
+
+	void MoveObj(int obj, int p);
+
+	char *Name(int obj);
+
+	int Parent(int obj);
+
+	/**
+	 * Returns address of <obj>.<p> (with <offset> provided for additive properties--
+	 * i.e. subsequent calls with the same <obj> and <p>.
+	 */
+	unsigned int PropAddr(int obj, int p, unsigned int offset);
+
+	/**
+	 * Writes (puts) one of four sets of 32 attributes.
+	 */
+	void PutAttributes(int obj, unsigned long a, int attribute_set);
+
+	/**
+	 * Set an attribute
+	 * c = 1 for set, 0 for clear
+	 */
+	void SetAttribute(int obj, int attr, int c);
+
+	int Sibling(int obj);
+
+	int TestAttribute(int obj, int attr, int nattr);
+
+	int Youngest(int obj);
 
 	/**@}*/
 private:
@@ -535,10 +654,7 @@ public:
 	void PromptMore() {}
 	void hugo_stopsample() {}
 	void hugo_stopmusic() {}
-	void SetAttribute(int obj, int attr, int c) {}
-	unsigned int PropAddr(int obj, int p, unsigned int offset) { return 0; }
-	int GetProp(int obj, int p, int n, char s) { return 0; }
-	void MoveObj(int obj, int p) {}
+	int hugo_hasgraphics() { return 0; }
 };
 
 } // End of namespace Hugo
diff --git a/engines/glk/hugo/hugo_defines.h b/engines/glk/hugo/hugo_defines.h
index 260586e..ce2d006 100644
--- a/engines/glk/hugo/hugo_defines.h
+++ b/engines/glk/hugo/hugo_defines.h
@@ -31,9 +31,16 @@ namespace Hugo {
 #define HEVERSION 3
 #define HEREVISION 3
 #define HEINTERIM ".0"
+#define GLK
+#define DEBUGGER
 
 #define MAXOBJLIST 32
 #define MAX_CONTEXT_COMMANDS	32
+#define MAX_EVAL_ELEMENTS 256
+#define MAX_GAME_TITLE 64
+#define MAX_DEBUG_LINE 256
+#define MAX_OBJECT 999
+#define MAX_PROPERTY 999
 #define MAXBUFFER 255
 #define MAXUNDO 1024
 #define CHARWIDTH 1
@@ -140,6 +147,12 @@ browsing.
 #define TAIL_RECURSION_ROUTINE          (-1)
 #define TAIL_RECURSION_PROPERTY         (-2)
 
+#if defined (DEBUGGER)
+#define VIEW_CALLS 0
+#define VIEW_LOCALS 1
+#define CODE_WINDOW 2
+#endif
+
 } // End of namespace Hugo
 } // End of namespace Glk
 
diff --git a/engines/glk/hugo/hugo_types.h b/engines/glk/hugo/hugo_types.h
index 9298510..3d7af86 100644
--- a/engines/glk/hugo/hugo_types.h
+++ b/engines/glk/hugo/hugo_types.h
@@ -107,10 +107,33 @@ struct CODE_BLOCK {
 	int type;			///< see #defines, below
 	long brk;			///< break address, or 0 to indicate NOP
 	long returnaddr;	///< used only for do-while loops
+#if defined (DEBUGGER)
+	int dbnest;			///< for recovering from 'break'
+#endif
+
+	CODE_BLOCK() : type(0), brk(0), returnaddr(0)
+#if defined (DEBUGGER)
+	, dbnest(0)
+#endif
+	{
+	}
+};
+
+#if defined (DEBUGGER)
+struct CALL {
+	long addr;
+	bool param;
 
-	CODE_BLOCK() : type(0), brk(0), returnaddr(0) {}
+	CALL() : addr(0), param(false) {}
 };
 
+struct WINDOW {
+	int count;
+
+	WINDOW() : count(99) {}
+};
+#endif
+
 } // End of namespace Hugo
 } // End of namespace Glk
 
diff --git a/engines/glk/module.mk b/engines/glk/module.mk
index 74ab60f..41c53c3 100644
--- a/engines/glk/module.mk
+++ b/engines/glk/module.mk
@@ -74,6 +74,7 @@ MODULE_OBJS := \
 	hugo/detection.o \
 	hugo/heglk.o \
 	hugo/hemisc.o \
+	hugo/heobject.o \
 	hugo/htokens.o \
 	hugo/hugo.o \
 	hugo/stringfn.o \


Commit: c1f8bd88232c026af207923a4f53bbfca439de13
    https://github.com/scummvm/scummvm/commit/c1f8bd88232c026af207923a4f53bbfca439de13
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-05-11T11:11:23+10:00

Commit Message:
GLK: HUGO: Replace contents of hemisc

Unlike initally where I was ripping out parts of the file and making
code changes, this version is intended to be more identical to the
original, except for the functions being made class methods. This
should make it easier to apply any further upstream code changes to
the codebase in the future

Changed paths:
    engines/glk/hugo/hemisc.cpp
    engines/glk/hugo/hugo.cpp
    engines/glk/hugo/hugo.h
    engines/glk/hugo/hugo_defines.h
    engines/glk/hugo/hugo_types.h


diff --git a/engines/glk/hugo/hemisc.cpp b/engines/glk/hugo/hemisc.cpp
index ac3a0d4..c5a7780 100644
--- a/engines/glk/hugo/hemisc.cpp
+++ b/engines/glk/hugo/hemisc.cpp
@@ -26,108 +26,109 @@ namespace Glk {
 namespace Hugo {
 
 void Hugo::AP(const char *a) {
-		char sticky = false, skipspchar = false, startofline = 0;
-		int i, alen, plen, cwidth;
-		char c = 0;			/* current character */
+	char sticky = false, skipspchar = false, startofline = 0;
+	int i, alen, plen, cwidth;
+	char c = 0;			/* current character */
 #ifdef USE_SMARTFORMATTING
-		char lastc = 0;			/* for smart formatting */
+	char lastc = 0;			/* for smart formatting */
 #endif
 
-		static int lastfcolor = 16, lastbgcolor = 17;
-		static int lastfont = NORMAL_FONT;
-		static int thisline = 0;	/* width in pixels or characters */
-		static int linebreaklen = 0, linebreak = 0;
-		int tempfont;
-		char printed_something = false;
+	static int lastfcolor = 16, lastbgcolor = 17;
+	static int lastfont = NORMAL_FONT;
+	static int thisline = 0;	/* width in pixels or characters */
+	static int linebreaklen = 0, linebreak = 0;
+	int tempfont;
+	char printed_something = false;
 #ifdef USE_TEXTBUFFER
-		int bufferfont = currentfont;
+	int bufferfont = currentfont;
 #endif
 
-		/* Shameless little trick to override control characters in engine-
-		printed text, such as MS-DOS filenames that contain '\'s:
-		*/
-		if (a[0] == NO_CONTROLCHAR)
-		{
-			skipspchar = true;
-			a++;
-		}
 
-		/* Semi-colon overrides LF */
-		if ((strlen(a) >= 2) && a[strlen(a) - 1] == ';' && a[strlen(a) - 2] == '\\')
-		{
-			sticky = true;
-		}
+	/* Shameless little trick to override control characters in engine-
+	   printed text, such as MS-DOS filenames that contain '\'s:
+	*/
+	if (a[0]==NO_CONTROLCHAR)
+	{
+		skipspchar = true;
+		a++;
+	}
 
-		if (hugo_strlen(pbuffer))
-			printed_something = true;
+	/* Semi-colon overrides LF */
+	if ((strlen(a)>=2) && a[strlen(a)-1]==';' && a[strlen(a)-2]=='\\')
+	{
+		sticky = true;
+	}
 
-		plen = strlen(pbuffer);
-		if (plen == 0)
-		{
-			thisline = 0;
-			linebreak = 0;
-			linebreaklen = 0;
-			lastfont = currentfont;
-			startofline = true;
+	if (hugo_strlen(pbuffer))
+		printed_something = true;
+
+	plen = strlen(pbuffer);
+	if (plen==0)
+	{
+		thisline = 0;
+		linebreak = 0;
+		linebreaklen = 0;
+		lastfont = currentfont;
+		startofline = true;
 #ifdef USE_TEXTBUFFER
-			bufferbreak = 0;
-			bufferbreaklen = 0;
+		bufferbreak = 0;
+		bufferbreaklen = 0;
 #endif
 #ifdef USE_SMARTFORMATTING
-			leftquote = true;
+		leftquote = true;
 #endif
-		}
+	}
 
-		/* Check for color changes */
-		if ((a[0]) && (lastfcolor != fcolor || lastbgcolor != bgcolor || startofline))
-		{
-			if (plen >= MAXBUFFER * 2 - 3) FatalError(OVERFLOW_E);
-			pbuffer[plen++] = COLOR_CHANGE;
-			pbuffer[plen++] = (char)(fcolor + 1);
-			pbuffer[plen++] = (char)(bgcolor + 1);
-			pbuffer[plen] = '\0';
-			lastfcolor = fcolor;
-			lastbgcolor = bgcolor;
-		}
+	/* Check for color changes */
+	if ((a[0]) && (lastfcolor!=fcolor || lastbgcolor!=bgcolor || startofline))
+	{
+		if (plen >= MAXBUFFER*2-3) FatalError(OVERFLOW_E);
+		pbuffer[plen++] = COLOR_CHANGE;
+		pbuffer[plen++] = (char)(fcolor+1);
+		pbuffer[plen++] = (char)(bgcolor+1);
+		pbuffer[plen] = '\0';
+		lastfcolor = fcolor;
+		lastbgcolor = bgcolor;
+	}
 
-		/* Check for font changes--since fonts can only get changed
-		by printing, we don't check lastfont */
-		if ((a[0]) && startofline)
-		{
-			if (plen >= MAXBUFFER * 2 - 2) FatalError(OVERFLOW_E);
-			pbuffer[plen++] = FONT_CHANGE;
-			pbuffer[plen++] = (char)(currentfont + 1);
-			pbuffer[plen] = '\0';
-			lastfont = currentfont;
-		}
+	/* Check for font changes--since fonts can only get changed
+	   by printing, we don't check lastfont */
+	if ((a[0]) && startofline)
+	{
+		if (plen >= MAXBUFFER*2-2) FatalError(OVERFLOW_E);
+		pbuffer[plen++] = FONT_CHANGE;
+		pbuffer[plen++] = (char)(currentfont+1);
+		pbuffer[plen] = '\0';
+		lastfont = currentfont;
+	}
+
+	/* Begin by looping through the entire provided string: */
 
-		/* Begin by looping through the entire provided string: */
+	alen = (int)strlen(a);
+	if (sticky)
+		alen -= 2;
 
-		alen = (int)strlen(a);
-		if (sticky)
-			alen -= 2;
+	/* Not printing any actual text, so we won't need to go through
+	   queued font changes
+	*/
+	if (alen==0)
+		lastfont = currentfont;
 
-		/* Not printing any actual text, so we won't need to go through
-		queued font changes
-		*/
-		if (alen == 0)
-			lastfont = currentfont;
+	for (i=0; i<alen; i++)
+	{
+		c = a[i];
+		
+		/* Left-justification */
+		if (thisline==0 && c==' ' && !textto && currentpos==0)
+			continue;
 
-		for (i = 0; i<alen; i++)
+		/* First check control characters */
+		if (c=='\\' && !skipspchar)
 		{
-			c = a[i];
+			c = a[++i];
 
-			/* Left-justification */
-			if (thisline == 0 && c == ' ' && !textto && currentpos == 0)
-				continue;
-
-			/* First check control characters */
-			if (c == '\\' && !skipspchar)
+			switch (c)
 			{
-				c = a[++i];
-
-				switch (c)
-				{
 				case 'n':
 				{
 					c = '\n';
@@ -171,7 +172,7 @@ void Hugo::AP(const char *a) {
 				case 'u':
 				{
 					currentfont &= ~UNDERLINE_FONT;
-				AddFontCode:
+AddFontCode:
 					if (!textto)
 					{
 						int m, n;
@@ -180,43 +181,43 @@ void Hugo::AP(const char *a) {
 
 						if (!printed_something)
 						{
-							for (m = 0; m<plen;)
+							for (m=0; m<plen;)
 							{
-								if (pbuffer[m] == FONT_CHANGE)
+								if (pbuffer[m]==FONT_CHANGE)
 								{
-									for (n = m; n<plen - 2; n++)
+									for (n=m; n<plen-2; n++)
 									{
-										pbuffer[n] = pbuffer[n + 2];
+										pbuffer[n] = pbuffer[n+2];
 									}
-									plen -= 2;
+									plen-=2;
 									pbuffer[plen] = '\0';
 									lastfont = currentfont;
 								}
-								else if (pbuffer[m] == COLOR_CHANGE)
-									m += 3;
+								else if (pbuffer[m]==COLOR_CHANGE)
+									m+=3;
 								else
 									break;
 							}
 						}
 #ifdef USE_TEXTBUFFER
-						if (hugo_strlen(pbuffer + bufferbreak) == 0)
+						if (hugo_strlen(pbuffer+bufferbreak)==0)
 							bufferfont = currentfont;
 #endif
-						if (plen >= MAXBUFFER * 2 - 2) FatalError(OVERFLOW_E);
-						pbuffer[plen + 2] = '\0';
-						pbuffer[plen + 1] = (char)(currentfont + 1);
+						if (plen >= MAXBUFFER*2-2) FatalError(OVERFLOW_E);
+						pbuffer[plen+2] = '\0';
+						pbuffer[plen+1] = (char)(currentfont+1);
 						pbuffer[plen] = FONT_CHANGE;
-						plen += 2;
+						plen+=2;
 
 						/* Convert full if font height changes, since
-						the amount of used screen real estate for
-						this particular font (in terms of lines)
-						will have changed:
+						   the amount of used screen real estate for
+						   this particular font (in terms of lines)
+						   will have changed:
 						*/
 						ratio = (double)lineheight;
 						hugo_font(currentfont);
 						ratio /= (double)lineheight;
-						newfull = (int)(((double)full)*ratio + 0.5);
+						newfull = (int)(((double)full)*ratio+0.5);
 						if (newfull) full = newfull;
 					}
 					continue;
@@ -229,618 +230,701 @@ void Hugo::AP(const char *a) {
 				}
 				default:
 					c = SpecialChar(a, &i);
-				}
-			}
-			else if (game_version <= 22)
-			{
-				if (c == '~')
-					c = '\"';
-				else if (c == '^')
-					c = '\n';
 			}
+		}
+		else if (game_version<=22)
+		{
+			if (c=='~')
+				c = '\"';
+			else if (c=='^')
+				c = '\n';
+		}
 
-			/* Add the new character */
+	/* Add the new character */
 
-			/* Text may be sent to an address in the array table instead
-			of being output to the screen
-			*/
-			if (textto)
-			{
-				/* space for array length */
-				int n = (game_version>23) ? 2 : 0;
+		/* Text may be sent to an address in the array table instead
+		   of being output to the screen
+		*/
+		if (textto)
+		{
+			/* space for array length */
+			int n = (game_version>23)?2:0;
 
-				if (c == '\n')
-				{
-					SETMEM(arraytable * 16L + textto * 2 + n, 0);
-					textto++;
-				}
-				else if ((unsigned char)c >= ' ')
-				{
-					SETMEM(arraytable * 16L + textto * 2 + n, c);
-					textto++;
-				}
-				/* Add a terminating zero in case we don't
-				print any more to the array */
-				SETMEM(arraytable * 16L + textto * 2 + n, 0);
+			if (c=='\n')
+			{
+				SETMEM(arraytable*16L + textto*2 + n, 0);
+				textto++;
+			}			
+			else if ((unsigned char)c >= ' ')
+			{
+				SETMEM(arraytable*16L + textto*2 + n, c);
+				textto++;
+			}
+			/* Add a terminating zero in case we don't
+			   print any more to the array */
+			SETMEM(arraytable*16L + textto*2 + n, 0);
 
-				if (i >= alen) return;
+			if (i >= alen) return;
 
-				continue;       /* back to for (i=0; i<slen; i++) */
-			}
+			continue;       /* back to for (i=0; i<slen; i++) */
+		}
 
-			printed_something = true;
+		printed_something = true;
 
-			/* Handle in-text newlines */
-			if (c == '\n')
-			{
-				hugo_font(currentfont = lastfont);
+		/* Handle in-text newlines */
+		if (c=='\n')
+		{
+			hugo_font(currentfont = lastfont);
 #ifdef USE_TEXTBUFFER
-				TB_AddWord(pbuffer + bufferbreak,
-					current_text_x + bufferbreaklen,
-					current_text_y,
-					current_text_x + thisline - 1,
-					current_text_y + lineheight - 1);
-#endif
-				Printout(pbuffer);
-				lastfont = currentfont;
+			TB_AddWord(pbuffer+bufferbreak,
+				current_text_x+bufferbreaklen,
+				current_text_y,
+				current_text_x+thisline-1,
+				current_text_y+lineheight-1);
+#endif
+			Printout(pbuffer, 0);
+			lastfont = currentfont;
 #ifdef USE_TEXTBUFFER
-				bufferfont = currentfont;
-				bufferbreak = 0;
-				bufferbreaklen = 0;
-#endif
-				strcpy(pbuffer, "");
-				plen = 0;
-				linebreak = 0;
-				linebreaklen = 0;
-				thisline = 0;
+			bufferfont = currentfont;
+			bufferbreak = 0;
+			bufferbreaklen = 0;
+#endif
+			strcpy(pbuffer, "");
+			plen = 0;
+			linebreak = 0;
+			linebreaklen = 0;
+			thisline = 0;
 #ifdef USE_SMARTFORMATTING
-				leftquote = true;
-				lastc = '\n';
+			leftquote = true;
+			lastc = '\n';
 #endif
-				pbuffer[plen++] = COLOR_CHANGE;
-				pbuffer[plen++] = (char)(fcolor + 1);
-				pbuffer[plen++] = (char)(bgcolor + 1);
-				pbuffer[plen] = '\0';
+			pbuffer[plen++] = COLOR_CHANGE;
+			pbuffer[plen++] = (char)(fcolor+1);
+			pbuffer[plen++] = (char)(bgcolor+1);
+			pbuffer[plen] = '\0';
 
 
-				continue;
-			}
+			continue;
+		}
 
 #ifdef USE_SMARTFORMATTING
-			/* Smart formatting only for non-fixed fonts */
-			if ((currentfont & PROP_FONT) && smartformatting)
+		/* Smart formatting only for non-fixed fonts */
+		if ((currentfont & PROP_FONT) && smartformatting)
+		{
+			if ((!strncmp(a+i, "--", 2)) && lastc!='-' && strncmp(a+i, "---", 3))
 			{
-				if ((!strncmp(a + i, "--", 2)) && lastc != '-' && strncmp(a + i, "---", 3))
-				{
-					lastc = '-';
-					c = (char)151;
-					i++;
-					leftquote = false;
-				}
-				else if (c == '\"')
-				{
-					if (leftquote)
-						c = (char)147;
-					else
-						c = (char)148;
-					leftquote = false;
-					lastc = c;
-				}
-				else if (c == '\'')
-				{
-					if (leftquote)
-						c = (char)145;
-					else
-						c = (char)146;
-					leftquote = false;
-					lastc = c;
-				}
+				lastc = '-';
+				c = (char)151;
+				i++;
+				leftquote = false;
+			}
+			else if (c=='\"')
+			{
+				if (leftquote)
+					c = (char)147;
 				else
-				{
-					if (c == ' ')
-						leftquote = true;
-					else
-						leftquote = false;
-					lastc = c;
-				}
+					c = (char)148;
+				leftquote = false;
+				lastc = c;
+			}
+			else if (c=='\'')
+			{
+				if (leftquote)
+					c = (char)145;
+				else
+					c = (char)146;
+				leftquote = false;
+				lastc = c;
+			}
+			else
+			{
+				if (c==' ')
+					leftquote = true;
+				else
+					leftquote = false;
+				lastc = c;
 			}
+		}
 #endif
 
-			/* Add the new character to the printing buffer */
-			if (plen >= MAXBUFFER * 2 - 1) FatalError(OVERFLOW_E);
-			pbuffer[plen + 1] = '\0';
-			pbuffer[plen] = c;
-			plen++;
-
-			cwidth = hugo_charwidth(c);
+		/* Add the new character to the printing buffer */
+		if (plen >= MAXBUFFER*2-1) FatalError(OVERFLOW_E);
+		pbuffer[plen+1] = '\0';
+		pbuffer[plen] = c;
+		plen++;
 
-			/* Check to see if we've overrun the current line */
+		cwidth = hugo_charwidth(c);
 
-			if (thisline + cwidth + currentpos > physical_windowwidth)
-			{
-				char t;
+	/* Check to see if we've overrun the current line */
 
-				if (!linebreak)
-				{
-					linebreak = plen - 1;
-					linebreaklen = thisline;
-				}
+		if (thisline+cwidth+currentpos > physical_windowwidth)
+		{
+			char t;
 
-				t = pbuffer[linebreak];
-				pbuffer[linebreak] = '\0';
-
-				tempfont = currentfont;
-				hugo_font(currentfont = lastfont);
-				Printout(pbuffer);
-				lastfont = currentfont;
-				hugo_font(currentfont = tempfont);
-
-				pbuffer[linebreak] = t;
-				plen = strlen(pbuffer + linebreak);
-				memmove(pbuffer, pbuffer + linebreak, plen + 1);
-				thisline = thisline - linebreaklen;
-				linebreak = 0;
-				linebreaklen = 0;
-				startofline = 0;
-#ifdef USE_TEXTBUFFER
-				bufferbreak = 0;
-				bufferbreaklen = 0;
-#endif
+			if (!linebreak)
+			{
+				linebreak = plen-1;
+				linebreaklen = thisline;
 			}
 
-			thisline += cwidth;
+			t = pbuffer[linebreak];
+			pbuffer[linebreak] = '\0';
 
+			tempfont = currentfont;
+			hugo_font(currentfont = lastfont);
+			Printout(pbuffer, linebreak);
+			lastfont = currentfont;
+			hugo_font(currentfont = tempfont);
+
+			pbuffer[linebreak] = t;
+			plen = strlen(pbuffer+linebreak);
+			memmove(pbuffer, pbuffer+linebreak, plen + 1);
+			thisline = thisline - linebreaklen;
+			linebreak = 0;
+			linebreaklen = 0;
+			startofline = 0;
 #ifdef USE_TEXTBUFFER
-			if ((c == ' ' || c == FORCED_SPACE) ||
-				(c == '/' && a[i + 1] != '/') || (c == '-' && a[i + 1] != '-'))
-			{
-				TB_AddWord(pbuffer + bufferbreak,
-					current_text_x + bufferbreaklen,
-					current_text_y,
-					current_text_x + thisline - 1,
-					current_text_y + lineheight - 1);
-
-				bufferbreak = plen;
-				bufferbreaklen = thisline;
-				bufferfont = currentfont;
-			}
+			bufferbreak = 0;
+			bufferbreaklen = 0;
 #endif
-			if ((c == ' ') || (c == '/' && a[i + 1] != '/') || (c == '-' && a[i + 1] != '-'))
-			{
-				linebreak = plen, linebreaklen = thisline;
-			}
 		}
 
+		thisline += cwidth;
+
 #ifdef USE_TEXTBUFFER
-		if (!sticky || alen > 1)
+		if ((c==' ' || c==FORCED_SPACE) ||
+			(c=='/' && a[i+1]!='/') || (c=='-' && a[i+1]!='-'))
 		{
-			tempfont = currentfont;
-			currentfont = bufferfont;
-
-			TB_AddWord(pbuffer + bufferbreak,
-				current_text_x + bufferbreaklen,
+			TB_AddWord(pbuffer+bufferbreak,
+				current_text_x+bufferbreaklen,
 				current_text_y,
-				current_text_x + thisline - 1,
-				current_text_y + lineheight - 1);
+				current_text_x+thisline-1,
+				current_text_y+lineheight-1);
 
 			bufferbreak = plen;
 			bufferbreaklen = thisline;
-			currentfont = tempfont;
+			bufferfont = currentfont;
 		}
 #endif
-		if (!sticky)
+		if ((c==' ') || (c=='/' && a[i+1]!='/') || (c=='-' && a[i+1]!='-'))
 		{
-			hugo_font(currentfont = lastfont);
-			Printout(pbuffer);
-			lastfont = currentfont;
-			strcpy(pbuffer, "");
-			linebreak = 0;
-			linebreaklen = 0;
-			thisline = 0;
-			plen = 0;
+			linebreak = plen, linebreaklen = thisline;
+		}
+	}
+
 #ifdef USE_TEXTBUFFER
-			bufferbreak = 0;
-			bufferbreaklen = 0;
+	if (!sticky || alen > 1)
+	{
+		tempfont = currentfont;
+		currentfont = bufferfont;
+
+		TB_AddWord(pbuffer+bufferbreak,
+			current_text_x+bufferbreaklen,
+			current_text_y,
+			current_text_x+thisline-1,
+			current_text_y+lineheight-1);
+
+		bufferbreak = plen;
+		bufferbreaklen = thisline;
+		currentfont = tempfont;
+	}
+#endif
+	if (!sticky)
+	{
+		hugo_font(currentfont = lastfont);
+		Printout(pbuffer, 0);
+		lastfont = currentfont;
+		strcpy(pbuffer, "");
+		linebreak = 0;
+		linebreaklen = 0;
+		thisline = 0;
+		plen = 0;
+#ifdef USE_TEXTBUFFER
+		bufferbreak = 0;
+		bufferbreaklen = 0;
 #endif
 #ifdef USE_SMARTFORMATTING
-			leftquote = true;
+		leftquote = true;
 #endif
-		}
 	}
+}
 
 int Hugo::CallRoutine(unsigned int addr) {
-		int arg, i;
-		int val;
-		int templocals[MAXLOCALS], temppass[MAXLOCALS];
-		int temp_stack_depth;
-		long tempptr;
-		int potential_tail_recursion = tail_recursion;
+	int arg, i;
+	int val;
+	int templocals[MAXLOCALS], temppass[MAXLOCALS];
+	int temp_stack_depth;
+	long tempptr;
+	int potential_tail_recursion = tail_recursion;
 #if defined (DEBUGGER)
-		int tempdbnest;
+	int tempdbnest;
 #endif
-		arg = 0;
-		tail_recursion = 0;
+	arg = 0;
+	tail_recursion = 0;
 
-		/* Pass local variables to routine, if specified */
-		if (MEM(codeptr) == OPEN_BRACKET_T)
+	/* Pass local variables to routine, if specified */
+	if (MEM(codeptr)==OPEN_BRACKET_T)
+	{
+		codeptr++;
+		while (MEM(codeptr) != CLOSE_BRACKET_T)
 		{
-			codeptr++;
-			while (MEM(codeptr) != CLOSE_BRACKET_T)
+			if (arg)
 			{
-				if (arg)
-				{
-					for (i = 0; i<arg; i++)
-						temppass[i] = passlocal[i];
-				}
-
-				passlocal[arg++] = GetValue();
+				for (i=0; i<arg; i++)
+					temppass[i] = passlocal[i];
+			}
 
-				if (arg > 1)
-				{
-					for (i = 0; i<arg - 1; i++)
-						passlocal[i] = temppass[i];
-				}
+			passlocal[arg++] = GetValue();
 
-				if (MEM(codeptr) == COMMA_T) codeptr++;
+			if (arg > 1)
+			{
+				for (i=0; i<arg-1; i++)
+					passlocal[i] = temppass[i];
 			}
-			codeptr++;
-		}
 
-		/* TAIL_RECURSION_ROUTINE if we came from a routine call immediately
-		following a 'return' statement...
-		*/
-		tail_recursion = potential_tail_recursion;
-		if (tail_recursion == TAIL_RECURSION_ROUTINE && MEM(codeptr) == EOL_T)
-		{
-			tail_recursion_addr = (long)addr*address_scale;
-			PassLocals(arg);
-			return 0;
-		}
-		/* ...but if we're not immediately followed by and end-of-line marker,
-		cancel the pending tail-recursion
-		*/
-		else
-		{
-			tail_recursion = 0;
+			if (MEM(codeptr)==COMMA_T) codeptr++;
 		}
+		codeptr++;
+	}
 
-		for (i = 0; i<MAXLOCALS; i++)
-			templocals[i] = var[MAXGLOBALS + i];
+	/* TAIL_RECURSION_ROUTINE if we came from a routine call immediately
+	   following a 'return' statement...
+	*/
+	tail_recursion = potential_tail_recursion;
+	if (tail_recursion==TAIL_RECURSION_ROUTINE && MEM(codeptr)==EOL_T)
+	{
+		tail_recursion_addr = (long)addr*address_scale;
 		PassLocals(arg);
+		return 0;
+	}
+	/* ...but if we're not immediately followed by and end-of-line marker,
+	   cancel the pending tail-recursion
+	*/
+	else
+	{
+		tail_recursion = 0;
+	}
 
-		temp_stack_depth = stack_depth;
+	for (i=0; i<MAXLOCALS; i++)
+		templocals[i] = var[MAXGLOBALS+i];
+	PassLocals(arg);
 
-		SetStackFrame(stack_depth, RUNROUTINE_BLOCK, 0, 0);
+	temp_stack_depth = stack_depth;
 
-		tempptr = codeptr;      /* store calling address */
-		ret = 0;
+	SetStackFrame(stack_depth, RUNROUTINE_BLOCK, 0, 0);
+
+	tempptr = codeptr;      /* store calling address */
+	ret = 0;
 
 #if defined (DEBUGGER)
-		tempdbnest = dbnest;
-		DebugRunRoutine((long)addr*address_scale);
-		dbnest = tempdbnest;
+	tempdbnest = dbnest;
+	DebugRunRoutine((long)addr*address_scale);
+	dbnest = tempdbnest;
 #else
-		RunRoutine((long)addr*address_scale);
+	RunRoutine((long)addr*address_scale);
 #endif
-		retflag = 0;
-		val = ret;
-		codeptr = tempptr;
+	retflag = 0;
+	val = ret;
+	codeptr = tempptr;
 
-		stack_depth = temp_stack_depth;
+	stack_depth = temp_stack_depth;
 
-		for (i = 0; i<MAXLOCALS; i++)
-			var[MAXGLOBALS + i] = templocals[i];
+	for (i=0; i<MAXLOCALS; i++)
+		var[MAXGLOBALS+i] = templocals[i];
 
-		return val;
-	}
+	return val;
+}
 
 void Hugo::ContextCommand() {
-		unsigned int n;
-
-	ContextCommandLoop:
+	unsigned int n;
 
-		codeptr++;
+ContextCommandLoop:
 
-		n = GetValue();
+	codeptr++;
+	
+	n = GetValue();
 #if !defined (COMPILE_V25)
-		if (n == 0)
-		{
-			context_commands = 0;
-		}
-		else if (context_commands < MAX_CONTEXT_COMMANDS)
-		{
-			char *cc;
+	if (n==0)
+	{
+		context_commands = 0;
+	}
+	else if (context_commands < MAX_CONTEXT_COMMANDS)
+	{
+		char *cc;
 
-			strncpy(context_command[context_commands], cc = GetWord(n), 64);
-			context_command[context_commands][63] = '\0';
-			if (strlen(cc) >= 64)
-				sprintf(context_command[context_commands] + 60, "...");
-			context_commands++;
-		}
-#endif
-		if (Peek(codeptr) == COMMA_T) goto ContextCommandLoop;
-		codeptr++;
+		strncpy(context_command[context_commands], cc = GetWord(n), 64);
+		context_command[context_commands][63] = '\0';
+		if (strlen(cc)>=64)
+			sprintf(context_command[context_commands]+60, "...");
+		context_commands++;
 	}
+#endif
+	if (Peek(codeptr)==COMMA_T) goto ContextCommandLoop;
+	codeptr++;
+}
 
 unsigned int Hugo::Dict() {
-		int i, len = 256;
-		unsigned int arr;
-		unsigned int pos = 2, loc;
+	int i, len = 256;
+	unsigned int arr;
+	unsigned int pos = 2, loc;
 
-		codeptr += 2;                           /* "(" */
+	codeptr += 2;                           /* "(" */
 
-		if (MEM(codeptr) == PARSE_T || MEM(codeptr) == WORD_T)
-			strcpy(line, GetWord(GetValue()));
-		else
+	if (MEM(codeptr)==PARSE_T || MEM(codeptr)==WORD_T)
+		strcpy(line, GetWord(GetValue()));
+	else
+	{
+		/* Get the array address to read the to-be-
+		   created dictionary entry from:
+		*/
+		arr = GetValue();
+		if (game_version>=22)
 		{
-			/* Get the array address to read the to-be-
-			created dictionary entry from:
+			/* Convert the address to a word
+			   value:
 			*/
-			arr = GetValue();
-			if (game_version >= 22)
-			{
-				/* Convert the address to a word
-				value:
-				*/
-				arr *= 2;
-
-				if (game_version >= 23)
-					/* space for array length */
-					arr += 2;
-			}
+			arr*=2;
 
-			defseg = arraytable;
-			for (i = 0; i<len && PeekWord(arr + i * 2) != 0; i++)
-				line[i] = (char)PeekWord(arr + i * 2);
-			defseg = gameseg;
-			line[i] = '\0';
+			if (game_version>=23)
+				/* space for array length */
+				arr+=2;
 		}
 
-		if (Peek(codeptr) == COMMA_T) codeptr++;
-		len = GetValue();
+		defseg = arraytable;
+		for (i=0; i<len && PeekWord(arr+i*2)!=0; i++)
+			line[i] = (char)PeekWord(arr+i*2);
+		defseg = gameseg;
+		line[i] = '\0';
+	}
 
-		if ((loc = FindWord(line)) != UNKNOWN_WORD) return loc;
+	if (Peek(codeptr)==COMMA_T) codeptr++;
+	len = GetValue();
 
-		defseg = dicttable;
+	if ((loc = FindWord(line))!=UNKNOWN_WORD) return loc;
 
-		for (i = 1; i <= dictcount; i++)
-			pos += Peek(pos) + 1;
+	defseg = dicttable;
 
-		loc = pos - 2;
+	for (i=1; i<=dictcount; i++)
+		pos += Peek(pos) + 1;
 
-		if ((long)(pos + strlen(line)) > (long)(codeend - dicttable * 16L))
-		{
+	loc = pos - 2;
+	
+	if ((long)(pos+strlen(line)) > (long)(codeend-dicttable*16L))
+	{
 #ifdef DEBUGGER
-			sprintf(debug_line, "$MAXDICTEXTEND dictionary space exceeded");
-			RuntimeWarning(debug_line);
+		sprintf(debug_line, "$MAXDICTEXTEND dictionary space exceeded");
+		RuntimeWarning(debug_line);
 #endif
-			defseg = gameseg;
-			return 0;
-		}
+		defseg = gameseg;
+		return 0;
+	}
 
-		Poke(pos++, (unsigned char)strlen(line));
-		for (i = 0; i<(int)strlen(line) && i<len; i++)
-			Poke(pos++, (unsigned char)(line[i] + CHAR_TRANSLATION));
-		PokeWord(0, ++dictcount);
+	Poke(pos++, (unsigned char)strlen(line));
+	for (i=0; i<(int)strlen(line) && i<len; i++)
+		Poke(pos++, (unsigned char)(line[i]+CHAR_TRANSLATION));
+	PokeWord(0, ++dictcount);
 
-		defseg = gameseg;
+	defseg = gameseg;
 
-		SaveUndo(DICT_T, strlen(line), 0, 0, 0);
+	SaveUndo(DICT_T, strlen(line), 0, 0, 0);
 
-		return loc;
-	}
+	return loc;
+}
 
-void Hugo::FatalError(int n){
+void Hugo::FatalError(int n) {
 	char fatalerrorline[64];
 
+#if defined (DEBUGGER)
+	hugo_stopmusic();
+	hugo_stopsample();
+
+	/* If the Debugger has already issued an error, it will
+	   try to recover instead of issuing a stream of
+	   identical errors.
+	*/
+	if (runtime_error) return;
+#else
 	hugo_cleanup_screen();
+#endif
 
 	switch (n)
 	{
-	case MEMORY_E:
-	{sprintf(line, "Out of memory\n");
-	break; }
+		case MEMORY_E:
+			{sprintf(line, "Out of memory\n");
+			break;}
+
+		case OPEN_E:
+			{sprintf(line, "Cannot open file\n");
+			break;}
+
+		case READ_E:
+			{sprintf(line, "Cannot read from file\n");
+			break;}
 
-	case OPEN_E:
-	{sprintf(line, "Cannot open file\n");
-	break; }
+		case WRITE_E:
+			{sprintf(line, "Cannot write to save file\n");
+			break;}
 
-	case READ_E:
-	{sprintf(line, "Cannot read from file\n");
-	break; }
+		case EXPECT_VAL_E:
+			{sprintf(line, "Expecting value at $%s\n", PrintHex(codeptr));
+			break;}
 
-	case WRITE_E:
-	{sprintf(line, "Cannot write to save file\n");
-	break; }
+		case UNKNOWN_OP_E:
+			{sprintf(line, "Unknown operation at $%s\n", PrintHex(codeptr));
+			break;}
 
-	case EXPECT_VAL_E:
-	{sprintf(line, "Expecting value at $%s\n", PrintHex(codeptr));
-	break; }
+		case ILLEGAL_OP_E:
+			{sprintf(line, "Illegal operation at $%s\n", PrintHex(codeptr));
+			break;}
 
-	case UNKNOWN_OP_E:
-	{sprintf(line, "Unknown operation at $%s\n", PrintHex(codeptr));
-	break; }
+		case OVERFLOW_E:
+			{sprintf(line, "Overflow at $%s\n", PrintHex(codeptr));
+			break;}
 
-	case ILLEGAL_OP_E:
-	{sprintf(line, "Illegal operation at $%s\n", PrintHex(codeptr));
-	break; }
+		case DIVIDE_E:
+			{sprintf(line, "Divide by zero at $%s\n", PrintHex(codeptr));
+			break;}
+	}
+
+#if defined (DEBUGGER)
+
+	if (routines > 0)
+        {
+		SwitchtoDebugger();
+
+		if (n==MEMORY_E) DebuggerFatal(D_MEMORY_ERROR);
+
+		RuntimeWarning(line);
+		debugger_interrupt = true;
+		debugger_skip = true;
+		runtime_error = true;
 
-	case OVERFLOW_E:
-	{sprintf(line, "Overflow at $%s\n", PrintHex(codeptr));
-	break; }
+		if (n!=EXPECT_VAL_E)
+			RecoverLastGood();
 
-	case DIVIDE_E:
-	{sprintf(line, "Divide by zero at $%s\n", PrintHex(codeptr));
-	break; }
+		codeptr = this_codeptr;
+
+		return;
 	}
 
+	hugo_cleanup_screen();
+#endif
+
+/* crash dump */
+/*
+if (n==UNKNOWN_OP_E || n==ILLEGAL_OP_E || n==EXPECT_VAL_E || n==OVERFLOW_E)
+{
+	for (n=-8; n<0; n++)
+		fprintf(stderr, " %c%2X", (n==-8)?'$':' ', MEM(codeptr+n));
+	fprintf(stderr, "\n");
+	for (n=-8; n<0; n++)
+		fprintf(stderr, " %3d", MEM(codeptr+n));
+	fprintf(stderr, "\n\n> %2X", MEM(codeptr));
+	for (n=1; n<8; n++)
+		fprintf(stderr, "  %2X", MEM(codeptr+n));
+	fprintf(stderr, "\n");
+	fprintf(stderr, ">%3d", MEM(codeptr));
+	for (n=1; n<8; n++)
+		fprintf(stderr, " %3d", MEM(codeptr+n));
+	fprintf(stderr, "\n");
+}
+*/
 	sprintf(fatalerrorline, "\nFatal Error:  %s", line);
-	error(fatalerrorline);
+	PRINTFATALERROR(fatalerrorline);
+
+	hugo_closefiles();
+	hugo_blockfree(mem);
+	mem = NULL;
+	error("Error code: %d", (int)n);
 }
 
 void Hugo::FileIO() {
-		char fileiopath[MAXPATH];
-		char iotype;
-		unsigned int fnameval;
-		long skipaddr;
-		int i, temp_stack_depth = stack_depth;
-
-		iotype = MEM(codeptr++);
-		skipaddr = (long)PeekWord(codeptr)*address_scale;
-		codeptr += 2;
-		fnameval = GetValue();
-		if (game_version >= 23) codeptr++; /* eol */
-
-		ioerror = 0;
-
-		/* Make sure the filename is legal, 8 alphanumeric characters or less */
-		strcpy(line, GetWord(fnameval));
-		if (strlen(line) > 8) goto LeaveFileIO;
-		for (i = 0; i<(int)strlen(line); i++)
+#ifndef GLK
+	char drive[MAXDRIVE], dir[MAXDIR], fname[MAXFILENAME], ext[MAXEXT];
+#endif
+	char fileiopath[MAXPATH];
+	char iotype;
+	unsigned int fnameval;
+	long skipaddr;
+	int i, temp_stack_depth = stack_depth;
+#if defined (DEBUGGER)
+	int tempdbnest;
+#endif
+
+	iotype = MEM(codeptr++);
+	skipaddr = (long)PeekWord(codeptr)*address_scale;
+	codeptr+=2;
+	fnameval = GetValue();
+	if (game_version>=23) codeptr++; /* eol */
+
+	ioerror = 0;
+	
+	/* Make sure the filename is legal, 8 alphanumeric characters or less */
+	strcpy(line, GetWord(fnameval));
+	if (strlen(line) > 8) goto LeaveFileIO;
+	for (i=0; i<(int)strlen(line); i++)
+	{
+		if ((line[i]>='0' && line[i]<='9') || (line[i]>='A' && line[i]<='Z') ||
+			(line[i]>='a' && line[i]<='z'))
 		{
-			if ((line[i] >= '0' && line[i] <= '9') || (line[i] >= 'A' && line[i] <= 'Z') ||
-				(line[i] >= 'a' && line[i] <= 'z'))
-			{
-				continue;
-			}
-			else
-				goto LeaveFileIO;
+			continue;
 		}
+		else
+			goto LeaveFileIO;
+	}
 
-		if (ioblock) goto LeaveFileIO;  /* can't nest file operations */
-
-		strcpy(fileiopath, GetWord(fnameval));
-
-		if (iotype == WRITEFILE_T)        /* "writefile" */
-		{
-			/* Glk implementation */
-			frefid_t fref = nullptr;
+	if (ioblock) goto LeaveFileIO;  /* can't nest file operations */
 
-			fref = glk_fileref_create_by_name(fileusage_Data | fileusage_BinaryMode,
-				fileiopath, 0);
-			io = glk_stream_open_file(fref, filemode_Write, 0);
-			glk_fileref_destroy(fref);
-			if (io == nullptr) goto LeaveFileIO;
+#if !defined (GLK)
+	hugo_splitpath(program_path, drive, dir, fname, ext);
+	hugo_makepath(fileiopath, drive, dir, GetWord(fnameval), "");
+#else
+	strcpy(fileiopath, GetWord(fnameval));
+#endif
 
-			ioblock = 1;
-		}
-		else                            /* "readfile"  */
-		{
-			/* Glk implementation */
-			frefid_t fref = nullptr;
+	if (iotype==WRITEFILE_T)        /* "writefile" */
+	{
+#if !defined (GLK)
+		/* stdio implementation */
+		if ((io = HUGO_FOPEN(fileiopath, "wb"))==NULL) goto LeaveFileIO;
+#else
+		/* Glk implementation */
+		frefid_t fref = NULL;
+
+		fref = glk_fileref_create_by_name(fileusage_Data | fileusage_BinaryMode,
+			fileiopath, 0);
+		io = glk_stream_open_file(fref, filemode_Write, 0);
+		glk_fileref_destroy(fref);
+		if (io==NULL) goto LeaveFileIO;
+#endif
+		ioblock = 1;
+	}
+	else                            /* "readfile"  */
+	{
+#if !defined (GLK)
+		/* stdio implementation */
+		if ((io = HUGO_FOPEN(fileiopath, "rb"))==NULL) goto LeaveFileIO;
+#else
+		/* Glk implementation */
+		frefid_t fref = NULL;
+
+		fref = glk_fileref_create_by_name(fileusage_Data | fileusage_BinaryMode,
+			fileiopath, 0);
+		io = glk_stream_open_file(fref, filemode_Read, 0);
+		glk_fileref_destroy(fref);
+		if (io==NULL) goto LeaveFileIO;
+#endif
+		ioblock = 2;
+	}
 
-			fref = glk_fileref_create_by_name(fileusage_Data | fileusage_BinaryMode,
-				fileiopath, 0);
-			if (glk_fileref_does_file_exist(fref))
-				io = glk_stream_open_file(fref, filemode_Read, 0);
-			else
-				io = nullptr;
-			glk_fileref_destroy(fref);
-			if (io == nullptr) goto LeaveFileIO;
+#if defined (DEBUGGER)
+	tempdbnest = dbnest++;
+#endif
 
-			ioblock = 2;
-		}
+	SetStackFrame(stack_depth, RUNROUTINE_BLOCK, 0, 0);
 
-		SetStackFrame(stack_depth, RUNROUTINE_BLOCK, 0, 0);
+	RunRoutine(codeptr);
 
-		RunRoutine(codeptr);
+#if defined (DEBUGGER)
+	dbnest = tempdbnest;
+#endif
 
-		stack_depth = temp_stack_depth;
+	stack_depth = temp_stack_depth;
 
-		if (ioerror) retflag = 0;
+	if (ioerror) retflag = 0;
 
-		delete io;
-		io = nullptr;
-		ioblock = 0;
+	hugo_fclose(io);
+	io = NULL;
+	ioblock = 0;
 
-	LeaveFileIO:
-		ioerror = 0;
-		codeptr = skipaddr;
-	}
+LeaveFileIO:
+	ioerror = 0;
+	codeptr = skipaddr;
+}
 
 void Hugo::Flushpbuffer() {
-		if (pbuffer[0] == '\0') return;
+	if (pbuffer[0]=='\0') return;
 
 #ifdef USE_TEXTBUFFER
-		/* For (single) characters left over from AP(), when Flushpbuffer() gets
-		called, say, from RunWindow()
-		*/
-		if (bufferbreaklen && pbuffer + bufferbreaklen && !(currentfont&PROP_FONT))
-		{
-			TB_AddWord(pbuffer + bufferbreak,
-				current_text_x + bufferbreaklen,
-				current_text_y,
-				current_text_x + bufferbreaklen + FIXEDCHARWIDTH,
-				current_text_y + lineheight - 1);
-		}
+	/* For (single) characters left over from AP(), when Flushpbuffer() gets
+	   called, say, from RunWindow()
+	*/
+	if (bufferbreaklen && pbuffer+bufferbreaklen && !(currentfont&PROP_FONT))
+	{
+		TB_AddWord(pbuffer+bufferbreak,
+			current_text_x+bufferbreaklen,
+			current_text_y,
+			current_text_x+bufferbreaklen+FIXEDCHARWIDTH,
+			current_text_y+lineheight-1);
+	}
 #endif
 
-		pbuffer[strlen(pbuffer) + 1] = '\0';
-		pbuffer[strlen(pbuffer)] = (char)NO_NEWLINE;
-		Printout(Ltrim(pbuffer));
-		currentpos = hugo_textwidth(pbuffer);	/* -charwidth; */
-		strcpy(pbuffer, "");
-	}
+	pbuffer[strlen(pbuffer)+1] = '\0';
+	pbuffer[strlen(pbuffer)] = (char)NO_NEWLINE;
+	Printout(Ltrim(pbuffer), 0);
+	currentpos = hugo_textwidth(pbuffer);	/* -charwidth; */
+	strcpy(pbuffer, "");
+}
 
 void Hugo::GetCommand() {
-		char a[256];
+	char a[256];
 #ifdef USE_TEXTBUFFER
-		int start, width, i, y;
+	int start, width, i, y;
 #endif
-		Flushpbuffer();
-		AP("");
+	Flushpbuffer();
+	AP("");
 
-		hugo_settextcolor(fcolor);
-		hugo_setbackcolor(bgcolor);
-		if (icolor == -1)
-			icolor = fcolor;	/* check unset input color */
+	hugo_settextcolor(fcolor);
+	hugo_setbackcolor(bgcolor);
+	if (icolor==-1)
+		icolor = fcolor;	/* check unset input color */
 
-		strncpy(a, GetWord(var[prompt]), 255);
-		during_player_input = true;
-		full = 0;
+	strncpy(a, GetWord(var[prompt]), 255);
+	during_player_input = true;
+	full = 0;
 #ifdef USE_TEXTBUFFER
-		/* Add the prompt to the textbuffer (using TB_Add()) */
-		y = current_text_y;
-		width = hugo_textwidth(GetWord(var[prompt]));
-		TB_AddWord(GetWord(var[prompt]), physical_windowleft, y,
-			physical_windowleft + width, y + lineheight - 1);
-
-		hugo_getline(a);
-
-		/* If hugo_scrollwindowup() called by hugo_getline() shifted things */
-		if (current_text_y > y)
-		{
-			y += (current_text_y - y);
-		}
+	/* Add the prompt to the textbuffer (using TB_Add()) */
+	y = current_text_y;
+	width = hugo_textwidth(GetWord(var[prompt]));
+	TB_AddWord(GetWord(var[prompt]), physical_windowleft, y,
+		physical_windowleft+width, y+lineheight-1);
+
+	hugo_getline(a);
+	
+	/* If hugo_scrollwindowup() called by hugo_getline() shifted things */
+	if (current_text_y > y)
+	{
+		y += (current_text_y - y);
+	}
 
-		/* Add each word in the input buffer */
-		start = 0;
-		for (i = 0; i<(int)strlen(buffer); i++)
+	/* Add each word in the input buffer */
+	start = 0;
+	for (i=0; i<(int)strlen(buffer); i++)
+	{
+		if (buffer[i]==' ')
 		{
-			if (buffer[i] == ' ')
-			{
-				buffer[i] = '\0';
-				TB_AddWord(buffer + start, physical_windowleft + width, y - lineheight,
-					physical_windowleft + width + hugo_textwidth(buffer + start), y - 1);
-				width += hugo_textwidth(buffer + start) + hugo_textwidth(" ");
-				start = i + 1;
-				buffer[i] = ' ';
-			}
+			buffer[i] = '\0';
+			TB_AddWord(buffer+start, physical_windowleft+width, y-lineheight, 
+				physical_windowleft+width+hugo_textwidth(buffer+start), y-1);
+			width += hugo_textwidth(buffer+start) + hugo_textwidth(" ");
+			start = i+1;
+			buffer[i] = ' ';
 		}
-		/* Add the final word */
-		TB_AddWord(buffer + start, physical_windowleft + width, y - lineheight,
-			physical_windowleft + width + hugo_textwidth(buffer + start), y - 1);
+	}
+	/* Add the final word */
+	TB_AddWord(buffer+start, physical_windowleft+width, y-lineheight, 
+		physical_windowleft+width+hugo_textwidth(buffer+start), y-1);
 #else
-		hugo_getline(a);
+	hugo_getline(a);
 #endif
-		during_player_input = false;
-		strcpy(buffer, Rtrim(buffer));
+	during_player_input = false;
+	strcpy(buffer, Rtrim(buffer));
 
-		strcpy(parseerr, "");
+	strcpy(parseerr, "");
 
-		full = 1;
-		remaining = 0;
-	}
+	full = 1;
+	remaining = 0;
+}
 
 char *Hugo::GetString(long addr) {
 	static char a[256];
@@ -848,9 +932,9 @@ char *Hugo::GetString(long addr) {
 
 	length = Peek(addr);
 
-	for (i = 1; i <= length; i++)
-		a[i - 1] = (char)(Peek(addr + i) - CHAR_TRANSLATION);
-	a[i - 1] = '\0';
+	for (i=1; i<=length; i++)
+		a[i-1] = (char)(Peek(addr + i) - CHAR_TRANSLATION);
+	a[i-1] = '\0';
 
 	return a;
 }
@@ -861,13 +945,13 @@ char *Hugo::GetText(long textaddr) {
 	int tdatal, tdatah, tlen;       /* low byte, high byte, length */
 
 
-									/* Read the string from memory... */
+	/* Read the string from memory... */
 	if (loaded_in_memory)
 	{
-		tlen = MEM(codeend + textaddr) + MEM(codeend + textaddr + 1) * 256;
-		for (i = 0; i<tlen; i++)
+		tlen = MEM(codeend+textaddr) + MEM(codeend+textaddr+1)*256;
+		for (i=0; i<tlen; i++)
 		{
-			g[i] = (char)(MEM(codeend + textaddr + 2 + i) - CHAR_TRANSLATION);
+			g[i] = (char)(MEM(codeend+textaddr+2+i) - CHAR_TRANSLATION);
 		}
 		g[i] = '\0';
 
@@ -875,17 +959,17 @@ char *Hugo::GetText(long textaddr) {
 	}
 
 	/* ...Or load the string from disk */
-	if (game->seek(codeend + textaddr)) FatalError(READ_E);
+	if (hugo_fseek(game, codeend+textaddr, SEEK_SET)) FatalError(READ_E);
 
-	tdatal = game->readByte();
-	tdatah = game->readByte();
-	if (tdatal == EOF || tdatah == EOF || game->err()) FatalError(READ_E);
+	tdatal = hugo_fgetc(game);
+	tdatah = hugo_fgetc(game);
+	if (tdatal==EOF || tdatah==EOF || hugo_ferror(game)) FatalError(READ_E);
 
 	tlen = tdatal + tdatah * 256;
 
-	for (i = 0; i<tlen; i++)
+	for (i=0; i<tlen; i++)
 	{
-		if ((a = game->readByte()) == EOF) FatalError(READ_E);
+		if ((a = hugo_fgetc(game))==EOF) FatalError(READ_E);
 		g[i] = (char)(a - CHAR_TRANSLATION);
 	}
 	g[i] = '\0';
@@ -899,13 +983,13 @@ char *Hugo::GetWord(unsigned int w) {
 
 	a = w;
 
-	if (a == 0) return "";
+	if (a==0) return "";
 
-	if (a == PARSE_STRING_VAL) return parseerr;
-	if (a == SERIAL_STRING_VAL) return serial;
+	if (a==PARSE_STRING_VAL) return parseerr;
+	if (a==SERIAL_STRING_VAL) return serial;
 
 	/* bounds-checking to avoid some sort of memory arena error */
-	if ((long)(a + dicttable * 16L) > codeend)
+	if ((long)(a+dicttable*16L) > codeend)
 	{
 		b = "";
 		return b;
@@ -922,7 +1006,7 @@ void Hugo::HandleTailRecursion(long addr) {
 	codeptr = addr;
 
 	/* Set up proper default return value for property or routine */
-	if (tail_recursion == TAIL_RECURSION_PROPERTY)
+	if (tail_recursion==TAIL_RECURSION_PROPERTY)
 		ret = 1;
 	else
 		ret = 0;
@@ -930,19 +1014,19 @@ void Hugo::HandleTailRecursion(long addr) {
 	/* Unstack until we get to any routine call that got us here */
 	while (stack_depth)
 	{
-		if (code_block[stack_depth].type == RUNROUTINE_BLOCK)
+		if (code_block[stack_depth].type==RUNROUTINE_BLOCK)
 			break;
 		stack_depth--;
 	}
 
 #ifdef DEBUGGER
-	currentroutine = (unsigned int)(codeptr / address_scale);
-	call[window[VIEW_CALLS].count - 1].addr = currentroutine;
-	call[window[VIEW_CALLS].count - 1].param = true;
+	currentroutine = (unsigned int)(codeptr/address_scale);
+	call[window[VIEW_CALLS].count-1].addr = currentroutine;
+	call[window[VIEW_CALLS].count-1].param = true;
 
 	sprintf(debug_line, "Calling:  %s", RoutineName(currentroutine));
 	/* Don't duplicate blank separator line in code window */
-	if (codeline[window[CODE_WINDOW].count - 1][0] != 0)
+	if (codeline[window[CODE_WINDOW].count-1][0] != 0)
 		AddStringtoCodeWindow("");
 	AddStringtoCodeWindow(debug_line);
 
@@ -966,27 +1050,27 @@ void Hugo::InitGame() {
 	context_commands = 0;
 #endif
 	game_reset = false;
-
+	
 	SetStackFrame(stack_depth, RUNROUTINE_BLOCK, 0, 0);
 
 	/* Figure out which objects have either a noun or an adjective property;
-	store it in obj_parselist, one bit per object */
-	if ((!obj_parselist) && (obj_parselist = (char *)hugo_blockalloc(sizeof(char)*((objects + 7) / 8))))
+	   store it in obj_parselist, one bit per object */
+	if ((!obj_parselist) && (obj_parselist = (char *)hugo_blockalloc(sizeof(char)*((objects+7)/8))))
 	{
-
-		for (i = 0; i<objects; i++)
+		
+		for (i=0; i<objects; i++)
 		{
-			if (i % 8 == 0) obj_parselist[i / 8] = 0;
-
+			if (i%8==0) obj_parselist[i/8] = 0;
+			
 			if (PropAddr(i, adjective, 0) || PropAddr(i, noun, 0))
-				obj_parselist[i / 8] |= 1 << (i % 8);
+				obj_parselist[i/8] |= 1 << (i%8);
 			else
-				obj_parselist[i / 8] &= ~(1 << (i % 8));
+				obj_parselist[i/8] &= ~(1 << (i%8));
 		}
 	}
-
+	
 #if defined (DEBUGGER)
-	for (i = 0; i<MAXLOCALS; i++) strcpy(localname[i], "");
+	for (i=0; i<MAXLOCALS; i++) strcpy(localname[i], "");
 	window[VIEW_LOCALS].count = current_locals = 0;
 
 	PassLocals(0);
@@ -1010,85 +1094,135 @@ void Hugo::LoadGame() {
 #ifndef LOADGAMEDATA_REPLACED
 	long c;
 #endif
-	game = &_gameFile;
-	filelength = game->size();
-	game->seek(0);
 
-	if (game->err()) FatalError(READ_E);
-	if ((game_version = game->readByte()) == EOF) FatalError(OPEN_E);
+#if defined (DEBUGGER)
+	if (!strcmp(gamefile, ""))
+	{
+		game = NULL;
+		strcpy(gamefile, "(no file)");
+		return;
+	}
+#endif
+
+#if !defined (GLK) /* since in Glk the game stream is always open */
+	if ((game = TrytoOpen(gamefile, "rb", "games"))==NULL)
+	{
+		if ((game = TrytoOpen(gamefile, "rb", "object"))==NULL)
+			FatalError(OPEN_E);
+	}
+#endif
+
+	hugo_fseek(game, 0, SEEK_END);
+	filelength = hugo_ftell(game);
+	hugo_fseek(game, 0, SEEK_SET);
+
+	if (hugo_ferror(game)) FatalError(READ_E);
+	if ((game_version = hugo_fgetc(game))==EOF) FatalError(OPEN_E);
 
 	/* Earlier versions of the compiler wrote the version code as
-	1 or 2 instead of 10 or 20.
+	   1 or 2 instead of 10 or 20.
 	*/
-	if (game_version == 1 || game_version == 2)
-		game_version *= 10;
+	if (game_version==1 || game_version==2)
+		game_version*=10;
 
 	if (game_version < 21) object_size = 12;
 
 	if (game_version < 31) address_scale = 4;
 
-	check_version = HEVERSION * 10 + HEREVISION;
+	check_version = HEVERSION*10 + HEREVISION;
+#if defined (COMPILE_V25)
+	if (check_version==25 && (game_version>=30 && game_version<=39))
+		check_version = game_version;
+#endif
 
 	defseg = gameseg;
 
 	if (game_version < HEVERSION)
 	{
+#if defined (DEBUGGER)
+		hugo_cleanup_screen();
+		hugo_clearfullscreen();
+#endif
 		sprintf(line, "Hugo Compiler v%d.%d or later required.\n", HEVERSION, HEREVISION);
 		if (game_version>0)
-			sprintf(line + strlen(line), "File \"%s\" is v%d.%d.\n", gamefile, game_version / 10, game_version % 10);
+			sprintf(line+strlen(line), "File \"%s\" is v%d.%d.\n", gamefile, game_version/10, game_version%10);
 
-		error(line);
+#if defined (DEBUGGER_PRINTFATALERROR)
+		DEBUGGER_PRINTFATALERROR(line);
+#else
+		//debug("%s", line);
+#endif
+		hugo_closefiles();
+		hugo_blockfree(mem);
+		mem = NULL;
+
+		hugo_exit(line);
 	}
 	else if (game_version > check_version)
 	{
-		error("File \"%s\" is incorrect or unknown version.\n", gamefile);
+#if defined (DEBUGGER)
+		hugo_cleanup_screen();
+		hugo_clearfullscreen();
+#endif
+		sprintf(line, "File \"%s\" is incorrect or unknown version.\n", gamefile);
+
+#if defined (DEBUGGER_PRINTFATALERROR)
+		DEBUGGER_PRINTFATALERROR(line);
+#else
+		//debug("%s", line);
+#endif
+		hugo_closefiles();
+		hugo_blockfree(mem);
+		mem = NULL;
+		hugo_exit(line);           /* ditto */
 	}
 
-	hugo_settextpos(1, physical_windowheight / lineheight);
+	hugo_settextpos(1, physical_windowheight/lineheight);
 
-	if (game_version >= 25)
-		game->seek(H_TEXTBANK, SEEK_SET);
+	if (game_version>=25)
+		hugo_fseek(game, H_TEXTBANK, SEEK_SET);
 	else
 		/* Because pre-v2.5 didn't have performaddr in the header */
-		game->seek(H_TEXTBANK - 2L, SEEK_SET);
+		hugo_fseek(game, H_TEXTBANK-2L, SEEK_SET);
 
-	data = game->readByte();
-	textbank = game->readByte();
-	if (data == EOF || textbank == EOF || game->err()) FatalError(READ_E);
-	textbank = (textbank * 256L + (long)data) * 16L;
+	data = hugo_fgetc(game);
+	textbank = hugo_fgetc(game);
+	if (data==EOF || textbank==EOF || hugo_ferror(game)) FatalError(READ_E);
+	textbank = (textbank*256L + (long)data) * 16L;
 	codeend = textbank;
 
 	/* Use a 1024-byte read block */
 	ccount = 1024;
 
-	if (game->seek(0, SEEK_SET)) FatalError(READ_E);
+	if (hugo_fseek(game, 0, SEEK_SET)) FatalError(READ_E);
 
 #ifndef LOADGAMEDATA_REPLACED
 	/* Allocate as much memory as is required */
-	if ((!loaded_in_memory) || (mem = (unsigned char *)hugo_blockalloc(filelength)) == nullptr)
+	if ((!loaded_in_memory) || (mem = (unsigned char *)hugo_blockalloc(filelength))==NULL)
 	{
 		loaded_in_memory = 0;
-		if ((mem = (unsigned char *)hugo_blockalloc(codeend)) == nullptr)
+		if ((mem = (unsigned char *)hugo_blockalloc(codeend))==NULL)
 			FatalError(MEMORY_E);
 	}
 
 	c = 0;
 
 	/* Load either the entire file or just up to the start of the
-	text bank
+	   text bank
 	*/
-	while (c < (loaded_in_memory ? filelength : codeend))
+	while (c < (loaded_in_memory ? filelength:codeend))
 	{
 		/* Complicated, but basically just makes sure that
-		the last read (whether loaded_in_memory or not)
-		doesn't override the end of the file.  Shouldn't
-		normally be a problem for fread(), but it caused
-		a crash under MSVC++.
+		   the last read (whether loaded_in_memory or not)
+		   doesn't override the end of the file.  Shouldn't
+		   normally be a problem for fread(), but it caused
+		   a crash under MSVC++.
 		*/
-		i = game->read((unsigned char *)&mem[c],
-			(loaded_in_memory) ?
-			((filelength - c>(long)ccount) ? ccount : (size_t)(filelength - c)) :
-			((codeend - c>(long)ccount) ? ccount : (size_t)(codeend - c)));
+		i = hugo_fread((unsigned char *)&mem[c], sizeof(unsigned char),
+			(loaded_in_memory)?
+				((filelength-c>(long)ccount)?ccount:(size_t)(filelength-c)):
+				((codeend-c>(long)ccount)?ccount:(size_t)(codeend-c)),
+			game);
 
 		if (!i) break;
 		c += i;
@@ -1097,36 +1231,36 @@ void Hugo::LoadGame() {
 	if (!LoadGameData(false)) FatalError(READ_E);
 #endif
 
-	if (game->err()) FatalError(READ_E);
+	if (hugo_ferror(game)) FatalError(READ_E);
 
 	defseg = gameseg;
 
 	/* Read header: */
 
 	id[0] = Peek(H_ID);
-	id[1] = Peek(H_ID + 1);
+	id[1] = Peek(H_ID+1);
 	id[2] = '\0';
 
-	for (i = 0; i<8; i++)
-		serial[i] = Peek(H_SERIAL + i);
+	for (i=0; i<8; i++)
+		serial[i] = Peek(H_SERIAL+i);
 	serial[8] = '\0';
 
-	codestart = PeekWord(H_CODESTART);
-	objtable = PeekWord(H_OBJTABLE) + gameseg;
-	proptable = PeekWord(H_PROPTABLE) + gameseg;
+	codestart  = PeekWord(H_CODESTART);
+	objtable   = PeekWord(H_OBJTABLE) + gameseg;
+	proptable  = PeekWord(H_PROPTABLE) + gameseg;
 	eventtable = PeekWord(H_EVENTTABLE) + gameseg;
 	arraytable = PeekWord(H_ARRAYTABLE) + gameseg;
-	dicttable = PeekWord(H_DICTTABLE) + gameseg;
-	syntable = PeekWord(H_SYNTABLE) + gameseg;
+	dicttable  = PeekWord(H_DICTTABLE) + gameseg;
+	syntable   = PeekWord(H_SYNTABLE) + gameseg;
 
-	initaddr = PeekWord(H_INIT);
-	mainaddr = PeekWord(H_MAIN);
-	parseaddr = PeekWord(H_PARSE);
+	initaddr       = PeekWord(H_INIT);
+	mainaddr       = PeekWord(H_MAIN);
+	parseaddr      = PeekWord(H_PARSE);
 	parseerroraddr = PeekWord(H_PARSEERROR);
 	findobjectaddr = PeekWord(H_FINDOBJECT);
-	endgameaddr = PeekWord(H_ENDGAME);
-	speaktoaddr = PeekWord(H_SPEAKTO);
-	performaddr = PeekWord(H_PERFORM);
+	endgameaddr    = PeekWord(H_ENDGAME);
+	speaktoaddr    = PeekWord(H_SPEAKTO);
+	performaddr    = PeekWord(H_PERFORM);
 
 
 	/* Read totals: */
@@ -1151,43 +1285,163 @@ void Hugo::LoadGame() {
 	{
 		data = FindWord("(display)");
 
-		for (i = 0; i<objects; i++)
+		for (i=0; i<objects; i++)
 		{
-			if (GetProp(i, 0, 1, true) == data)
+			if (GetProp(i, 0, 1, true)==data)
 			{
 				display_object = i;
 				break;
 			}
 		}
 	}
-
+	
 	/* build punctuation string (additional user-specified punctuation) */
 	synptr = 2;
 	strcpy(punc_string, "");
-	for (i = 1; i <= syncount; i++)
+	for (i=1; i<=syncount; i++)
 	{
 		defseg = syntable;
-		if (Peek(synptr) == 3)	/* 3 = punctuation */
+		if (Peek(synptr)==3)	/* 3 = punctuation */
 		{
-			strcpy(line, GetWord(PeekWord(synptr + 1)));
+			strcpy(line, GetWord(PeekWord(synptr+1)));
 			if (strlen(line) + strlen(punc_string) > 63) break;
 			strcat(punc_string, line);
 		}
-		synptr += 5;
+		synptr+=5;
+	}
+}
+
+#if !defined (GLK)	/* ParseCommandLine() is omitted for Glk */
+
+signed char def_fcolor    = DEF_FCOLOR;
+signed char def_bgcolor   = DEF_BGCOLOR;
+signed char def_slfcolor  = DEF_SLFCOLOR;
+signed char def_slbgcolor = DEF_SLBGCOLOR;
+
+void ParseCommandLine(int argc, char *argv[])
+{
+	char drive[MAXDRIVE], dir[MAXDIR], fname[MAXFILENAME], ext[MAXEXT];
+	char* game_file_arg = NULL;
+
+#if defined(GCC_UNIX) && defined(DO_COLOR)
+        int ch;
+	/* Parse comand line options (colour switches) */
+	while ((ch = getopt(argc, argv, "f:b:F:B:?h")) != -1) {
+	  switch (ch) {
+	    case 'f':
+	      def_fcolor = atoi(optarg);
+	      break;
+	    case 'b':
+	      def_bgcolor = atoi(optarg);
+	      break;
+	    case 'F':
+	      def_slfcolor = atoi(optarg);
+	      break;
+	    case 'B':
+	      def_slbgcolor = atoi(optarg);
+	      break;
+	    case 'h':
+	    case '?':
+	    default:
+	      Banner();
+	      if (mem) hugo_blockfree(mem);
+	      mem = NULL;
+	      exit(0);
+	  }
+	}
+	if ( optind < argc ) {
+	  game_file_arg = argv[optind];
+	}
+#else
+	if (argc>1) {
+	  game_file_arg = argv[1];
+	}
+#endif
+
+	if (game_file_arg==NULL)
+	{
+		Banner();
+		if (mem) hugo_blockfree(mem);
+		mem = NULL;
+		exit(0);
 	}
+
+	hugo_splitpath(game_file_arg, drive, dir, fname, ext);
+
+	if (strcmp(ext, ""))
+		strcpy(gamefile, game_file_arg);
+	else
+		hugo_makepath(gamefile, drive, dir, fname,
+#if defined (DEBUGGER)
+			"hdx");
+#else
+			"hex");
+#endif
+
+	if (getenv("HUGO_SAVE"))
+		hugo_makepath(savefile, "", getenv("HUGO_SAVE"), fname, "sav");
+	else
+		hugo_makepath(savefile, drive, dir, fname, "sav");
+
+	if (getenv("HUGO_RECORD"))
+		hugo_makepath(recordfile, "", getenv("HUGO_RECORD"), fname, "rec");
+	else
+		hugo_makepath(recordfile, drive, dir, fname, "rec");
+
+	strcpy(scriptfile, DEF_PRN);
+
+	hugo_makepath(gamepath, drive, dir, "", "");
+}
+
+#endif	/* GLK */
+
+void Hugo::PassLocals(int n) {
+	int i;
+
+	for (i=0; i<MAXLOCALS; i++)
+	{
+		var[MAXGLOBALS+i] = passlocal[i];
+		passlocal[i] = 0;
+	}
+	arguments_passed = n;
+}
+
+#ifdef NO_INLINE_MEM_FUNCTIONS
+
+/* PEEK */
+
+unsigned char Peek(long a)
+{
+	return MEM(defseg * 16L + a);
+}
+
+
+/* PEEKWORD */
+
+unsigned int PeekWord(long a)
+{
+	return (unsigned char)MEM(defseg*16L+a) + (unsigned char)MEM(defseg*16L+a+1)*256;
+}
+
+
+/* POKE */
+
+void Poke(unsigned int a, unsigned char v)
+{
+	SETMEM(defseg * 16L + a, v);
 }
 
-void Hugo::PassLocals(int n) {
-	int i;
 
-	for (i = 0; i<MAXLOCALS; i++)
-	{
-		var[MAXGLOBALS + i] = passlocal[i];
-		passlocal[i] = 0;
-	}
-	arguments_passed = n;
+/* POKEWORD */
+
+void PokeWord(unsigned int a, unsigned int v)
+{
+	SETMEM(defseg * 16L + a, (char)(v%256));
+	SETMEM(defseg * 16L + a + 1, (char)(v/256));
 }
 
+#endif	/* NO_INLINED_MEM_FUNCTIONS */
+
 const char *Hugo::PrintHex(long a) {
 	static char hex[7];
 	int h = 0;
@@ -1202,12 +1456,12 @@ const char *Hugo::PrintHex(long a) {
 	if (a < 256L) hex[h++] = '0';
 	if (a < 16L) hex[h++] = '0';
 
-	sprintf(hex + h, "%lX", a);
+	sprintf(hex+h, "%lX", a);
 
 	return hex;
 }
 
-void Hugo::Printout(char *a) {
+void Hugo::Printout(char *a, int no_scrollback_linebreak) {
 	char b[2], sticky = 0, trimmed = 0;
 	char tempfcolor;
 	int i, l;
@@ -1217,20 +1471,20 @@ void Hugo::Printout(char *a) {
 	/* hugo_font() should do this if necessary, but just in case */
 	if (lineheight < FIXEDLINEHEIGHT)
 		lineheight = FIXEDLINEHEIGHT;
-
+	
 	tempfcolor = fcolor;
 
 	/* The before-check of the linecount: */
 	if (full)
 	{
 		/* -1 here since it's before printing */
-		if (full >= physical_windowheight / lineheight - 1)
+		if (full >= physical_windowheight/lineheight-1)
 			PromptMore();
 	}
 
-	if ((a[0] != '\0') && a[strlen(a) - 1] == (char)NO_NEWLINE)
+	if ((a[0]!='\0') && a[strlen(a)-1]==(char)NO_NEWLINE)
 	{
-		a[strlen(a) - 1] = '\0';
+		a[strlen(a)-1] = '\0';
 		sticky = true;
 	}
 
@@ -1238,19 +1492,19 @@ void Hugo::Printout(char *a) {
 
 
 	/* The easy part is just skimming <a> and processing each code
-	or printed character, as the case may be:
+	   or printed character, as the case may be:
 	*/
-
+	
 	l = 0;	/* physical length of string */
 
-	for (i = 0; i<(int)strlen(a); i++)
+	for (i=0; i<(int)strlen(a); i++)
 	{
-		if ((a[i] == ' ') && !trimmed && currentpos == 0)
+		if ((a[i]==' ') && !trimmed && currentpos==0)
 		{
 			continue;
 		}
 
-		if ((unsigned char)a[i] > ' ' || a[i] == FORCED_SPACE)
+		if ((unsigned char)a[i] > ' ' || a[i]==FORCED_SPACE)
 		{
 			trimmed = true;
 			last_printed_font = currentfont;
@@ -1258,55 +1512,55 @@ void Hugo::Printout(char *a) {
 
 		switch (b[0] = a[i])
 		{
-		case FONT_CHANGE:
-			n = (int)(a[++i] - 1);
-			if (currentfont != n)
-				hugo_font(currentfont = n);
-			break;
+			case FONT_CHANGE:
+				n = (int)(a[++i]-1);
+				if (currentfont != n)
+					hugo_font(currentfont = n);
+				break;
 
-		case COLOR_CHANGE:
-			fcolor = (char)(a[++i] - 1);
-			hugo_settextcolor((int)fcolor);
-			hugo_setbackcolor((int)(a[++i] - 1));
-			hugo_font(currentfont);
-			break;
+			case COLOR_CHANGE:
+				fcolor = (char)(a[++i]-1);
+				hugo_settextcolor((int)fcolor);
+				hugo_setbackcolor((int)(a[++i]-1));
+				hugo_font(currentfont);
+				break;
 
-		default:
-			if (b[0] == FORCED_SPACE) b[0] = ' ';
-			l += hugo_charwidth(b[0]);
+			default:
+				if (b[0]==FORCED_SPACE) b[0] = ' ';
+				l += hugo_charwidth(b[0]);
 
-			/* A minor adjustment for font changes and RunWindow() to make
-			sure we're not printing unnecessarily downscreen
-			*/
-			if ((just_left_window) && current_text_y > physical_windowbottom - lineheight)
-			{
-				current_text_y = physical_windowbottom - lineheight;
-			}
-			just_left_window = false;
+				/* A minor adjustment for font changes and RunWindow() to make
+				   sure we're not printing unnecessarily downscreen
+				*/
+				if ((just_left_window) && current_text_y > physical_windowbottom-lineheight)
+				{
+					current_text_y = physical_windowbottom-lineheight;
+				}
+				just_left_window = false;
 
-			hugo_print(b);
+				hugo_print(b);
 		}
 
-		if (script && (unsigned char)b[0] >= ' ')
-			script->putBuffer(b, strlen(b));
+		if (script && (unsigned char)b[0]>=' ')
+			if (hugo_writetoscript(b) < 0) FatalError(WRITE_E);
 
 #if defined (SCROLLBACK_DEFINED)
-		if (!inwindow && (unsigned char)b[0] >= ' ')
+		if (!inwindow && (unsigned char)b[0]>=' ')
 		{
 #ifdef USE_SMARTFORMATTING
 			/* Undo smart-formatting for ASCII scrollback */
 			switch ((unsigned char)b[0])
 			{
-			case 151:
-				hugo_sendtoscrollback("--");
-				continue;
-			case 145:
-			case 146:
-				b[0] = '\'';
-				break;
-			case 147:
-			case 148:
-				b[0] = '\"';
+				case 151:
+					hugo_sendtoscrollback("--");
+					continue;
+				case 145:
+				case 146:
+					b[0] = '\'';
+					break;
+				case 147:
+				case 148:
+					b[0] = '\"';
 			}
 #endif
 			hugo_sendtoscrollback(b);
@@ -1315,37 +1569,37 @@ void Hugo::Printout(char *a) {
 	}
 
 	/* If we've got a linefeed and didn't hit the right edge of the
-	window
+	   window
 	*/
 #ifdef NO_TERMINAL_LINEFEED
 	if (!sticky)
 #else
-	if (!sticky && currentpos + l < physical_windowwidth)
+	if (!sticky && currentpos+l < physical_windowwidth)
 #endif
 	{
 		/* The background color may have to be temporarily set if we're
-		not in a window--the reason is that full lines of the
-		current background color might be printed by the OS-specific
-		scrolling function.  (This behavior is overridden by the
-		Hugo Engine for in-window printing, which always adds new
-		lines in the current background color when scrolling.)
+		   not in a window--the reason is that full lines of the
+		   current background color might be printed by the OS-specific
+		   scrolling function.  (This behavior is overridden by the
+		   Hugo Engine for in-window printing, which always adds new
+		   lines in the current background color when scrolling.)
 		*/
-		hugo_setbackcolor((inwindow) ? bgcolor : default_bgcolor);
+		hugo_setbackcolor((inwindow)?bgcolor:default_bgcolor);
 		hugo_print("\r");
 
 		i = currentfont;
 		hugo_font(currentfont = last_printed_font);
 
 #ifndef GLK
-		if (currentline > physical_windowheight / lineheight)
+		if (currentline > physical_windowheight/lineheight)
 		{
-			int full_limit = physical_windowheight / lineheight;
+			int full_limit = physical_windowheight/lineheight;
 
 			hugo_scrollwindowup();
 
 			if ((current_text_y)
-				&& full >= full_limit - 3
-				&& physical_windowbottom - current_text_y - lineheight > lineheight / 2)
+				&& full >= full_limit-3
+				&& physical_windowbottom-current_text_y-lineheight > lineheight/2)
 			{
 				PromptMore();
 			}
@@ -1353,7 +1607,7 @@ void Hugo::Printout(char *a) {
 		}
 
 		/* Don't scroll single-line windows before PromptMore() */
-		else if (physical_windowheight / lineheight > 1)
+		else if (physical_windowheight/lineheight > 1)
 #endif
 		{
 			hugo_print("\n");
@@ -1379,96 +1633,230 @@ void Hugo::Printout(char *a) {
 	}
 
 	/* Otherwise, take care of all the line-feeding, line-counting,
-	etc.
+	   etc.
 	*/
 	else
 	{
 		currentpos = 0;
-		if (currentline++ > physical_windowheight / lineheight)
-			currentline = physical_windowheight / lineheight;
+		if (currentline++ > physical_windowheight/lineheight)
+			currentline = physical_windowheight/lineheight;
 
 		if (!playback) skipping_more = false;
 
 		++full;
-
+		
 		/* The after-check of the linecount: */
-		if ((full) && full >= physical_windowheight / lineheight)
+		if ((full) && full >= physical_windowheight/lineheight)
 		{
 			PromptMore();
 		}
 
-		if (script)
+		if (script && !no_scrollback_linebreak)
 		{
-			script->putBuffer("\n", 1);
+			if (hugo_writetoscript("\n")<0) FatalError(WRITE_E);
 		}
 
 #if defined (SCROLLBACK_DEFINED)
-		if (!inwindow) hugo_sendtoscrollback("\n");
+		if (!inwindow && !no_scrollback_linebreak) hugo_sendtoscrollback("\n");
 #endif
 	}
 
 	fcolor = tempfcolor;
 }
 
-int Hugo::RecordCommands() {
-	remaining = 0;
+#ifndef PROMPTMORE_REPLACED
+
+void Hugo::PromptMore() {
+	char temp_during_player_input;
+	int k, tempcurrentfont;
+	int temp_current_text_y = current_text_y;
+	
+	if (playback && skipping_more)
+	{
+		full = 0;
+		return;
+	}
 	skipping_more = false;
+	
+	/* Clear the key buffer */
+	while (hugo_iskeywaiting()) hugo_getkey();
 
-	switch (Peek(codeptr))
+	temp_during_player_input = during_player_input;
+	during_player_input = false;
+
+	tempcurrentfont = currentfont;
+	hugo_font(currentfont = NORMAL_FONT);
+
+	hugo_settextpos(1, physical_windowheight/lineheight);
+
+#ifdef NO_TERMINAL_LINEFEED
+	/* For ports where it's possible, do a better "MORE..." prompt
+	   without a flashing caret */
+	if (default_bgcolor!=DEF_SLBGCOLOR)
 	{
-	case RECORDON_T:
+		/* system statusline colors */
+		hugo_settextcolor(DEF_SLFCOLOR);
+		hugo_setbackcolor(DEF_SLBGCOLOR);
+	}
+	else
 	{
-		if (!record && !playback)
-		{
-			/* Glk implementation */
-			frefid_t fref = nullptr;
+		/* system colors */
+		if (DEF_FCOLOR < 8)
+			hugo_settextcolor(DEF_FCOLOR+8); /* bright */
+		else
+			hugo_settextcolor(DEF_FCOLOR);
+		hugo_setbackcolor(DEF_BGCOLOR);
+	}
 
-			fref = glk_fileref_create_by_prompt(fileusage_Transcript | fileusage_TextMode,
-				filemode_Write, 0);
-			record = glk_stream_open_file(fref, filemode_Write, 0);
-			glk_fileref_destroy(fref);
-			if (!record)
-				return 0;
+	if (current_text_y)
+		current_text_y = physical_windowbottom - lineheight;
 
-			return 1;
-		}
-		break;
+	/* Make sure we fit in a window */
+	if (physical_windowwidth/FIXEDCHARWIDTH >= 19)
+		hugo_print("     [MORE...]     ");
+	else
+		hugo_print("[MORE...]");
+
+	if (!inwindow)
+		hugo_setbackcolor(default_bgcolor);
+	else
+		hugo_setbackcolor(bgcolor);
+
+	/* Note that hugo_iskeywaiting() must flush the printing buffer,
+	   if one is being employed */
+	while (!hugo_iskeywaiting())
+	{
+		hugo_timewait(100);
 	}
 
-	case RECORDOFF_T:
+	k = hugo_waitforkey();
+
+#else
+	/* The normal, with-caret way of doing it */
+	/* system colors */
+	hugo_settextcolor(16); /* DEF_FCOLOR);  */
+	hugo_setbackcolor(17); /* DEF_BGCOLOR); */
+	hugo_print("[MORE...]");
+
+	k = hugo_waitforkey();
+
+	if (!inwindow)
+		hugo_setbackcolor(default_bgcolor);
+	else
+		hugo_setbackcolor(bgcolor);
+#endif
+
+	if (playback && k==27)         /* if ESC is pressed during playback */
 	{
-		if (playback) return 1;
+		if (hugo_fclose(playback))
+			FatalError(READ_E);
+		playback = NULL;
+	}
+	else if (playback && k=='+')
+		skipping_more = true;
+
+	hugo_settextpos(1, physical_windowheight/lineheight);
+#ifdef NO_TERMINAL_LINEFEED
+	current_text_y = physical_windowbottom - lineheight;
+	/* Make sure we fit in a window */
+	if (physical_windowwidth/FIXEDCHARWIDTH >= 19)
+		hugo_print("                    ");
+	else
+		hugo_print("         ");
+#else
+	hugo_print("         ");
+#endif
+	hugo_font(currentfont = tempcurrentfont);
+
+	hugo_settextpos(1, physical_windowheight/lineheight);
+	current_text_y = temp_current_text_y;
+	full = 0;
 
-		if (record)
+	hugo_settextcolor(fcolor);
+	hugo_setbackcolor(bgcolor);
+
+	during_player_input = temp_during_player_input;
+}
+
+#endif	/* ifndef PROMPTMORE_REPLACED */
+
+int Hugo::RecordCommands() {
+	remaining = 0;
+	skipping_more = false;
+
+	switch (Peek(codeptr))
+	{
+		case RECORDON_T:
 		{
-			delete record;
-			record = nullptr;
-			return 1;
+			if (!record && !playback)
+			{
+#if !defined (GLK)
+				/* stdio implementation */
+				hugo_getfilename("for command recording", recordfile);
+				if (!strcmp(line, ""))
+					return 0;
+				if (!hugo_overwrite(line))
+					return 0;
+				if (!(record = HUGO_FOPEN(line, "wt")))
+					return 0;
+				strcpy(recordfile, line);
+#else
+				/* Glk implementation */
+				frefid_t fref = NULL;
+
+				fref = glk_fileref_create_by_prompt(fileusage_Transcript | fileusage_TextMode,
+					filemode_Write, 0);
+				record = glk_stream_open_file(fref, filemode_Write, 0);
+				glk_fileref_destroy(fref);
+				if (!record)
+					return 0;
+#endif
+				return 1;
+			}
+			break;
 		}
-		break;
-	}
 
-	case PLAYBACK_T:
-	{
-		if (!playback)
+		case RECORDOFF_T:
 		{
-			/* Glk implementation */
-			frefid_t fref = nullptr;
+			if (playback) return 1;
 
-			fref = glk_fileref_create_by_prompt(fileusage_InputRecord | fileusage_TextMode,
-				filemode_Read, 0);
-			if (glk_fileref_does_file_exist(fref))
-				playback = glk_stream_open_file(fref, filemode_Read, 0);
-			else
-				playback = nullptr;
-			glk_fileref_destroy(fref);
+			if (record)
+			{
+				if (hugo_fclose(record)) return (0);
+
+				record = NULL;
+				return 1;
+			}
+			break;
+		}
+
+		case PLAYBACK_T:
+		{
 			if (!playback)
-				return 0;
+			{
+#if !defined (GLK)
+				/* stdio implementation */
+				hugo_getfilename("for command playback", recordfile);
+				if (!strcmp(line, ""))
+					return 0;
+				if (!(playback = HUGO_FOPEN(line, "rt")))
+					return 0;
+				strcpy(recordfile, line);
+#else
+				/* Glk implementation */
+				frefid_t fref = NULL;
 
-			return 1;
+				fref = glk_fileref_create_by_prompt(fileusage_InputRecord | fileusage_TextMode,
+					filemode_Read, 0);
+				playback = glk_stream_open_file(fref, filemode_Read, 0);
+				glk_fileref_destroy(fref);
+				if (!playback)
+					return 0;
+#endif
+				return 1;
+			}
+			break;
 		}
-		break;
-	}
 	}
 	return 0;
 }
@@ -1485,43 +1873,45 @@ void Hugo::SaveUndo(int a, int b, int c, int d, int e) {
 		undostack[undoptr][4] = e;
 
 		/* Put zeroes at end of this operation in case
-		the stack wraps around */
+		   the stack wraps around */
 		tempptr = undoptr;
-		if (++undoptr == MAXUNDO) undoptr = 0;
+		if (++undoptr==MAXUNDO) undoptr = 0;
 		undostack[undoptr][0] = 0;
 		undostack[undoptr][1] = 0;
 		undoptr = tempptr;
 
-		if (++undoturn == MAXUNDO)        /* turn too complex */
-		{
-			undoptr = 0;
+		if (++undoturn==MAXUNDO)        /* turn too complex */
+			{undoptr = 0;
 			undoturn = MAXUNDO;
-			undoinvalid = 1;
-		}
+			undoinvalid = 1;}
 
-		if (++undoptr == MAXUNDO) undoptr = 0;
+		if (++undoptr==MAXUNDO) undoptr = 0;
 	}
 }
 
 void Hugo::SetStackFrame(int depth, int type, long brk, long returnaddr) {
-	if (depth == RESET_STACK_DEPTH) stack_depth = 0;
-	else if (++stack_depth >= MAXSTACKDEPTH) FatalError(MEMORY_E);
+	if (depth==RESET_STACK_DEPTH) stack_depth = 0;
+	else if (++stack_depth>=MAXSTACKDEPTH) FatalError(MEMORY_E);
 
 	code_block[stack_depth].type = type;
 	code_block[stack_depth].brk = brk;
 	code_block[stack_depth].returnaddr = returnaddr;
+
+#if defined (DEBUGGER)
+	code_block[stack_depth].dbnest = dbnest;
+#endif
 }
 
 void Hugo::SetupDisplay() {
 	hugo_settextmode();
 
 	hugo_settextwindow(1, 1,
-		SCREENWIDTH / FIXEDCHARWIDTH, SCREENHEIGHT / FIXEDLINEHEIGHT);
+		SCREENWIDTH/FIXEDCHARWIDTH, SCREENHEIGHT/FIXEDLINEHEIGHT);
 
 	last_window_left = 1;
 	last_window_top = 1;
-	last_window_right = SCREENWIDTH / FIXEDCHARWIDTH;
-	last_window_bottom = SCREENHEIGHT / FIXEDLINEHEIGHT;
+	last_window_right = SCREENWIDTH/FIXEDCHARWIDTH;
+	last_window_bottom = SCREENHEIGHT/FIXEDLINEHEIGHT;
 
 	hugo_settextcolor(16);
 	hugo_setbackcolor(17);
@@ -1534,225 +1924,272 @@ char Hugo::SpecialChar(const char *a, int *i) {
 	r = a[*i];
 	s = r;
 
-	if (r == '\"') return r;
+	if (r=='\"') return r;
 
 	/* For a couple of versions, Hugo allowed Inform-style
-	punctuation control characters; I don't remember
-	exactly why.
+	   punctuation control characters; I don't remember
+	   exactly why.
 	*/
 	if (game_version <= 22)
-		if (r == '~' || r == '^') return r;
+		if (r=='~' || r=='^') return r;
 
-	if (r == '(')
-	{
-		r = a[++*i];
-		skipbracket = true;
-	}
+	if (r=='(')
+		{r = a[++*i];
+		skipbracket = true;}
 
 	switch (r)
 	{
-	case '`':               /* accent grave */
-	{
-		/* Note that the "s = '...'" characters are
-		Latin-1 and may not display properly under,
-		e.g., DOS */
+		case '`':               /* accent grave */
+		{
+			/* Note that the "s = '...'" characters are
+			   Latin-1 and may not display properly under,
+			   e.g., DOS */
 
-		s = a[++*i];
+			s = a[++*i];
 #ifndef NO_LATIN1_CHARSET
-		switch (s)
-		{
-		case 'a':  s = (char)0xe0; break; /* ? */
-		case 'e':  s = (char)0xe8; break; /* ? */
-		case 'i':  s = (char)0xec; break; /* ? */
-		case 'o':  s = (char)0xf2; break; /* ? */
-		case 'u':  s = (char)0xf9; break; /* ? */
-		case 'A':  s = (char)0xc0; break; /* ? */
-		case 'E':  s = (char)0xc8; break; /* ? */
-		case 'I':  s = (char)0xcc; break; /* ? */
-		case 'O':  s = (char)0xd2; break; /* ? */
-		case 'U':  s = (char)0xd9; break; /* ? */
-		}
+			switch (s)
+			{
+				case 'a':  s = (char)0xe0; break;
+				case 'e':  s = (char)0xe8; break;
+				case 'i':  s = (char)0xec; break;
+				case 'o':  s = (char)0xf2; break;
+				case 'u':  s = (char)0xf9; break;
+				case 'A':  s = (char)0xc0; break;
+				case 'E':  s = (char)0xc8; break;
+				case 'I':  s = (char)0xcc; break;
+				case 'O':  s = (char)0xd2; break;
+				case 'U':  s = (char)0xd9; break;
+			}
 #endif
-		break;
-	}
-	case '\'':              /* accent acute */
-	{
-		s = a[++*i];
-#ifndef NO_LATIN1_CHARSET
-		switch (s)
-		{
-		case 'a':  s = (char)0xe1; break; /* ? */
-		case 'e':  s = (char)0xe9; break; /* ? */
-		case 'i':  s = (char)0xed; break; /* ? */
-		case 'o':  s = (char)0xf3; break; /* ? */
-		case 'u':  s = (char)0xfa; break; /* ? */
-		case 'y':  s = (char)0xfd; break;
-		case 'A':  s = (char)0xc1; break; /* ? */
-		case 'E':  s = (char)0xc9; break; /* ? */
-		case 'I':  s = (char)0xcd; break; /* ? */
-		case 'O':  s = (char)0xd3; break; /* ? */
-		case 'U':  s = (char)0xda; break; /* ? */
-		case 'Y':  s = (char)0xdd; break; /* ? */
+			break;
 		}
-#endif
-		break;
-	}
-	case '~':               /* tilde */
-	{
-		s = a[++*i];
-#ifndef NO_LATIN1_CHARSET
-		switch (s)
+		case '\'':              /* accent acute */
 		{
-		case 'a':  s = (char)0xe3; break; /* ? */
-		case 'n':  s = (char)0xf1; break; /* ? */
-		case 'o':  s = (char)0xf5; break; /* ? */
-		case 'A':  s = (char)0xc3; break; /* ? */
-		case 'N':  s = (char)0xd1; break; /* ? */
-		case 'O':  s = (char)0xd5; break; /* ? */
-		}
-#endif
-		break;
-	}
-	case '^':               /* circumflex */
-	{
-		s = a[++*i];
+			s = a[++*i];
 #ifndef NO_LATIN1_CHARSET
-		switch (s)
-		{
-		case 'a':  s = (char)0xe2; break; /* ? */
-		case 'e':  s = (char)0xea; break; /* ? */
-		case 'i':  s = (char)0xee; break; /* ? */
-		case 'o':  s = (char)0xf4; break; /* ? */
-		case 'u':  s = (char)0xfb; break; /* ? */
-		case 'A':  s = (char)0xc2; break; /* ? */
-		case 'E':  s = (char)0xca; break; /* ? */
-		case 'I':  s = (char)0xce; break; /* ? */
-		case 'O':  s = (char)0xd4; break; /* ? */
-		case 'U':  s = (char)0xdb; break; /* ? */
-		}
+			switch (s)
+			{
+				case 'a':  s = (char)0xe1; break;
+				case 'e':  s = (char)0xe9; break;
+				case 'i':  s = (char)0xed; break;
+				case 'o':  s = (char)0xf3; break;
+				case 'u':  s = (char)0xfa; break;
+				case 'y':  s = (char)0xfd; break;
+				case 'A':  s = (char)0xc1; break;
+				case 'E':  s = (char)0xc9; break;
+				case 'I':  s = (char)0xcd; break;
+				case 'O':  s = (char)0xd3; break;
+				case 'U':  s = (char)0xda; break;
+				case 'Y':  s = (char)0xdd; break;
+			}
 #endif
-		break;
-	}
-	case ':':               /* umlaut */
-	{
-		s = a[++*i];
-#ifndef NO_LATIN1_CHARSET
-		switch (s)
-		{
-		case 'a':  s = (char)0xe4; break; /* ? */
-		case 'e':  s = (char)0xeb; break; /* ? */
-		case 'i':  s = (char)0xef; break; /* ? */
-		case 'o':  s = (char)0xf6; break; /* ? */
-		case 'u':  s = (char)0xfc; break; /* ? */
-											/* case 'y':  s = (char)0xff; break; */ /* ? */
-		case 'A':  s = (char)0xc4; break; /* ? */
-		case 'E':  s = (char)0xcb; break; /* ? */
-		case 'I':  s = (char)0xcf; break; /* ? */
-		case 'O':  s = (char)0xd6; break; /* ? */
-		case 'U':  s = (char)0xdc; break; /* ? */
+			break;
 		}
+		case '~':               /* tilde */
+		{
+			s = a[++*i];
+#ifndef NO_LATIN1_CHARSET
+			switch (s)
+			{
+				case 'a':  s = (char)0xe3; break;
+				case 'n':  s = (char)0xf1; break;
+				case 'o':  s = (char)0xf5; break;
+				case 'A':  s = (char)0xc3; break;
+				case 'N':  s = (char)0xd1; break;
+				case 'O':  s = (char)0xd5; break;
+			}
 #endif
-		break;
-	}
-	case ',':               /* cedilla */
-	{
-		s = a[++*i];
+			break;
+		}
+		case '^':               /* circumflex */
+		{
+			s = a[++*i];
 #ifndef NO_LATIN1_CHARSET
-		switch (s)
+			switch (s)
+			{
+				case 'a':  s = (char)0xe2; break;
+				case 'e':  s = (char)0xea; break;
+				case 'i':  s = (char)0xee; break;
+				case 'o':  s = (char)0xf4; break;
+				case 'u':  s = (char)0xfb; break;
+				case 'A':  s = (char)0xc2; break;
+				case 'E':  s = (char)0xca; break;
+				case 'I':  s = (char)0xce; break;
+				case 'O':  s = (char)0xd4; break;
+				case 'U':  s = (char)0xdb; break;
+			}
+#endif
+			break;
+		}
+		case ':':               /* umlaut */
 		{
-		case 'C':  s = (char)0xc7; break; /* ? */
-		case 'c':  s = (char)0xe7; break; /* ? */
+			s = a[++*i];
+#ifndef NO_LATIN1_CHARSET
+			switch (s)
+			{
+				case 'a':  s = (char)0xe4; break;
+				case 'e':  s = (char)0xeb; break;
+				case 'i':  s = (char)0xef; break;
+				case 'o':  s = (char)0xf6; break;
+				case 'u':  s = (char)0xfc; break;
+				/* case 'y':  s = (char)0xff; break; */
+				case 'A':  s = (char)0xc4; break;
+				case 'E':  s = (char)0xcb; break;
+				case 'I':  s = (char)0xcf; break;
+				case 'O':  s = (char)0xd6; break;
+				case 'U':  s = (char)0xdc; break;
+			}
+#endif
+			break;
 		}
+		case ',':               /* cedilla */
+		{
+			s = a[++*i];
+#ifndef NO_LATIN1_CHARSET
+			switch (s)
+			{
+				case 'C':  s = (char)0xc7; break;
+				case 'c':  s = (char)0xe7; break;
+			}
 #endif
-		break;
-	}
-	case '<':               /* Spanish left quotation marks */
+			break;
+		}
+		case '<':               /* Spanish left quotation marks */
 #ifndef NO_LATIN1_CHARSET
-		s = (char)0xab; /* ? */
+			s = (char)0xab;
 #endif
-		break;
-	case '>':               /* Spanish right quotation marks */
+			break;
+		case '>':               /* Spanish right quotation marks */
 #ifndef NO_LATIN1_CHARSET
-		s = (char)0xbb; /* ? */
-		break;
+			s = (char)0xbb;
+			break;
 #endif
-	case '!':               /* upside-down exclamation mark */
+		case '!':               /* upside-down exclamation mark */
 #ifndef NO_LATIN1_CHARSET
-		s = (char)0xa1; /* ? */
+			s = (char)0xa1;
 #endif
-		break;
-	case '?':               /* upside-down question mark */
+			break;
+		case '?':               /* upside-down question mark */
 #ifndef NO_LATIN1_CHARSET
-		s = (char)0xbf; /* ? */
+			s = (char)0xbf;
 #endif
-		break;
-	case 'a':               /* ae ligature */
+			break;
+		case 'a':               /* ae ligature */
 #ifndef NO_LATIN1_CHARSET
-		s = (char)0xe6; ++*i; /* ? */
+			s = (char)0xe6; ++*i;
 #else
-		s = 'e'; ++*i;
+			s = 'e'; ++*i;
 #endif
-		break;
-	case 'A':               /* AE ligature */
+			break;
+		case 'A':               /* AE ligature */
 #ifndef NO_LATIN1_CHARSET
-		s = (char)0xc6; ++*i; /* ? */
+			s = (char)0xc6; ++*i;
 #else
-		s = 'E'; ++*i;
+			s = 'E'; ++*i;
 #endif
-		break;
-	case 'c':               /* cents symbol */
+			break;
+		case 'c':               /* cents symbol */
 #ifndef NO_LATIN1_CHARSET
-		s = (char)0xa2; /* ? */
+			s = (char)0xa2;
 #endif
-		break;
-	case 'L':               /* British pound */
+			break;
+		case 'L':               /* British pound */
 #ifndef NO_LATIN1_CHARSET
-		s = (char)0xa3; /* ? */
+			s = (char)0xa3;
 #endif
-		break;
-	case 'Y':               /* Japanese Yen */
+			break;
+		case 'Y':               /* Japanese Yen */
 #ifndef NO_LATIN1_CHARSET
-		s = (char)0xa5; /* ? */
+			s = (char)0xa5;
 #endif
-		break;
-	case '-':               /* em dash */
+			break;
+		case '-':               /* em dash */
 #ifndef NO_LATIN1_CHARSET
-							/* s = (char)0x97; */ /* ? */
+			/* s = (char)0x97; */
 #endif
-		break;
-	case '#':               /* 3-digit decimal code */
-	{
-		s = (char)((a[++*i] - '0') * 100);
-		s += (a[++*i] - '0') * 10;
-		s += (a[++*i] - '0');
+			break;
+		case '#':               /* 3-digit decimal code */
+		{
+			s = (char)((a[++*i]-'0')*100);
+			s += (a[++*i]-'0')*10;
+			s += (a[++*i]-'0');
 #ifdef NO_LATIN1_CHARSET
-		if ((unsigned)s>127) s = '?';
+			if ((unsigned)s>127) s = '?';
 #endif
-	}
+		}
 	}
 
 	if (skipbracket)
 	{
 		++*i;
-		if (a[*i + 1] == ')')++*i;
-		if (s == ')') s = r;
+		if (a[*i+1]==')') ++*i;
+		if (s==')') s = r;
 	}
 
 	return s;
 }
 
+#if !defined (GLK)	/* not used for Glk */
+
+HUGO_FILE TrytoOpen(char *f, char *p, char *d)
+{
+	char drive[MAXDRIVE], dir[MAXDIR], fname[MAXFILENAME], ext[MAXEXT];
+	char envvar[32];
+	HUGO_FILE tempfile; char temppath[MAXPATH];
+
+	/* Try to open the given, vanilla filename */
+	if ((strcmp(f, "")) && (tempfile = HUGO_FOPEN(f, p)))
+	{
+		return tempfile;
+	}
+
+	hugo_splitpath(f, drive, dir, fname, ext);      /* file to open */
+
+	/* If the given filename doesn't already specify where to find it */
+	if (!strcmp(drive, "") && !strcmp(dir, ""))
+	{
+		/* Check gamefile directory */
+		hugo_makepath(temppath, "", gamepath, fname, ext);
+
+		if ((tempfile = HUGO_FOPEN(temppath, p)))
+		{
+			strcpy(f, temppath);    /* the new pathname */
+			return tempfile;
+		}
+
+		/* Check environment variables */
+		strcpy(envvar, "hugo_");        /* the actual var. name */
+		strcat(envvar, d);
+
+		if (getenv(strupr(envvar)))
+		{
+			hugo_makepath(temppath, "", getenv(strupr(envvar)), fname, ext);
+
+			if ((tempfile = HUGO_FOPEN(temppath, p)))
+			{
+				strcpy(f, temppath);  /* the new pathname */
+				return tempfile;
+			}
+		}
+	}
+
+	return NULL;            /* return NULL if not openable */
+}
+
+#endif	/* GLK */
+
 int Hugo::Undo() {
 	int count = 0, n;
 	int turns, turncount, tempptr;
 	int obj, prop, attr, v;
 	unsigned int addr;
 
-	if (--undoptr < 0) undoptr = MAXUNDO - 1;
+	if (--undoptr < 0) undoptr = MAXUNDO-1;
 
-	if (undostack[undoptr][1] != 0)
+	if (undostack[undoptr][1]!=0)
 	{
 		/* Get the number of operations to be undone for
-		the last turn.
+		   the last turn.
 		*/
 		if ((turns = undostack[undoptr][1]) >= MAXUNDO)
 			goto CheckUndoFailed;
@@ -1763,121 +2200,122 @@ int Hugo::Undo() {
 		tempptr = undoptr;
 
 		/* Count the number of operations available to see if there
-		are enough to undo the last turn (as per the number
-		required in <turns>.
+		   are enough to undo the last turn (as per the number
+		   required in <turns>.
 		*/
 		do
 		{
-			if (--undoptr < 0) undoptr = MAXUNDO - 1;
+			if (--undoptr < 0) undoptr = MAXUNDO-1;
 			turncount++;
 
 			/* if end of turn */
-			if (undostack[undoptr][0] == 0)
+			if (undostack[undoptr][0]==0)
 				break;
-		} while (true);
+		}
+		while (true);
 
 		if (turncount<turns) goto CheckUndoFailed;
 
 		undoptr = tempptr;
 
-		if (--undoptr < 0) undoptr = MAXUNDO - 1;
+		if (--undoptr < 0) undoptr = MAXUNDO-1;
 
 		while (undostack[undoptr][0] != 0)
 		{
 			switch (undostack[undoptr][0])
 			{
-			case MOVE_T:
-			{
-				MoveObj(undostack[undoptr][1], undostack[undoptr][2]);
-				count++;
-				break;
-			}
-
-			case PROP_T:
-			{
-				obj = undostack[undoptr][1];
-				prop = undostack[undoptr][2];
-				n = undostack[undoptr][3];
-				v = undostack[undoptr][4];
+				case MOVE_T:
+				{
+					MoveObj(undostack[undoptr][1], undostack[undoptr][2]);
+					count++;
+					break;
+				}
 
-				if ((addr = PropAddr(obj, prop, 0)) != 0)
+				case PROP_T:
 				{
-					defseg = proptable;
+					obj = undostack[undoptr][1];
+					prop = undostack[undoptr][2];
+					n = undostack[undoptr][3];
+					v = undostack[undoptr][4];
 
-					if (n == PROP_ROUTINE)
+					if ((addr = PropAddr(obj, prop, 0))!=0)
 					{
-						Poke(addr + 1, PROP_ROUTINE);
-						n = 1;
-					}
+						defseg = proptable;
 
-					/* Use this new prop count number if the
-					existing one is too low or a prop routine
-					*/
-					else if (Peek(addr + 1) == PROP_ROUTINE || Peek(addr + 1)<(unsigned char)n)
-						Poke(addr + 1, (unsigned char)n);
+						if (n==PROP_ROUTINE)
+						{
+							Poke(addr+1, PROP_ROUTINE);
+							n = 1;
+						}
+
+						/* Use this new prop count number if the
+						   existing one is too low or a prop routine
+						*/
+						else if (Peek(addr+1)==PROP_ROUTINE || Peek(addr+1)<(unsigned char)n)
+							Poke(addr+1, (unsigned char)n);
 
-					/* property length */
-					if (n <= (int)Peek(addr + 1))
-						PokeWord(addr + 2 + (n - 1) * 2, v);
+						/* property length */
+						if (n<=(int)Peek(addr+1))
+							PokeWord(addr+2+(n-1)*2, v);
+					}
+					count++;
+					break;
 				}
-				count++;
-				break;
-			}
 
-			case ATTR_T:
-			{
-				obj = undostack[undoptr][1];
-				attr = undostack[undoptr][2];
-				n = undostack[undoptr][3];
-				SetAttribute(obj, attr, n);
-				count++;
-				break;
-			}
+				case ATTR_T:
+				{
+					obj = undostack[undoptr][1];
+					attr = undostack[undoptr][2];
+					n = undostack[undoptr][3];
+					SetAttribute(obj, attr, n);
+					count++;
+					break;
+				}
 
-			case VAR_T:
-			{
-				n = undostack[undoptr][1];
-				v = undostack[undoptr][2];
-				var[n] = v;
-				count++;
-				break;
-			}
+				case VAR_T:
+				{
+					n = undostack[undoptr][1];
+					v = undostack[undoptr][2];
+					var[n] = v;
+					count++;
+					break;
+				}
 
-			case ARRAYDATA_T:
-			{
-				defseg = arraytable;
-				addr = undostack[undoptr][1];
-				n = undostack[undoptr][2];
-				v = undostack[undoptr][3];
+				case ARRAYDATA_T:
+				{
+					defseg = arraytable;
+					addr = undostack[undoptr][1];
+					n = undostack[undoptr][2];
+					v = undostack[undoptr][3];
 
 				/* The array length was already accounted for before calling
-				SaveUndo(), so there is no adjustment of
-				+2 here.
+				   SaveUndo(), so there is no adjustment of
+				   +2 here.
 				*/
-				PokeWord(addr + n * 2, v);
-				count++;
-				break;
-			}
+					PokeWord(addr+n*2, v);
+					count++;
+					break;
+				}
 
-			case DICT_T:
-			{
-				defseg = dicttable;
-				PokeWord(0, --dictcount);
-				count++;
-				break;
-			}
-			case WORD_T:
-			{
-				n = undostack[undoptr][1];
-				v = undostack[undoptr][2];
-				wd[n] = v;
-				word[n] = GetWord(wd[n]);
-				count++;
-			}
+				case DICT_T:
+				{
+					defseg = dicttable;
+					PokeWord(0, --dictcount);
+					count++;
+					break;
+				}
+				case WORD_T:
+				{
+					n = undostack[undoptr][1];
+					v = undostack[undoptr][2];
+					wd[n] = v;
+					word[n] = GetWord(wd[n]);
+					count++;
+				}
 			}
 			defseg = gameseg;
 
-			if (--undoptr < 0) undoptr = MAXUNDO - 1;
+			if (--undoptr < 0) undoptr = MAXUNDO-1;
 		}
 	}
 
@@ -1895,5 +2333,50 @@ CheckUndoFailed:
 	return 1;
 }
 
+/*
+ * Random-number generator replacement by Andrew Plotkin:
+ *
+ */
+
+#if defined (BUILD_RANDOM)
+
+static unsigned int rand_table[55];	/* state for the RNG */
+static int rand_index1, rand_index2;
+
+int random()
+{
+    rand_index1 = (rand_index1 + 1) % 55;
+    rand_index2 = (rand_index2 + 1) % 55;
+    rand_table[rand_index1] = rand_table[rand_index1] - rand_table[rand_index2];
+    return rand_table[rand_index1];
+}
+
+void srandom(int seed)
+{
+	int k = 1;
+	int i, loop;
+
+	rand_table[54] = seed;
+	rand_index1 = 0;
+	rand_index2 = 31;
+	
+	for (i = 0; i < 55; i++)
+	{
+		int ii = (21 * i) % 55;
+
+		rand_table[ii] = k;
+		k = seed - k;
+		seed = rand_table[ii];
+	}
+	
+	for (loop = 0; loop < 4; loop++)
+	{
+		for (i = 0; i < 55; i++)
+			rand_table[i] = rand_table[i] - rand_table[ (1 + i + 30) % 55];
+	}
+}
+
+#endif /* BUILD_RANDOM */
+
 } // End of namespace Hugo
 } // End of namespace Glk
diff --git a/engines/glk/hugo/hugo.cpp b/engines/glk/hugo/hugo.cpp
index a5fbdb9..0fca432 100644
--- a/engines/glk/hugo/hugo.cpp
+++ b/engines/glk/hugo/hugo.cpp
@@ -65,9 +65,10 @@ Hugo::Hugo(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gam
 		arrexpr(0), multiprop(0), set_value(0)
 #if defined (DEBUGGER)
 		, debug_eval(false), debug_eval_error(false), debugger_step_over(false),
-		debugger_finish(false), debugger_run(false), currentroutine(false),
-		complex_prop_breakpoint(false), trace_complex_prop_routine(false), properties(0),
-		current_locals(0)
+		debugger_finish(false), debugger_run(false), debugger_interrupt(false),
+		debugger_skip(false), runtime_error(false), currentroutine(false),
+		complex_prop_breakpoint(false), trace_complex_prop_routine(false), routines(0),
+		properties(0), current_locals(0), this_codeptr(0)
 #endif
 		{
 	// heexpr
diff --git a/engines/glk/hugo/hugo.h b/engines/glk/hugo/hugo.h
index de64744..1f5aaff 100644
--- a/engines/glk/hugo/hugo.h
+++ b/engines/glk/hugo/hugo.h
@@ -220,6 +220,9 @@ private:
 	bool debugger_step_over;
 	bool debugger_finish;
 	bool debugger_run;
+	bool debugger_interrupt;
+	bool debugger_skip;
+	bool runtime_error;
 	int currentroutine;
 	bool complex_prop_breakpoint;
 	bool trace_complex_prop_routine;
@@ -227,11 +230,13 @@ private:
 	char *propertyname[MAX_PROPERTY];
 //	CODE code[999];
 	CALL call[999];
+	int routines;
 	int properties;
 	WINDOW window[99];
 	int codeline[9][100];
 	char localname[9][100];
 	int current_locals;
+	long this_codeptr;
 #endif
 private:
 	/**
@@ -469,7 +474,9 @@ private:
 	 * Print to client display taking into account cursor relocation,
 	 * font changes, color setting, and window scrolling.
 	 */
-	void Printout(char *a);
+	void Printout(char *a, int no_scrollback_linebreak);
+
+	void PromptMore();
 
 	int RecordCommands();
 
@@ -558,6 +565,12 @@ private:
 	const char *RoutineName(long loc) { return "Routine"; }
 
 	void AddStringtoCodeWindow(const char *str) {}
+
+	void SwitchtoDebugger() {}
+
+	void DebuggerFatal(DEBUGGER_ERROR err) { error("Debugger error"); }
+
+	void RecoverLastGood() {}
 #endif
 
 	int Child(int obj);
@@ -612,6 +625,42 @@ private:
 	int Youngest(int obj);
 
 	/**@}*/
+
+	/**
+	* \defgroup Miscellaneous
+	* @{
+	*/
+
+	int hugo_fseek(Common::SeekableReadStream *s, long int offset, int whence) {
+		return s->seek(offset, whence);
+	}
+
+	int hugo_fgetc(Common::SeekableReadStream *s) {
+		return s->readByte();
+	}
+
+	bool hugo_ferror(Common::SeekableReadStream *s) const {
+		return s->err();
+	}
+
+	long hugo_ftell(Common::SeekableReadStream *s) {
+		return s->pos();
+	}
+
+	int hugo_fclose(strid_t f) {
+		delete f;
+		return 0;
+	}
+
+	void hugo_exit(const char *msg) {
+		error("%s", line);
+	}
+
+	size_t hugo_fread(void *ptr, size_t size, size_t count, Common::SeekableReadStream *s) {
+		return s->read(ptr, size * count);
+	}
+
+	/**@}*/
 private:
 	/**
 	 * Allocate memory block
@@ -651,10 +700,10 @@ public:
 	void hugo_closefiles() {}
 	void RunRoutine(long v) {}
 	unsigned int FindWord(const char *a) { return 0; }
-	void PromptMore() {}
 	void hugo_stopsample() {}
 	void hugo_stopmusic() {}
 	int hugo_hasgraphics() { return 0; }
+	int hugo_writetoscript(const char *s) { return 0; }
 };
 
 } // End of namespace Hugo
diff --git a/engines/glk/hugo/hugo_defines.h b/engines/glk/hugo/hugo_defines.h
index ce2d006..7809ea1 100644
--- a/engines/glk/hugo/hugo_defines.h
+++ b/engines/glk/hugo/hugo_defines.h
@@ -153,6 +153,8 @@ browsing.
 #define CODE_WINDOW 2
 #endif
 
+#define PRINTFATALERROR(a)	error(a)
+
 } // End of namespace Hugo
 } // End of namespace Glk
 
diff --git a/engines/glk/hugo/hugo_types.h b/engines/glk/hugo/hugo_types.h
index 3d7af86..dacde03 100644
--- a/engines/glk/hugo/hugo_types.h
+++ b/engines/glk/hugo/hugo_types.h
@@ -120,6 +120,10 @@ struct CODE_BLOCK {
 };
 
 #if defined (DEBUGGER)
+enum DEBUGGER_ERROR {
+	D_MEMORY_ERROR
+};
+
 struct CALL {
 	long addr;
 	bool param;





More information about the Scummvm-git-logs mailing list